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                      48x 48x 48x 48x 48x   48x         48x 17x             117x   48x 117x 204x 204x 30x 17x   30x 30x 55x     174x 174x   10x       174x               48x 117x 117x 198x 30x   168x   117x     48x   48x 39x 39x             48x           48x   17x               48x    
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];
}