@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.
- package/README.md +138 -6
- package/bin/cli.js +53 -52
- package/dist/mapper.d.ts +9 -4
- package/dist/mapper.d.ts.map +1 -1
- package/dist/readers/csv.d.ts +10 -0
- package/dist/readers/csv.d.ts.map +1 -0
- package/dist/readers/csv.test.d.ts +2 -0
- package/dist/readers/csv.test.d.ts.map +1 -0
- package/dist/readers/data-reader.d.ts +21 -0
- package/dist/readers/data-reader.d.ts.map +1 -0
- package/dist/readers/data-reader.test.d.ts +2 -0
- package/dist/readers/data-reader.test.d.ts.map +1 -0
- package/dist/readers/index.d.ts +6 -0
- package/dist/readers/index.d.ts.map +1 -0
- package/dist/readers/json.d.ts +6 -0
- package/dist/readers/json.d.ts.map +1 -0
- package/dist/readers/json.test.d.ts +2 -0
- package/dist/readers/json.test.d.ts.map +1 -0
- package/dist/readers/jsonl.d.ts +6 -0
- package/dist/readers/jsonl.d.ts.map +1 -0
- package/dist/readers/jsonl.test.d.ts +2 -0
- package/dist/readers/jsonl.test.d.ts.map +1 -0
- package/dist/readers/yaml.d.ts +6 -0
- package/dist/readers/yaml.d.ts.map +1 -0
- package/dist/readers/yaml.test.d.ts +2 -0
- package/dist/readers/yaml.test.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +10 -25
- package/src/graphql-client.test.ts +19 -4
- package/src/mapper.test.ts +115 -64
- package/src/mapper.ts +138 -33
- package/src/{csv-reader.test.ts → readers/csv.test.ts} +1 -1
- package/src/readers/csv.ts +29 -0
- package/src/readers/data-reader.test.ts +104 -0
- package/src/readers/data-reader.ts +61 -0
- package/src/readers/index.ts +18 -0
- package/src/readers/json.test.ts +80 -0
- package/src/readers/json.ts +27 -0
- package/src/readers/jsonl.test.ts +96 -0
- package/src/readers/jsonl.ts +28 -0
- package/src/readers/yaml.test.ts +95 -0
- package/src/readers/yaml.ts +28 -0
- package/dist/csv-reader.d.ts +0 -5
- package/dist/csv-reader.d.ts.map +0 -1
- package/dist/csv-reader.test.d.ts +0 -2
- package/dist/csv-reader.test.d.ts.map +0 -1
- 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
|
|
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
|
-
|
|
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
|
|
23
|
+
private mapRowToVariables;
|
|
24
|
+
private getValueByPath;
|
|
25
|
+
private mapNestedObject;
|
|
21
26
|
private extractVariableTypes;
|
|
22
27
|
private extractTypeName;
|
|
23
28
|
private convertValue;
|
package/dist/mapper.d.ts.map
CHANGED
|
@@ -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;
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"json.test.d.ts","sourceRoot":"","sources":["../../src/readers/json.test.ts"],"names":[],"mappings":""}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl.test.d.ts","sourceRoot":"","sources":["../../src/readers/jsonl.test.ts"],"names":[],"mappings":""}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"yaml.test.d.ts","sourceRoot":"","sources":["../../src/readers/yaml.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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);
|
package/src/mapper.test.ts
CHANGED
|
@@ -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("./
|
|
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 {
|
|
118
|
-
|
|
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(
|
|
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(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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 {
|
|
169
|
-
|
|
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 {
|
|
214
|
-
|
|
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(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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 {
|
|
252
|
-
|
|
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(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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 {
|
|
282
|
-
|
|
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 {
|
|
313
|
-
|
|
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 {
|
|
369
|
-
|
|
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(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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 {
|
|
417
|
-
|
|
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(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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 {
|
|
477
|
-
|
|
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(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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(
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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 {
|
|
531
|
-
|
|
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(
|
|
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(
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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.'
|