@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: 'Opsone/jerem'`):
81
+ Or with custom directory configuration (`directories.etl: 'etl'`):
82
82
 
83
- - `Opsone/jerem/sources/import_product_csv_source.ts`
84
- - `Opsone/jerem/transforms/import_product_csv_to_db_transform.ts`
85
- - `Opsone/jerem/destinations/import_product_db_destination.ts`
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-alpha.1
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.snakeCase(this.name),
65
- string.snakeCase(sourceDetails),
66
- 'source',
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.snakeCase(this.name),
72
- string.snakeCase(destinationDetails),
73
- 'destination',
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.snakeCase(this.name),
79
- string.snakeCase(sourceDetails),
80
- 'to',
81
- string.snakeCase(destinationDetails),
82
- 'transform',
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.0-alpha.3",
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": "alpha"
53
+ "tag": "latest"
54
54
  },
55
55
  "volta": {
56
56
  "node": "22.17.0"