@jackchuka/gql-ingest 1.5.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +138 -6
  2. package/bin/cli.js +53 -52
  3. package/dist/mapper.d.ts +9 -4
  4. package/dist/mapper.d.ts.map +1 -1
  5. package/dist/readers/csv.d.ts +10 -0
  6. package/dist/readers/csv.d.ts.map +1 -0
  7. package/dist/readers/csv.test.d.ts +2 -0
  8. package/dist/readers/csv.test.d.ts.map +1 -0
  9. package/dist/readers/data-reader.d.ts +21 -0
  10. package/dist/readers/data-reader.d.ts.map +1 -0
  11. package/dist/readers/data-reader.test.d.ts +2 -0
  12. package/dist/readers/data-reader.test.d.ts.map +1 -0
  13. package/dist/readers/index.d.ts +6 -0
  14. package/dist/readers/index.d.ts.map +1 -0
  15. package/dist/readers/json.d.ts +6 -0
  16. package/dist/readers/json.d.ts.map +1 -0
  17. package/dist/readers/json.test.d.ts +2 -0
  18. package/dist/readers/json.test.d.ts.map +1 -0
  19. package/dist/readers/jsonl.d.ts +6 -0
  20. package/dist/readers/jsonl.d.ts.map +1 -0
  21. package/dist/readers/jsonl.test.d.ts +2 -0
  22. package/dist/readers/jsonl.test.d.ts.map +1 -0
  23. package/dist/readers/yaml.d.ts +6 -0
  24. package/dist/readers/yaml.d.ts.map +1 -0
  25. package/dist/readers/yaml.test.d.ts +2 -0
  26. package/dist/readers/yaml.test.d.ts.map +1 -0
  27. package/package.json +1 -1
  28. package/src/cli.ts +10 -25
  29. package/src/graphql-client.test.ts +19 -4
  30. package/src/mapper.test.ts +115 -64
  31. package/src/mapper.ts +138 -33
  32. package/src/{csv-reader.test.ts → readers/csv.test.ts} +1 -1
  33. package/src/readers/csv.ts +29 -0
  34. package/src/readers/data-reader.test.ts +104 -0
  35. package/src/readers/data-reader.ts +61 -0
  36. package/src/readers/index.ts +18 -0
  37. package/src/readers/json.test.ts +80 -0
  38. package/src/readers/json.ts +27 -0
  39. package/src/readers/jsonl.test.ts +96 -0
  40. package/src/readers/jsonl.ts +28 -0
  41. package/src/readers/yaml.test.ts +95 -0
  42. package/src/readers/yaml.ts +28 -0
  43. package/dist/csv-reader.d.ts +0 -5
  44. package/dist/csv-reader.d.ts.map +0 -1
  45. package/dist/csv-reader.test.d.ts +0 -2
  46. package/dist/csv-reader.test.d.ts.map +0 -1
  47. package/src/csv-reader.ts +0 -18
package/dist/mapper.d.ts CHANGED
@@ -2,22 +2,27 @@ import { GraphQLClientWrapper } from "./graphql-client";
2
2
  import { MetricsCollector } from "./metrics";
3
3
  import { ParallelProcessingConfig, RetryConfig } from "./config";
4
4
  export interface MappingConfig {
5
- csvFile: string;
5
+ csvFile?: string;
6
+ dataFile?: string;
7
+ dataFormat?: string;
6
8
  graphqlFile: string;
7
- mapping: Record<string, string>;
9
+ mapping: Record<string, string | any>;
8
10
  }
