@jrmc/adonis-etl 1.0.0-alpha.3 → 1.0.2
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
CHANGED
|
@@ -78,11 +78,11 @@ This will create (with default configuration):
|
|
|
78
78
|
- `app/etl/transforms/import_product_csv_to_db_transform.ts`
|
|
79
79
|
- `app/etl/destinations/import_product_db_destination.ts`
|
|
80
80
|
|
|
81
|
-
Or with custom directory configuration (`directories.etl: '
|
|
81
|
+
Or with custom directory configuration (`directories.etl: 'etl'`):
|
|
82
82
|
|
|
83
|
-
- `
|
|
84
|
-
- `
|
|
85
|
-
- `
|
|
83
|
+
- `etl/sources/import_product_csv_source.ts`
|
|
84
|
+
- `etl/transforms/import_product_csv_to_db_transform.ts`
|
|
85
|
+
- `etl/destinations/import_product_db_destination.ts`
|
|
86
86
|
|
|
87
87
|
## Generated Files
|
|
88
88
|
|
|
@@ -123,6 +123,74 @@ export default class ImportProductDbDestination implements Destination {
|
|
|
123
123
|
}
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
## Usage Examples
|
|
127
|
+
|
|
128
|
+
The `sample/` folder contains two complete ETL implementation examples:
|
|
129
|
+
|
|
130
|
+
### 1. Books Import (Source → Destination)
|
|
131
|
+
|
|
132
|
+
This example shows a simple ETL process without transformation:
|
|
133
|
+
|
|
134
|
+
**Command:** `node ace import:books`
|
|
135
|
+
|
|
136
|
+
**Components:**
|
|
137
|
+
- **Source**: `book_csv_source.ts` - Reads a CSV file of books (5M records) with batch processing (500 items)
|
|
138
|
+
- **Destination**: `book_db_destination.ts` - Inserts data into database via `db.table().multiInsert()`
|
|
139
|
+
|
|
140
|
+
**Features:**
|
|
141
|
+
- Batch processing for performance optimization
|
|
142
|
+
- CSV error handling (empty lines and errors ignored)
|
|
143
|
+
- Optimized buffer (128KB) for large files
|
|
144
|
+
|
|
145
|
+
### 2. Products Import (Source → Transform → Destination)
|
|
146
|
+
|
|
147
|
+
This example shows a complete ETL process with data transformation:
|
|
148
|
+
|
|
149
|
+
**Command:** `node ace import:products`
|
|
150
|
+
|
|
151
|
+
**Components:**
|
|
152
|
+
- **Source**: `product_csv_source.ts` - Reads a CSV file of products (500K records)
|
|
153
|
+
- **Transform**: `product_csv_to_db_transform.ts` - Transforms CSV data (French column names → English)
|
|
154
|
+
- **Destination**: `product_db_destination.ts` - Saves via Lucid model `Product.create()`
|
|
155
|
+
|
|
156
|
+
**Features:**
|
|
157
|
+
- Column name transformation (e.g., `Nom` → `name`, `Prix` → `price`)
|
|
158
|
+
- AdonisJS model usage for persistence
|
|
159
|
+
- Data processing logging
|
|
160
|
+
|
|
161
|
+
### Example Files Structure
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
sample/
|
|
165
|
+
├── commands/
|
|
166
|
+
│ ├── import_books.ts # Books import command
|
|
167
|
+
│ └── import_products.ts # Products import command
|
|
168
|
+
├── etl/
|
|
169
|
+
│ ├── sources/
|
|
170
|
+
│ │ ├── book_csv_source.ts
|
|
171
|
+
│ │ └── product_csv_source.ts
|
|
172
|
+
│ ├── transforms/
|
|
173
|
+
│ │ └── product_csv_to_db_transform.ts
|
|
174
|
+
│ ├── destinations/
|
|
175
|
+
│ │ ├── book_db_destination.ts
|
|
176
|
+
│ │ └── product_db_destination.ts
|
|
177
|
+
│ └── resources/
|
|
178
|
+
│ ├── books.csv # Sample data
|
|
179
|
+
│ └── products.csv # Sample data
|
|
180
|
+
└── app/models/
|
|
181
|
+
├── book.ts # Book model
|
|
182
|
+
└── product.ts # Product model
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
These examples demonstrate different possible approaches:
|
|
186
|
+
- **Batch processing** vs **line-by-line processing**
|
|
187
|
+
- **Direct database insertion** vs **Lucid model usage**
|
|
188
|
+
- **With or without data transformation**
|
|
189
|
+
|
|
190
|
+
## Performance Optimization
|
|
191
|
+
|
|
192
|
+
For large-scale ETL operations, consider integrating with a job queue system (like BullMQ, or AdonisJS Queue package) to run ETL processes asynchronously, distribute workload across multiple workers, and improve reliability with automatic retry mechanisms.
|
|
193
|
+
|
|
126
194
|
## Dependencies
|
|
127
195
|
|
|
128
196
|
This package requires:
|
|
@@ -190,11 +258,9 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
190
258
|
|
|
191
259
|
## Changelog
|
|
192
260
|
|
|
193
|
-
### 1.0.0
|
|
261
|
+
### 1.0.0
|
|
194
262
|
- Initial release
|
|
195
263
|
- Interactive ETL component generation
|
|
196
264
|
- Support for Source, Transform, and Destination components
|
|
197
265
|
- TypeScript support
|
|
198
|
-
|
|
199
|
-
### 1.0.0-alpha.2
|
|
200
266
|
- Custom directory configuration support via `directories.etl` in adonisrc.ts
|
|
@@ -19,17 +19,13 @@ export default class MakeEtl extends BaseCommand {
|
|
|
19
19
|
static description = 'Create a new ETL files (source, transform, destination)';
|
|
20
20
|
async run() {
|
|
21
21
|
// Ask which ETL components to create
|
|
22
|
-
const components = await this.prompt.multiple('Which ETL components do you want to create?', [
|
|
23
|
-
'Source',
|
|
24
|
-
'Transform',
|
|
25
|
-
'Destination'
|
|
26
|
-
], {
|
|
22
|
+
const components = await this.prompt.multiple('Which ETL components do you want to create?', ['Source', 'Transform', 'Destination'], {
|
|
27
23
|
validate: (value) => {
|
|
28
24
|
if (!value || value.length === 0) {
|
|
29
25
|
return 'You must select at least one component';
|
|
30
26
|
}
|
|
31
27
|
return true;
|
|
32
|
-
}
|
|
28
|
+
},
|
|
33
29
|
});
|
|
34
30
|
// Ask for source details if source is selected
|
|
35
31
|
let sourceDetails = null;
|
|
@@ -40,7 +36,7 @@ export default class MakeEtl extends BaseCommand {
|
|
|
40
36
|
return 'Source type is required';
|
|
41
37
|
}
|
|
42
38
|
return true;
|
|
43
|
-
}
|
|
39
|
+
},
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
42
|
// Ask for destination details if destination is selected
|
|
@@ -52,7 +48,7 @@ export default class MakeEtl extends BaseCommand {
|
|
|
52
48
|
return 'Destination type is required';
|
|
53
49
|
}
|
|
54
50
|
return true;
|
|
55
|
-
}
|
|
51
|
+
},
|
|
56
52
|
});
|
|
57
53
|
}
|
|
58
54
|
const codemods = await this.createCodemods();
|
|
@@ -61,30 +57,30 @@ export default class MakeEtl extends BaseCommand {
|
|
|
61
57
|
for (const component of components) {
|
|
62
58
|
if (component === 'Source') {
|
|
63
59
|
className = [
|
|
64
|
-
string.
|
|
65
|
-
string.
|
|
66
|
-
'
|
|
67
|
-
].join('
|
|
60
|
+
string.pascalCase(this.name),
|
|
61
|
+
string.pascalCase(sourceDetails),
|
|
62
|
+
'Source',
|
|
63
|
+
].join('');
|
|
68
64
|
}
|
|
69
65
|
else if (component === 'Destination') {
|
|
70
66
|
className = [
|
|
71
|
-
string.
|
|
72
|
-
string.
|
|
73
|
-
'
|
|
74
|
-
].join('
|
|
67
|
+
string.pascalCase(this.name),
|
|
68
|
+
string.pascalCase(destinationDetails),
|
|
69
|
+
'Destination',
|
|
70
|
+
].join('');
|
|
75
71
|
}
|
|
76
72
|
else if (component === 'Transform') {
|
|
77
73
|
className = [
|
|
78
|
-
string.
|
|
79
|
-
string.
|
|
80
|
-
'
|
|
81
|
-
string.
|
|
82
|
-
'
|
|
83
|
-
].join('
|
|
74
|
+
string.pascalCase(this.name),
|
|
75
|
+
string.pascalCase(sourceDetails),
|
|
76
|
+
'To',
|
|
77
|
+
string.pascalCase(destinationDetails),
|
|
78
|
+
'Transform',
|
|
79
|
+
].join('');
|
|
84
80
|
}
|
|
85
81
|
const stubPath = `make/etl/${component.toLowerCase()}s/main.ts.stub`;
|
|
86
82
|
await codemods.makeUsingStub(stubsRoot, stubPath, {
|
|
87
|
-
className
|
|
83
|
+
className,
|
|
88
84
|
});
|
|
89
85
|
}
|
|
90
86
|
this.logger.success(`ETL files created successfully for: ${this.name}`);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
to: app.makePath(app.rcFile.directories['etl'] || 'app/etl/', 'destinations', resourceFileName)
|
|
5
5
|
})
|
|
6
6
|
}}}
|
|
7
|
-
import { Destination } from '@jrmc/adonis-etl'
|
|
7
|
+
import type { Destination } from '@jrmc/adonis-etl'
|
|
8
8
|
|
|
9
9
|
export default class {{ className }} implements Destination {
|
|
10
10
|
async write(row: unknown) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
to: app.makePath(app.rcFile.directories['etl'] || 'app/etl/', 'sources', resourceFileName)
|
|
5
5
|
})
|
|
6
6
|
}}}
|
|
7
|
-
import { Source } from '@jrmc/adonis-etl'
|
|
7
|
+
import type { Source } from '@jrmc/adonis-etl'
|
|
8
8
|
|
|
9
9
|
export default class {{ className }} implements Source {
|
|
10
10
|
async *each() {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
to: app.makePath(app.rcFile.directories['etl'] || 'app/etl/', 'transforms', resourceFileName)
|
|
5
5
|
})
|
|
6
6
|
}}}
|
|
7
|
-
import { Transform } from '@jrmc/adonis-etl'
|
|
7
|
+
import type { Transform } from '@jrmc/adonis-etl'
|
|
8
8
|
|
|
9
9
|
export default class {{ className }} implements Transform {
|
|
10
10
|
async process(row: unknown) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jrmc/adonis-etl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"adonisjs",
|
|
6
6
|
"etl",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"prettier": "@adonisjs/prettier-config",
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public",
|
|
53
|
-
"tag": "
|
|
53
|
+
"tag": "latest"
|
|
54
54
|
},
|
|
55
55
|
"volta": {
|
|
56
56
|
"node": "22.17.0"
|