All files / create-relational-structure create-relational-structure.ts

97.43% Statements 38/39
91.66% Branches 11/12
100% Functions 6/6
97.36% Lines 37/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100                      50x 50x 50x 50x 50x   50x         50x 18x             122x   50x 122x 214x 214x 32x 18x   32x 32x 58x     182x 182x   10x       182x               50x 122x 122x 208x 32x   176x   122x     50x   50x 41x 41x             50x           50x   18x               50x    
import type { NormalizedData, NormalizedValue } from "../normalize";
import { createForeignKey } from "./create-foreign-key";
import { getIsNullable } from "./get-is-nullable";
import { getType, UnknownTypeError } from "./get-type";
import type { Field, RelationalTable } from "./types";
 
export function createRelationalStructure(
  prefix: string,
  data: NormalizedData,
  parentTable?: RelationalTable,
): RelationalTable[] {
  const fields: Map<string, Field> = new Map();
  const fieldTypes: Map<string, Field["type"]> = new Map();
  const fieldIsNullable: Map<string, Field["isNullable"]> = new Map();
  const nestedData: Map<string, NormalizedData> = new Map();
  const selfData: Record<string, NormalizedValue>[] = [];
 
  fields.set("normalize_id", {
    key: "normalize_id",
    type: "integer",
    isPrimaryKey: true,
  });
  if (parentTable) {
    fields.set("normalize_parent_id", {
      key: "normalize_parent_id",
      type: "integer",
      reference: createForeignKey(parentTable, "normalize_id"),
    });
  }
 
  const keys = new Set(data.map((row) => Object.keys(row)).flat());
 
  data.forEach((row, normalize_parent_id) => {
    for (const key of keys) {
      const value = row[key];
      if (Array.isArray(value)) {
        if (!nestedData.has(key)) {
          nestedData.set(key, []);
        }
        const nestedDataByKey = nestedData.get(key)!;
        value.forEach((item) =>
          nestedDataByKey.push({ ...item, normalize_parent_id }),
        );
      } else {
        try {
          fieldTypes.set(key, getType(fieldTypes.get(key), value));
        } catch (error) {
          Iif (!(error instanceof UnknownTypeError)) {
            throw error;
          }
        }
        fieldIsNullable.set(
          key,
          getIsNullable(fieldIsNullable.get(key), value),
        );
      }
    }
  });
 
  data.forEach((row, normalize_id) => {
    const selfDataItem: Record<string, NormalizedValue> = { normalize_id };
    for (const [key, value] of Object.entries(row)) {
      if (Array.isArray(value)) {
        continue;
      }
      selfDataItem[key] = value;
    }
    selfData.push(selfDataItem);
  });
 
  fieldTypes.delete("normalize_parent_id");
 
  for (const [key, type] of fieldTypes.entries()) {
    const isNullable = fieldIsNullable.get(key);
    fields.set(key, {
      key,
      type,
      ...(isNullable ? { isNullable } : {}),
    });
  }
 
  const relationalTable: RelationalTable = {
    name: prefix,
    fields: [...fields.values()],
    data: selfData,
  };
 
  const nestedRelationalTables = [...nestedData.entries()]
    .map((entry) =>
      createRelationalStructure(
        `${prefix}_${entry[0]}`,
        entry[1],
        relationalTable,
      ),
    )
    .flat();
 
  return [relationalTable, ...nestedRelationalTables];
}