9
11
  export declare class DataMapper {
10
12
  private client;
11
13
  private basePath;
12
14
  private metrics;
13
15
  private verbose;
14
- constructor(client: GraphQLClientWrapper, basePath?: string, metrics?: MetricsCollector, verbose?: boolean);
16
+ private formatOverride?;
17
+ constructor(client: GraphQLClientWrapper, basePath?: string, metrics?: MetricsCollector, verbose?: boolean, formatOverride?: string);
15
18
  discoverMappings(configDir: string, entityFilter?: string[]): string[];
16
19
  processEntity(configPath: string, parallelConfig?: ParallelProcessingConfig, retryConfig?: RetryConfig): Promise<void>;
17
20
  private processRowsSequentially;
18
21
  private processRowsConcurrently;
19
22
  private chunkArray;
20
- private mapCsvRowToVariables;
23
+ private mapRowToVariables;
24
+ private getValueByPath;
25
+ private mapNestedObject;
21
26
  private extractVariableTypes;
22
27
  private extractTypeName;
23
28
  private convertValue;
@@ -1 +1 @@
1
- {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../src/mapper.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,OAAO,CAAU;gBAGvB,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,GAAE,MAAsB,EAChC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,OAAO,GAAE,OAAe;IAQ1B,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE;IA4ChE,aAAa,CACjB,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,wBAAwB,EACzC,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,IAAI,CAAC;YA8CF,uBAAuB;YAwCvB,uBAAuB;IAkFrC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,oBAAoB;IA4B5B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,YAAY;IA8DpB,UAAU,IAAI,gBAAgB;CAG/B"}
1
+ {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../src/mapper.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEjE,MAAM,WAAW,aAAa;IAE5B,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,CAAC;CACvC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,cAAc,CAAC,CAAS;gBAG9B,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,GAAE,MAAsB,EAChC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,OAAO,GAAE,OAAe,EACxB,cAAc,CAAC,EAAE,MAAM;IASzB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE;IA4ChE,aAAa,CACjB,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,wBAAwB,EACzC,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,IAAI,CAAC;YAyDF,uBAAuB;YAwCvB,uBAAuB;IA8ErC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,iBAAiB;IA+CzB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,eAAe;IA0CvB,OAAO,CAAC,oBAAoB;IA4B5B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,YAAY;IAmEpB,UAAU,IAAI,gBAAgB;CAG/B"}
@@ -0,0 +1,10 @@
1
+ import { DataReader, DataRow } from "./data-reader";
2
+ export interface CsvRow {
3
+ [key: string]: string;
4
+ }
5
+ export declare function readCsvFile(filePath: string): Promise<CsvRow[]>;
6
+ export declare class CsvReader extends DataReader {
7
+ getSupportedExtensions(): string[];
8
+ readFile(filePath: string): Promise<DataRow[]>;
9
+ }
10
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/readers/csv.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,WAAW,MAAM;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAUrE;AAED,qBAAa,SAAU,SAAQ,UAAU;IACvC,sBAAsB,IAAI,MAAM,EAAE;IAI5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAGrD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=csv.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.test.d.ts","sourceRoot":"","sources":["../../src/readers/csv.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ export interface DataRow {
2
+ [key: string]: any;
3
+ }
4
+ export declare abstract class DataReader {
5
+ abstract readFile(filePath: string): Promise<DataRow[]>;
6
+ /**
7
+ * Get the supported file extensions for this reader
8
+ */
9
+ abstract getSupportedExtensions(): string[];
10
+ /**
11
+ * Check if this reader can handle the given file
12
+ */
13
+ canHandle(filePath: string): boolean;
14
+ }
15
+ export declare class DataReaderFactory {
16
+ private static readers;
17
+ static registerReader(reader: DataReader): void;
18
+ static getReader(filePath: string, format?: string): DataReader;
19
+ static getSupportedFormats(): string[];
20
+ }
21
+ //# sourceMappingURL=data-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-reader.d.ts","sourceRoot":"","sources":["../../src/readers/data-reader.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,8BAAsB,UAAU;IAC9B,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAEvD;;OAEG;IACH,QAAQ,CAAC,sBAAsB,IAAI,MAAM,EAAE;IAE3C;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAMrC;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAoB;IAE1C,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI/C,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU;IAuB/D,MAAM,CAAC,mBAAmB,IAAI,MAAM,EAAE;CAOvC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data-reader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-reader.test.d.ts","sourceRoot":"","sources":["../../src/readers/data-reader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export { DataReader, DataRow, DataReaderFactory } from "./data-reader";
2
+ export { CsvReader, readCsvFile, CsvRow } from "./csv";
3
+ export { JsonReader } from "./json";
4
+ export { YamlReader } from "./yaml";
5
+ export { JsonlReader } from "./jsonl";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/readers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { DataReader, DataRow } from "./data-reader";
2
+ export declare class JsonReader extends DataReader {
3
+ getSupportedExtensions(): string[];
4
+ readFile(filePath: string): Promise<DataRow[]>;
5
+ }
6
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/readers/json.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEpD,qBAAa,UAAW,SAAQ,UAAU;IACxC,sBAAsB,IAAI,MAAM,EAAE;IAI5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAkBrD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=json.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.test.d.ts","sourceRoot":"","sources":["../../src/readers/json.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { DataReader, DataRow } from "./data-reader";
2
+ export declare class JsonlReader extends DataReader {
3
+ getSupportedExtensions(): string[];
4
+ readFile(filePath: string): Promise<DataRow[]>;
5
+ }
6
+ //# sourceMappingURL=jsonl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../../src/readers/jsonl.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEpD,qBAAa,WAAY,SAAQ,UAAU;IACzC,sBAAsB,IAAI,MAAM,EAAE;IAI5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAmBrD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=jsonl.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.test.d.ts","sourceRoot":"","sources":["../../src/readers/jsonl.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { DataReader, DataRow } from "./data-reader";
2
+ export declare class YamlReader extends DataReader {
3
+ getSupportedExtensions(): string[];
4
+ readFile(filePath: string): Promise<DataRow[]>;
5
+ }
6
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/readers/yaml.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEpD,qBAAa,UAAW,SAAQ,UAAU;IACxC,sBAAsB,IAAI,MAAM,EAAE;IAI5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAkBrD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=yaml.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.test.d.ts","sourceRoot":"","sources":["../../src/readers/yaml.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jackchuka/gql-ingest",
3
- "version": "1.5.0",
3
+ "version": "2.0.1",
4
4
  "description": "A CLI tool for ingesting data from CSV files into a GraphQL API",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
package/src/cli.ts CHANGED
@@ -40,6 +40,10 @@ program
40
40
  "JSON string of headers to include in requests"
41
41
  )
42
42
  .option("-v, --verbose", "Show detailed request results and responses")
43
+ .option(
44
+ "-f, --format <format>",
45
+ "Override data format detection (csv, json, yaml, jsonl)"
46
+ )
43
47
  .action(async (options) => {
44
48
  try {
45
49
  console.log("Starting seed data generation...");
@@ -66,7 +70,8 @@ program
66
70
  client,
67
71
  process.cwd(),
68
72
  metrics,
69
- options.verbose
73
+ options.verbose,
74
+ options.format
70
75
  );
71
76
 
72
77
  // Parse entities filter if provided
@@ -117,7 +122,9 @@ program
117
122
  // When using --entities flag, show warnings instead of errors
118
123
  console.warn("\n⚠️ Warning: Dependency validation issues:");
119
124
  validationErrors.forEach((error) => console.warn(` - ${error}`));
120
- console.warn("This may cause errors if the dependent data doesn't already exist.\n");
125
+ console.warn(
126
+ "This may cause errors if the dependent data doesn't already exist.\n"
127
+ );
121
128
  } else {
122
129
  // Strict validation when processing all entities
123
130
  console.error("Dependency validation errors:");
@@ -126,12 +133,7 @@ program
126
133
  }
127
134
  }
128
135
 
129
- // Process entities in dependency-aware waves
130
- if (config.parallelProcessing.entityConcurrency === 1) {
131
- await processEntitiesSequentially(mappingPaths, mapper, config);
132
- } else {
133
- await processEntitiesInWaves(mappingPaths, resolver, mapper, config);
134
- }
136
+ await processEntitiesInWaves(mappingPaths, resolver, mapper, config);
135
137
 
136
138
  metrics.finishProcessing();
137
139
  console.log(metrics.generateSummary());
@@ -141,23 +143,6 @@ program
141
143
  }
142
144
  });
