@jackchuka/gql-ingest 3.1.9 → 4.0.0
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 +72 -71
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/index.js +98 -100
- package/dist/cli/templates/index.d.ts +1 -1
- package/dist/cli/templates/index.d.ts.map +1 -1
- package/dist/index.js +65 -118
- package/dist/index.js.map +3 -3
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/events.d.ts +0 -2
- package/dist/lib/events.d.ts.map +1 -1
- package/dist/lib/gql-ingest.d.ts +7 -12
- package/dist/lib/gql-ingest.d.ts.map +1 -1
- package/dist/lib/mapper.d.ts +2 -3
- package/dist/lib/mapper.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ A TypeScript library and CLI tool that reads data from multiple formats (CSV, JS
|
|
|
28
28
|
npm install -g @jackchuka/gql-ingest
|
|
29
29
|
|
|
30
30
|
# Or use with npx (no installation required)
|
|
31
|
-
npx @jackchuka/gql-ingest
|
|
31
|
+
npx @jackchuka/gql-ingest -e <url> [-c config.yaml] entity1/entity.json entity2/entity.json
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
### For Development
|
|
@@ -46,13 +46,13 @@ Initialize a new configuration and start ingesting data in minutes:
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
# Create a new configuration directory
|
|
49
|
-
gql-ingest init ./my-
|
|
49
|
+
gql-ingest init ./my-project
|
|
50
50
|
|
|
51
51
|
# Add a new entity
|
|
52
|
-
gql-ingest add users -p ./my-
|
|
52
|
+
gql-ingest add users -p ./my-project -f json --fields "id,name,email"
|
|
53
53
|
|
|
54
54
|
# Run ingestion
|
|
55
|
-
gql-ingest -e https://your-api.com/graphql
|
|
55
|
+
gql-ingest -e https://your-api.com/graphql ./my-project/users/entity.json
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
## Usage
|
|
@@ -75,11 +75,14 @@ Options:
|
|
|
75
75
|
|
|
76
76
|
This creates:
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
```
|
|
79
|
+
my-project/
|
|
80
|
+
├── config.yaml
|
|
81
|
+
└── example/
|
|
82
|
+
├── entity.json # entity definition (name: "example")
|
|
83
|
+
├── example.csv
|
|
84
|
+
└── example.graphql
|
|
85
|
+
```
|
|
83
86
|
|
|
84
87
|
#### Add Entity
|
|
85
88
|
|
|
@@ -101,15 +104,14 @@ Interactive mode prompts for format, fields, and mutation name. Use `--no-intera
|
|
|
101
104
|
|
|
102
105
|
#### Run Ingestion
|
|
103
106
|
|
|
104
|
-
Ingest data from
|
|
107
|
+
Ingest data from entity files into GraphQL API:
|
|
105
108
|
|
|
106
109
|
```bash
|
|
107
|
-
gql-ingest [options]
|
|
110
|
+
gql-ingest [options] <entity files...>
|
|
108
111
|
|
|
109
112
|
Options:
|
|
110
113
|
-e, --endpoint <url> GraphQL endpoint URL (required)
|
|
111
|
-
-c, --config <path> Path to
|
|
112
|
-
-n, --entities <list> Comma-separated list of entities to process
|
|
114
|
+
-c, --config <path> Path to config.yaml (optional, for orchestration settings)
|
|
113
115
|
-h, --headers <headers> JSON string of headers
|
|
114
116
|
-f, --format <format> Override data format detection
|
|
115
117
|
-q, --quiet Suppress output
|
|
@@ -118,22 +120,22 @@ Options:
|
|
|
118
120
|
### CLI Examples
|
|
119
121
|
|
|
120
122
|
```bash
|
|
121
|
-
# Basic usage
|
|
123
|
+
# Basic usage — pass entity files as positional arguments
|
|
122
124
|
gql-ingest \
|
|
123
125
|
-e https://your-graphql-api.com/graphql \
|
|
124
|
-
|
|
126
|
+
./examples/demo/items/entity.json
|
|
125
127
|
|
|
126
128
|
# With authentication headers
|
|
127
129
|
gql-ingest \
|
|
128
130
|
-e https://your-graphql-api.com/graphql \
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
+
-h '{"Authorization": "Bearer YOUR_TOKEN"}' \
|
|
132
|
+
./examples/demo/users/entity.json ./examples/demo/items/entity.json
|
|
131
133
|
|
|
132
|
-
#
|
|
134
|
+
# With optional config for orchestration (retry, parallelism, dependencies)
|
|
133
135
|
gql-ingest \
|
|
134
136
|
-e https://your-graphql-api.com/graphql \
|
|
135
|
-
-c ./examples/demo \
|
|
136
|
-
|
|
137
|
+
-c ./examples/demo/config.yaml \
|
|
138
|
+
./examples/demo/users/entity.json ./examples/demo/items/entity.json
|
|
137
139
|
```
|
|
138
140
|
|
|
139
141
|
### Programmatic API
|
|
@@ -160,8 +162,8 @@ const client = new GQLIngest({
|
|
|
160
162
|
logger: createConsoleLogger({ prefix: "my-app" }), // Optional: enable logging with prefix
|
|
161
163
|
});
|
|
162
164
|
|
|
163
|
-
// Ingest all data from
|
|
164
|
-
const result = await client.ingest("./
|
|
165
|
+
// Ingest all data from entity files
|
|
166
|
+
const result = await client.ingest(["./users/entity.json"]);
|
|
165
167
|
|
|
166
168
|
// Check if ingestion was successful
|
|
167
169
|
if (result.success) {
|
|
@@ -172,15 +174,14 @@ if (result.success) {
|
|
|
172
174
|
}
|
|
173
175
|
```
|
|
174
176
|
|
|
175
|
-
#### Processing
|
|
177
|
+
#### Processing Multiple Entities
|
|
176
178
|
|
|
177
179
|
```typescript
|
|
178
|
-
// Process
|
|
179
|
-
const result = await client.
|
|
180
|
+
// Process multiple entity files
|
|
181
|
+
const result = await client.ingest(["./users/entity.json", "./products/entity.json"]);
|
|
180
182
|
|
|
181
|
-
//
|
|
182
|
-
const result = await client.ingest("./
|
|
183
|
-
entities: ["users", "products"],
|
|
183
|
+
// With options
|
|
184
|
+
const result = await client.ingest(["./users/entity.json", "./products/entity.json"], {
|
|
184
185
|
format: "csv", // Optional: override format detection
|
|
185
186
|
});
|
|
186
187
|
```
|
|
@@ -203,10 +204,10 @@ import {
|
|
|
203
204
|
const logger = createConsoleLogger();
|
|
204
205
|
const metrics = new MetricsCollector();
|
|
205
206
|
const client = new GraphQLClientWrapper(endpoint, headers, metrics, logger);
|
|
206
|
-
const mapper = new DataMapper(client,
|
|
207
|
+
const mapper = new DataMapper(client, metrics, logger);
|
|
207
208
|
|
|
208
209
|
// Load configuration
|
|
209
|
-
const config = loadConfig(
|
|
210
|
+
const config = loadConfig();
|
|
210
211
|
|
|
211
212
|
// Process entities with custom logic
|
|
212
213
|
// ... your custom implementation
|
|
@@ -217,8 +218,7 @@ const config = loadConfig("./config");
|
|
|
217
218
|
**GQLIngest Class Methods:**
|
|
218
219
|
|
|
219
220
|
- `constructor(options: GQLIngestOptions)` - Initialize the client
|
|
220
|
-
- `ingest(
|
|
221
|
-
- `ingestEntities(configPath: string, entities: string[])` - Process specific entities
|
|
221
|
+
- `ingest(entityPaths: string[], options?: IngestOptions)` - Ingest data from entity files
|
|
222
222
|
- `getMetrics()` - Get current processing metrics
|
|
223
223
|
- `getMetricsSummary()` - Get formatted metrics summary
|
|
224
224
|
- `setLogger(logger: Logger)` - Set custom logger
|
|
@@ -258,14 +258,14 @@ client.on("cancelled", (p) => console.log(`Cancelled: ${p.reason}`));
|
|
|
258
258
|
// Handle graceful shutdown
|
|
259
259
|
process.on("SIGINT", () => client.cancel("User interrupted"));
|
|
260
260
|
|
|
261
|
-
await client.ingest("./
|
|
261
|
+
await client.ingest(["./users/entity.json"]);
|
|
262
262
|
```
|
|
263
263
|
|
|
264
264
|
**Available Events:**
|
|
265
265
|
|
|
266
266
|
| Event | When Emitted | Key Payload Fields |
|
|
267
267
|
| ---------------- | ------------------------ | ------------------------------------------------- |
|
|
268
|
-
| `started` | Ingestion begins | `
|
|
268
|
+
| `started` | Ingestion begins | `entityNames`, `totalWaves` |
|
|
269
269
|
| `progress` | Periodic interval | `progressPercent`, `successfulRows`, `failedRows` |
|
|
270
270
|
| `entityStart` | Entity processing begins | `entityName`, `totalRows`, `waveIndex` |
|
|
271
271
|
| `entityComplete` | Entity processing ends | `entityName`, `metrics`, `success` |
|
|
@@ -283,12 +283,12 @@ Cancel in-progress ingestion using the `cancel()` method or external AbortContro
|
|
|
283
283
|
// Method 1: Using cancel()
|
|
284
284
|
const client = new GQLIngest({ endpoint: "..." });
|
|
285
285
|
process.on("SIGINT", () => client.cancel("User interrupted"));
|
|
286
|
-
await client.ingest("./
|
|
286
|
+
await client.ingest(["./users/entity.json"]);
|
|
287
287
|
|
|
288
288
|
// Method 2: Using external AbortController
|
|
289
289
|
const controller = new AbortController();
|
|
290
290
|
setTimeout(() => controller.abort("Timeout"), 60000);
|
|
291
|
-
await client.ingest("./
|
|
291
|
+
await client.ingest(["./users/entity.json"], { signal: controller.signal });
|
|
292
292
|
```
|
|
293
293
|
|
|
294
294
|
#### TypeScript Support
|
|
@@ -389,33 +389,31 @@ entityConfig:
|
|
|
389
389
|
|
|
390
390
|
## Selective Entity Processing
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
Pass only the entity files you want to process as positional arguments:
|
|
393
393
|
|
|
394
|
-
- Process multiple entities:
|
|
395
|
-
- Process a single entity:
|
|
396
|
-
- Entities are processed in dependency order automatically
|
|
394
|
+
- Process multiple entities: `gql-ingest -e <url> users/entity.json products/entity.json`
|
|
395
|
+
- Process a single entity: `gql-ingest -e <url> items/entity.json`
|
|
396
|
+
- Entities are processed in dependency order automatically when `-c config.yaml` is provided
|
|
397
397
|
- Missing dependencies will trigger a warning but not prevent execution
|
|
398
398
|
|
|
399
|
-
**Note**: When using
|
|
399
|
+
**Note**: When using entity dependencies defined in `config.yaml`, the tool will warn you about any missing dependencies but will still attempt to process the selected entities. Ensure dependent data exists in your GraphQL API before processing entities with unmet dependencies.
|
|
400
400
|
|
|
401
401
|
## Configuration
|
|
402
402
|
|
|
403
|
-
The
|
|
404
|
-
|
|
405
|
-
- `mappings/` - JSON files that map CSV columns to GraphQL variables
|
|
406
|
-
- `config.yaml` - _(Optional)_ Parallel processing and dependency configuration
|
|
403
|
+
Each entity is defined by an `entity.json` file colocated with its data and GraphQL mutation files. The entity name comes from the `name` field inside the file, not from the filename or directory name. Paths in the entity file resolve relative to the entity file's directory.
|
|
407
404
|
|
|
408
|
-
|
|
405
|
+
The optional `-c` flag points to a `config.yaml` file for orchestration settings (retry, parallelism, dependencies).
|
|
409
406
|
|
|
410
407
|
### Example Configuration
|
|
411
408
|
|
|
412
|
-
**examples/demo/
|
|
409
|
+
**examples/demo/items/entity.json** (entity definition):
|
|
413
410
|
|
|
414
411
|
```json
|
|
415
412
|
{
|
|
416
|
-
"
|
|
413
|
+
"name": "items",
|
|
414
|
+
"dataFile": "items.csv",
|
|
417
415
|
"dataFormat": "csv",
|
|
418
|
-
"graphqlFile": "
|
|
416
|
+
"graphqlFile": "items.graphql",
|
|
419
417
|
"mapping": {
|
|
420
418
|
"name": "item_name",
|
|
421
419
|
"sku": "item_sku"
|
|
@@ -423,7 +421,7 @@ Each entity has three corresponding files across these directories with matching
|
|
|
423
421
|
}
|
|
424
422
|
```
|
|
425
423
|
|
|
426
|
-
**examples/demo/
|
|
424
|
+
**examples/demo/items/items.csv**:
|
|
427
425
|
|
|
428
426
|
```csv
|
|
429
427
|
item_name,item_sku
|
|
@@ -431,7 +429,7 @@ Item1,item-1-sku
|
|
|
431
429
|
Item2,item-2-sku
|
|
432
430
|
```
|
|
433
431
|
|
|
434
|
-
**examples/demo/
|
|
432
|
+
**examples/demo/items/items.graphql**:
|
|
435
433
|
|
|
436
434
|
```graphql
|
|
437
435
|
mutation CreateItem($name: String!, $sku: String!) {
|
|
@@ -487,11 +485,11 @@ GQL Ingest now supports multiple data formats beyond CSV for more flexible data
|
|
|
487
485
|
The tool automatically detects the format based on file extension, or you can specify it explicitly:
|
|
488
486
|
|
|
489
487
|
```bash
|
|
490
|
-
# Auto-detect from
|
|
491
|
-
gql-ingest
|
|
488
|
+
# Auto-detect from entity file configuration
|
|
489
|
+
gql-ingest -e <url> ./items/entity.json
|
|
492
490
|
|
|
493
491
|
# Force specific format
|
|
494
|
-
gql-ingest
|
|
492
|
+
gql-ingest -e <url> --format json ./items/entity.json
|
|
495
493
|
```
|
|
496
494
|
|
|
497
495
|
### JSON/YAML Format Examples
|
|
@@ -500,7 +498,7 @@ gql-ingest --endpoint <url> --config ./config --format json
|
|
|
500
498
|
|
|
501
499
|
For complex GraphQL mutations with nested input types, you can map the entire data object:
|
|
502
500
|
|
|
503
|
-
**
|
|
501
|
+
**products/products.json** (data file):
|
|
504
502
|
|
|
505
503
|
```json
|
|
506
504
|
[
|
|
@@ -531,13 +529,14 @@ For complex GraphQL mutations with nested input types, you can map the entire da
|
|
|
531
529
|
]
|
|
532
530
|
```
|
|
533
531
|
|
|
534
|
-
**
|
|
532
|
+
**products/entity.json** (entity definition):
|
|
535
533
|
|
|
536
534
|
```json
|
|
537
535
|
{
|
|
538
|
-
"
|
|
536
|
+
"name": "products",
|
|
537
|
+
"dataFile": "products.json",
|
|
539
538
|
"dataFormat": "json",
|
|
540
|
-
"graphqlFile": "
|
|
539
|
+
"graphqlFile": "newProduct.graphql",
|
|
541
540
|
"mapping": {
|
|
542
541
|
"input": "$" // Map entire object to input variable
|
|
543
542
|
}
|
|
@@ -548,7 +547,7 @@ For complex GraphQL mutations with nested input types, you can map the entire da
|
|
|
548
547
|
|
|
549
548
|
For transforming flat JSON into nested structures:
|
|
550
549
|
|
|
551
|
-
**
|
|
550
|
+
**products/products-flat.json** (data file):
|
|
552
551
|
|
|
553
552
|
```json
|
|
554
553
|
[
|
|
@@ -560,12 +559,13 @@ For transforming flat JSON into nested structures:
|
|
|
560
559
|
]
|
|
561
560
|
```
|
|
562
561
|
|
|
563
|
-
**
|
|
562
|
+
**products/entity.json** (entity definition with path-based mapping):
|
|
564
563
|
|
|
565
564
|
```json
|
|
566
565
|
{
|
|
567
|
-
"
|
|
568
|
-
"
|
|
566
|
+
"name": "products",
|
|
567
|
+
"dataFile": "products-flat.json",
|
|
568
|
+
"graphqlFile": "newProduct.graphql",
|
|
569
569
|
"mapping": {
|
|
570
570
|
"input": {
|
|
571
571
|
"name": "$.product_name",
|
|
@@ -580,7 +580,7 @@ For transforming flat JSON into nested structures:
|
|
|
580
580
|
|
|
581
581
|
YAML provides a more readable alternative:
|
|
582
582
|
|
|
583
|
-
**
|
|
583
|
+
**products/products.yaml**:
|
|
584
584
|
|
|
585
585
|
```yaml
|
|
586
586
|
- name: Premium T-Shirt
|
|
@@ -614,21 +614,22 @@ pnpm run test # Run test suite
|
|
|
614
614
|
|
|
615
615
|
## How It Works
|
|
616
616
|
|
|
617
|
-
1. **
|
|
618
|
-
2. **
|
|
619
|
-
3. **
|
|
617
|
+
1. **Entity Loading**: The tool reads the entity JSON files passed as arguments
|
|
618
|
+
2. **Path Resolution**: Data files and GraphQL mutations are resolved relative to each entity file's directory
|
|
619
|
+
3. **Dependency Resolution**: If `-c config.yaml` is provided, analyzes `entityDependencies` to create execution waves
|
|
620
|
+
4. **Parallel Processing**: For each dependency wave:
|
|
620
621
|
- Processes up to `entityConcurrency` entities simultaneously
|
|
621
|
-
- Within each entity, processes up to `concurrency`
|
|
622
|
+
- Within each entity, processes up to `concurrency` rows concurrently
|
|
622
623
|
- Waits for the entire wave to complete before starting the next wave
|
|
623
|
-
|
|
624
|
+
5. **GraphQL Execution**: For each data row:
|
|
624
625
|
- Loads the GraphQL mutation definition
|
|
625
|
-
- Maps
|
|
626
|
+
- Maps data fields to GraphQL variables using the mapping configuration
|
|
626
627
|
- Executes the mutation against the GraphQL endpoint
|
|
627
|
-
|
|
628
|
+
6. **Error Handling & Retries**:
|
|
628
629
|
- Failed mutations are automatically retried with exponential backoff
|
|
629
630
|
- Non-retryable errors (e.g., validation failures) are logged and skipped
|
|
630
631
|
- Configurable retry policies per entity type
|
|
631
|
-
|
|
632
|
+
7. **Metrics & Monitoring**:
|
|
632
633
|
- Real-time progress tracking and success/failure rates
|
|
633
634
|
- Retry attempt counts and success rates
|
|
634
635
|
- Detailed per-entity performance breakdown
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkE1D"}
|