143
145
 
144
- async function processEntitiesSequentially(
145
- mappingPaths: string[],
146
- mapper: DataMapper,
147
- config: ReturnType<typeof loadConfig>
148
- ): Promise<void> {
149
- for (const configPath of mappingPaths) {
150
- try {
151
- const entityName = basename(configPath, ".json");
152
- const entityConfig = getEntityConfig(entityName, config);
153
- const retryConfig = getRetryConfig(entityName, config);
154
- await mapper.processEntity(configPath, entityConfig, retryConfig);
155
- } catch (error) {
156
- console.warn(`Warning: Could not process ${configPath}:`, error);
157
- }
158
- }
159
- }
160
-
161
146
  async function processEntitiesInWaves(
162
147
  mappingPaths: string[],
163
148
  resolver: DependencyResolver,
@@ -63,7 +63,10 @@ describe("GraphQLClientWrapper", () => {
63
63
  clientWrapper.executeMutation(mutation, variables)
64
64
  ).rejects.toThrow("GraphQL error");
65
65
 
66
- expect(consoleSpy).toHaveBeenCalledWith("GraphQL mutation failed after 3 attempts:", error);
66
+ expect(consoleSpy).toHaveBeenCalledWith(
67
+ "GraphQL mutation failed after 3 attempts:",
68
+ error
69
+ );
67
70
 
68
71
  consoleSpy.mockRestore();
69
72
  });
@@ -96,7 +99,11 @@ describe("GraphQLClientWrapper", () => {
96
99
  .mockRejectedValueOnce(serverError)
97
100
  .mockResolvedValueOnce({ data: { result: "success" } });
98
101
 
99
- const result = await clientWrapper.executeMutation(mutation, variables, retryConfig);
102
+ const result = await clientWrapper.executeMutation(
103
+ mutation,
104
+ variables,
105
+ retryConfig
106
+ );
100
107
 
101
108
  expect(mockRequest).toHaveBeenCalledTimes(3);
102
109
  expect(result).toEqual({ data: { result: "success" } });
@@ -143,7 +150,11 @@ describe("GraphQLClientWrapper", () => {
143
150
  .mockRejectedValueOnce(networkError)
144
151
  .mockResolvedValueOnce({ data: { result: "success" } });
145
152
 
146
- const result = await clientWrapper.executeMutation(mutation, variables, retryConfig);
153
+ const result = await clientWrapper.executeMutation(
154
+ mutation,
155
+ variables,
156
+ retryConfig
157
+ );
147
158
 
148
159
  expect(mockRequest).toHaveBeenCalledTimes(2);
149
160
  expect(result).toEqual({ data: { result: "success" } });
@@ -192,7 +203,11 @@ describe("GraphQLClientWrapper", () => {
192
203
  .mockResolvedValueOnce({ data: { result: "success" } });
193
204
 
194
205
  const startTime = Date.now();
195
- const result = await clientWrapper.executeMutation(mutation, variables, retryConfig);
206
+ const result = await clientWrapper.executeMutation(
207
+ mutation,
208
+ variables,
209
+ retryConfig
210
+ );
196
211
  const totalTime = Date.now() - startTime;
197
212
 
198
213
  expect(mockRequest).toHaveBeenCalledTimes(3);
@@ -3,9 +3,18 @@ import path from "path";
3
3
  import { DataMapper } from "./mapper";
4
4
  import { GraphQLClientWrapper } from "./graphql-client";
5
5
  import { MetricsCollector } from "./metrics";
6
+ import { readCsvFile, DataReaderFactory } from "./readers";
6
7
 
7
8
  jest.mock("fs");
8
- jest.mock("./csv-reader");
9
+ jest.mock("./readers", () => ({
10
+ ...jest.requireActual("./readers"),
11
+ readCsvFile: jest.fn(),
12
+ DataReaderFactory: {
13
+ getReader: jest.fn().mockReturnValue({
14
+ readFile: jest.fn(),
15
+ }),
16
+ },
17
+ }));
9
18
 
10
19
  const mockFs = fs as jest.Mocked<typeof fs>;
11
20
 
@@ -114,8 +123,8 @@ describe("DataMapper", () => {
114
123
  .mockReturnValueOnce(JSON.stringify(mockConfig))
115
124
  .mockReturnValueOnce(mockMutation);
116
125
 
117
- const { readCsvFile } = require("./csv-reader");
118
- readCsvFile.mockResolvedValue(mockCsvData);
126
+ const { DataReaderFactory } = require("./readers");
127
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
119
128
 
120
129
  mockClient.executeMutation.mockResolvedValue({
121
130
  createUser: { id: "123" },
@@ -129,8 +138,9 @@ describe("DataMapper", () => {
129
138
  path.resolve(testBasePath, "configs/test/mappings/users.json"),
130
139
  "utf8"
131
140
  );
132
- expect(readCsvFile).toHaveBeenCalledWith(
133
- path.resolve(testBasePath, "configs/test", "data/users.csv")
141
+ expect(DataReaderFactory.getReader).toHaveBeenCalledWith(
142
+ path.resolve(testBasePath, "configs/test", "data/users.csv"),
143
+ undefined
134
144
  );
135
145
  expect(mockFs.readFileSync).toHaveBeenCalledWith(
136
146
  path.resolve(testBasePath, "configs/test", "graphql/users.graphql"),
@@ -138,14 +148,22 @@ describe("DataMapper", () => {
138
148
  );
139
149
 
140
150
  expect(mockClient.executeMutation).toHaveBeenCalledTimes(2);
141
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
142
- name: "John",
143
- email: "john@example.com",
144
- }, undefined);
145
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
146
- name: "Jane",
147
- email: "jane@example.com",
148
- }, undefined);
151
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
152
+ mockMutation,
153
+ {
154
+ name: "John",
155
+ email: "john@example.com",
156
+ },
157
+ undefined
158
+ );
159
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
160
+ mockMutation,
161
+ {
162
+ name: "Jane",
163
+ email: "jane@example.com",
164
+ },
165
+ undefined
166
+ );
149
167
 
150
168
  consoleSpy.mockRestore();
151
169
  });
@@ -165,8 +183,8 @@ describe("DataMapper", () => {
165
183
  .mockReturnValueOnce(JSON.stringify(mockConfig))
166
184
  .mockReturnValueOnce(mockMutation);
167
185
 
168
- const { readCsvFile } = require("./csv-reader");
169
- readCsvFile.mockResolvedValue(mockCsvData);
186
+ const { DataReaderFactory } = require("./readers");
187
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
170
188
 
171
189
  mockClient.executeMutation.mockRejectedValue(new Error("GraphQL error"));
172
190
 
@@ -210,8 +228,8 @@ describe("DataMapper", () => {
210
228
  .mockReturnValueOnce(JSON.stringify(mockConfig))
211
229
  .mockReturnValueOnce(mockMutation);
212
230
 
213
- const { readCsvFile } = require("./csv-reader");
214
- readCsvFile.mockResolvedValue(mockCsvData);
231
+ const { DataReaderFactory } = require("./readers");
232
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
215
233
 
216
234
  mockClient.executeMutation.mockResolvedValue({
217
235
  createProduct: { id: "456" },
@@ -219,11 +237,15 @@ describe("DataMapper", () => {
219
237
 
220
238
  await dataMapper.processEntity("configs/test/mappings/products.json");
221
239
 
222
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
223
- name: "Widget",
224
- price: "19.99",
225
- sku: "W001",
226
- }, undefined);
240
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
241
+ mockMutation,
242
+ {
243
+ name: "Widget",
244
+ price: "19.99",
245
+ sku: "W001",
246
+ },
247
+ undefined
248
+ );
227
249
  });
228
250
 
229
251
  it("should handle missing CSV columns gracefully", async () => {
@@ -248,8 +270,8 @@ describe("DataMapper", () => {
248
270
  .mockReturnValueOnce(JSON.stringify(mockConfig))
249
271
  .mockReturnValueOnce(mockMutation);
250
272
 
251
- const { readCsvFile } = require("./csv-reader");
252
- readCsvFile.mockResolvedValue(mockCsvData);
273
+ const { DataReaderFactory } = require("./readers");
274
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
253
275
 
254
276
  mockClient.executeMutation.mockResolvedValue({
255
277
  createUser: { id: "789" },
@@ -257,10 +279,14 @@ describe("DataMapper", () => {
257
279
 
258
280
  await dataMapper.processEntity("configs/test/mappings/users.json");
259
281
 
260
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
261
- name: "John",
262
- email: "john@example.com",
263
- }, undefined);
282
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
283
+ mockMutation,
284
+ {
285
+ name: "John",
286
+ email: "john@example.com",
287
+ },
288
+ undefined
289
+ );
264
290
  });
265
291
 
266
292
  it("should call metrics methods during successful processing", async () => {
@@ -278,8 +304,8 @@ describe("DataMapper", () => {
278
304
  .mockReturnValueOnce(JSON.stringify(mockConfig))
279
305
  .mockReturnValueOnce(mockMutation);
280
306
 
281
- const { readCsvFile } = require("./csv-reader");
282
- readCsvFile.mockResolvedValue(mockCsvData);
307
+ const { DataReaderFactory } = require("./readers");
308
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
283
309
 
284
310
  mockClient.executeMutation.mockResolvedValue({
285
311
  createUser: { id: "123" },
@@ -309,8 +335,8 @@ describe("DataMapper", () => {
309
335
  .mockReturnValueOnce(JSON.stringify(mockConfig))
310
336
  .mockReturnValueOnce(mockMutation);
311
337
 
312
- const { readCsvFile } = require("./csv-reader");
313
- readCsvFile.mockResolvedValue(mockCsvData);
338
+ const { DataReaderFactory } = require("./readers");
339
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
314
340
 
315
341
  mockClient.executeMutation.mockRejectedValue(new Error("GraphQL error"));
316
342
 
@@ -365,8 +391,8 @@ describe("DataMapper", () => {
365
391
  .mockReturnValueOnce(JSON.stringify(mockConfig))
366
392
  .mockReturnValueOnce(mockMutation);
367
393
 
368
- const { readCsvFile } = require("./csv-reader");
369
- readCsvFile.mockResolvedValue(mockCsvData);
394
+ const { DataReaderFactory } = require("./readers");
395
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
370
396
 
371
397
  mockClient.executeMutation.mockResolvedValue({
372
398
  createProduct: { id: "123" },
@@ -374,12 +400,16 @@ describe("DataMapper", () => {
374
400
 
375
401
  await dataMapper.processEntity("configs/test/mappings/products.json");
376
402
 
377
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
378
- name: "Widget",
379
- price: 19.99,
380
- quantity: 10,
381
- active: true,
382
- }, undefined);
403
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
404
+ mockMutation,
405
+ {
406
+ name: "Widget",
407
+ price: 19.99,
408
+ quantity: 10,
409
+ active: true,
410
+ },
411
+ undefined
412
+ );
383
413
  });
384
414
 
385
415
  it("should handle invalid numeric conversions gracefully", async () => {
@@ -413,8 +443,8 @@ describe("DataMapper", () => {
413
443
  .mockReturnValueOnce(JSON.stringify(mockConfig))
414
444
  .mockReturnValueOnce(mockMutation);
415
445
 
416
- const { readCsvFile } = require("./csv-reader");
417
- readCsvFile.mockResolvedValue(mockCsvData);
446
+ const { DataReaderFactory } = require("./readers");
447
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
418
448
 
419
449
  mockClient.executeMutation.mockResolvedValue({
420
450
  createProduct: { id: "123" },
@@ -424,11 +454,15 @@ describe("DataMapper", () => {
424
454
 
425
455
  await dataMapper.processEntity("configs/test/mappings/products.json");
426
456
 
427
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
428
- name: "Widget",
429
- price: "invalid_price",
430
- quantity: "invalid_quantity",
431
- }, undefined);
457
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
458
+ mockMutation,
459
+ {
460
+ name: "Widget",
461
+ price: "invalid_price",
462
+ quantity: "invalid_quantity",
463
+ },
464
+ undefined
465
+ );
432
466
 
433
467
  expect(consoleSpy).toHaveBeenCalledWith(
434
468
  'Warning: Cannot convert "invalid_price" to Float for variable $price. Expected a valid number. Using original value.'
@@ -473,8 +507,8 @@ describe("DataMapper", () => {
473
507
  .mockReturnValueOnce(JSON.stringify(mockConfig))
474
508
  .mockReturnValueOnce(mockMutation);
475
509
 
476
- const { readCsvFile } = require("./csv-reader");
477
- readCsvFile.mockResolvedValue(mockCsvData);
510
+ const { DataReaderFactory } = require("./readers");
511
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
478
512
 
479
513
  mockClient.executeMutation.mockResolvedValue({
480
514
  createProduct: { id: "123" },
@@ -485,15 +519,23 @@ describe("DataMapper", () => {
485
519
  await dataMapper.processEntity("configs/test/mappings/products.json");
486
520
 
487
521
  // Should keep invalid values as strings
488
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
489
- int_field: "1.5",
490
- float_field: "Infinity",
491
- }, undefined);
522
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
523
+ mockMutation,
524
+ {
525
+ int_field: "1.5",
526
+ float_field: "Infinity",
527
+ },
528
+ undefined
529
+ );
492
530
 
493
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
494
- int_field: "not_a_number",
495
- float_field: "1.2.3",
496
- }, undefined);
531
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
532
+ mockMutation,
533
+ {
534
+ int_field: "not_a_number",
535
+ float_field: "1.2.3",
536
+ },
537
+ undefined
538
+ );
497
539
 
498
540
  consoleSpy.mockRestore();
499
541
  });
@@ -527,24 +569,33 @@ describe("DataMapper", () => {
527
569
  .mockReturnValueOnce(JSON.stringify(mockConfig))
528
570
  .mockReturnValueOnce(mockMutation);
529
571
 
530
- const { readCsvFile } = require("./csv-reader");
531
- readCsvFile.mockResolvedValue(mockCsvData);
572
+ const { DataReaderFactory } = require("./readers");
573
+ DataReaderFactory.getReader().readFile.mockResolvedValue(mockCsvData);
532
574
 
533
575
  mockClient.executeMutation.mockResolvedValue({
534
576
  createProduct: { id: "123" },
535
577
  });
536
578
 
537
579
  // Create verbose mapper to test the logging
538
- const verboseMapper = new DataMapper(mockClient, testBasePath, mockMetrics, true);
580
+ const verboseMapper = new DataMapper(
581
+ mockClient,
582
+ testBasePath,
583
+ mockMetrics,
584
+ true
585
+ );
539
586
  const consoleSpy = jest.spyOn(console, "log").mockImplementation();
540
587
 
541
588
  await verboseMapper.processEntity("configs/test/mappings/products.json");
542
589
 
543
590
  // Should keep custom scalar as string
544
- expect(mockClient.executeMutation).toHaveBeenCalledWith(mockMutation, {
545
- name: "Widget",
546
- custom_field: "123",
547
- }, undefined);
591
+ expect(mockClient.executeMutation).toHaveBeenCalledWith(
592
+ mockMutation,
593
+ {
594
+ name: "Widget",
595
+ custom_field: "123",
596
+ },
597
+ undefined
598
+ );
548
599
 
549
600
  expect(consoleSpy).toHaveBeenCalledWith(
550
601
  'Unknown GraphQL type "CustomScalar" for variable $custom_field. Keeping value as string.'