etlplus 0.8.4__tar.gz → 0.9.0__tar.gz
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.
- {etlplus-0.8.4 → etlplus-0.9.0}/DEMO.md +25 -23
- {etlplus-0.8.4/etlplus.egg-info → etlplus-0.9.0}/PKG-INFO +61 -29
- {etlplus-0.8.4 → etlplus-0.9.0}/README.md +60 -28
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/commands.py +170 -116
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/constants.py +13 -7
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/handlers.py +3 -4
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/io.py +21 -5
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/main.py +2 -1
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/options.py +1 -1
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/state.py +3 -2
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/load.py +1 -1
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/utils.py +1 -1
- {etlplus-0.8.4 → etlplus-0.9.0/etlplus.egg-info}/PKG-INFO +61 -29
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/README.md +4 -3
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/quickstart_python.py +1 -1
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/conftest.py +2 -2
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_cli.py +127 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_pagination_strategy.py +2 -2
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/cli/test_u_cli_handlers.py +335 -19
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/cli/test_u_cli_main.py +3 -3
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_load.py +1 -1
- {etlplus-0.8.4 → etlplus-0.9.0}/.coveragerc +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.editorconfig +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.gitattributes +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.github/actions/python-bootstrap/action.yml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.github/workflows/ci.yml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.gitignore +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.pre-commit-config.yaml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/.ruff.toml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/CODE_OF_CONDUCT.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/CONTRIBUTING.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/LICENSE +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/MANIFEST.in +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/Makefile +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/REFERENCES.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/docs/README.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/docs/pipeline-guide.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/docs/snippets/installation_version.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/__main__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/__version__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/README.md +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/auth.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/config.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/endpoint_client.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/errors.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/pagination/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/pagination/client.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/pagination/config.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/pagination/paginator.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/rate_limiting/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/rate_limiting/config.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/rate_limiting/rate_limiter.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/request_manager.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/retry_manager.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/transport.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/api/types.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/cli/types.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/connector.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/jobs.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/pipeline.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/profile.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/types.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/config/utils.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/database/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/database/ddl.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/database/engine.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/database/orm.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/database/schema.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/database/types.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/enums.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/extract.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/file.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/mixins.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/py.typed +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/run.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/run_helpers.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/templates/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/templates/ddl.sql.j2 +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/templates/view.sql.j2 +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/transform.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/types.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/validate.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/validation/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus/validation/utils.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus.egg-info/SOURCES.txt +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus.egg-info/dependency_links.txt +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus.egg-info/entry_points.txt +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus.egg-info/requires.txt +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/etlplus.egg-info/top_level.txt +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/configs/ddl_spec.yml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/configs/pipeline.yml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/data/sample.csv +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/data/sample.json +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/data/sample.xml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/data/sample.xsd +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/examples/data/sample.yaml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/pyproject.toml +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/pytest.ini +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/setup.cfg +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/setup.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/__init__.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/conftest.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_examples_data_parity.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_pipeline_smoke.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_pipeline_yaml_load.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_run.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_run_profile_pagination_defaults.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/integration/test_i_run_profile_rate_limit_defaults.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/conftest.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_auth.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_config.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_endpoint_client.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_mocks.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_pagination_client.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_pagination_config.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_paginator.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_rate_limit_config.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_rate_limiter.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_request_manager.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_retry_manager.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_transport.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/api/test_u_types.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/cli/conftest.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/cli/test_u_cli_state.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/config/test_u_config_utils.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/config/test_u_connector.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/config/test_u_jobs.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/config/test_u_pipeline.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/conftest.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/database/test_u_database_ddl.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/database/test_u_database_engine.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/database/test_u_database_orm.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/database/test_u_database_schema.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_enums.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_extract.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_file.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_main.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_mixins.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_run.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_run_helpers.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_transform.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_utils.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_validate.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/test_u_version.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tests/unit/validation/test_u_validation_utils.py +0 -0
- {etlplus-0.8.4 → etlplus-0.9.0}/tools/update_demo_snippets.py +0 -0
|
@@ -89,14 +89,14 @@ $ etlplus validate '{"email": "user@example.com", "age": 25}' \
|
|
|
89
89
|
|
|
90
90
|
### Filter and Select
|
|
91
91
|
```bash
|
|
92
|
-
$ etlplus transform '
|
|
92
|
+
$ etlplus transform --operations '{
|
|
93
|
+
"filter": {"field": "age", "op": "gt", "value": 26},
|
|
94
|
+
"select": ["name", "age"]
|
|
95
|
+
}' '[
|
|
93
96
|
{"name": "John", "age": 30, "city": "NYC"},
|
|
94
97
|
{"name": "Jane", "age": 25, "city": "LA"},
|
|
95
98
|
{"name": "Bob", "age": 35, "city": "Chicago"}
|
|
96
|
-
]'
|
|
97
|
-
"filter": {"field": "age", "op": "gt", "value": 26},
|
|
98
|
-
"select": ["name", "age"]
|
|
99
|
-
}'
|
|
99
|
+
]'
|
|
100
100
|
[
|
|
101
101
|
{
|
|
102
102
|
"name": "John",
|
|
@@ -111,24 +111,19 @@ $ etlplus transform '[
|
|
|
111
111
|
|
|
112
112
|
### Sort Data
|
|
113
113
|
```bash
|
|
114
|
-
$ etlplus transform
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
{"name": "Bob", "score": 90}
|
|
118
|
-
]' --operations '{
|
|
119
|
-
"sort": {"field": "score", "reverse": true}
|
|
120
|
-
}'
|
|
114
|
+
$ etlplus transform -\
|
|
115
|
+
-operations '{"sort": {"field": "score", "reverse": true}}' \
|
|
116
|
+
'[{"name": "Charlie", "score": 85}, {"name": "Alice", "score": 95}, {"name": "Bob", "score": 90}]'
|
|
121
117
|
```
|
|
122
118
|
|
|
123
119
|
### Aggregate Data
|
|
124
120
|
```bash
|
|
125
|
-
$ etlplus transform '
|
|
121
|
+
$ etlplus transform --operations '{"aggregate": {"field": "sales", "func": "sum"}}' \
|
|
122
|
+
'[
|
|
126
123
|
{"product": "A", "sales": 100},
|
|
127
124
|
{"product": "B", "sales": 150},
|
|
128
125
|
{"product": "C", "sales": 200}
|
|
129
|
-
]'
|
|
130
|
-
"aggregate": {"field": "sales", "func": "sum"}
|
|
131
|
-
}'
|
|
126
|
+
]'
|
|
132
127
|
{
|
|
133
128
|
"sum_sales": 450
|
|
134
129
|
}
|
|
@@ -138,7 +133,9 @@ $ etlplus transform '[
|
|
|
138
133
|
|
|
139
134
|
### Load to JSON File
|
|
140
135
|
```bash
|
|
141
|
-
$ etlplus load
|
|
136
|
+
$ etlplus load \
|
|
137
|
+
'{"name": "John", "status": "active"}' \
|
|
138
|
+
output.json --target-type file
|
|
142
139
|
{
|
|
143
140
|
"status": "success",
|
|
144
141
|
"message": "Data loaded to output.json",
|
|
@@ -148,10 +145,12 @@ $ etlplus load '{"name": "John", "status": "active"}' file output.json
|
|
|
148
145
|
|
|
149
146
|
### Load to CSV File
|
|
150
147
|
```bash
|
|
151
|
-
$ etlplus load
|
|
148
|
+
$ etlplus load \
|
|
149
|
+
'[
|
|
152
150
|
{"name": "John", "email": "john@example.com"},
|
|
153
151
|
{"name": "Jane", "email": "jane@example.com"}
|
|
154
|
-
]'
|
|
152
|
+
]' \
|
|
153
|
+
users.csv --target-type file
|
|
155
154
|
{
|
|
156
155
|
"status": "success",
|
|
157
156
|
"message": "Data loaded to users.csv",
|
|
@@ -173,19 +172,22 @@ This example shows a complete ETL workflow:
|
|
|
173
172
|
$ etlplus extract raw_data.csv > extracted.json
|
|
174
173
|
|
|
175
174
|
# Step 2: Transform
|
|
176
|
-
$ etlplus transform
|
|
175
|
+
$ etlplus transform \
|
|
177
176
|
--operations '{
|
|
178
177
|
"filter": {"field": "age", "op": "gte", "value": 18},
|
|
179
178
|
"select": ["name", "email", "age"]
|
|
180
|
-
}'
|
|
179
|
+
}' \
|
|
180
|
+
extracted.json \
|
|
181
|
+
transformed.json
|
|
181
182
|
|
|
182
183
|
# Step 3: Validate
|
|
183
|
-
$ etlplus validate
|
|
184
|
+
$ etlplus validate \
|
|
184
185
|
--rules '{
|
|
185
186
|
"name": {"type": "string", "required": true},
|
|
186
187
|
"email": {"type": "string", "required": true, "pattern": "^[\\w.-]+@[\\w.-]+\\.\\w+$"},
|
|
187
188
|
"age": {"type": "number", "min": 18, "max": 120}
|
|
188
|
-
}'
|
|
189
|
+
}' \
|
|
190
|
+
transformed.json
|
|
189
191
|
|
|
190
192
|
# Step 4: Load
|
|
191
193
|
$ etlplus load transformed.json file final_output.csv
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: etlplus
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: A Swiss Army knife for simple ETL operations
|
|
5
5
|
Home-page: https://github.com/Dagitali/ETLPlus
|
|
6
6
|
Author: ETLPlus Team
|
|
@@ -64,6 +64,7 @@ package and command-line interface for data extraction, validation, transformati
|
|
|
64
64
|
- [Quickstart](#quickstart)
|
|
65
65
|
- [Usage](#usage)
|
|
66
66
|
- [Command Line Interface](#command-line-interface)
|
|
67
|
+
- [Argument Order and Required Options](#argument-order-and-required-options)
|
|
67
68
|
- [Check Pipelines](#check-pipelines)
|
|
68
69
|
- [Render SQL DDL](#render-sql-ddl)
|
|
69
70
|
- [Extract Data](#extract-data)
|
|
@@ -151,8 +152,8 @@ etlplus --version
|
|
|
151
152
|
|
|
152
153
|
# One-liner: extract CSV, filter, select, and write JSON
|
|
153
154
|
etlplus extract file examples/data/sample.csv \
|
|
154
|
-
| etlplus transform
|
|
155
|
-
-
|
|
155
|
+
| etlplus transform --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
|
|
156
|
+
- temp/sample_output.json
|
|
156
157
|
```
|
|
157
158
|
|
|
158
159
|
[Python API](#python-api):
|
|
@@ -185,6 +186,27 @@ etlplus --version
|
|
|
185
186
|
The CLI is implemented with Typer (Click-based). There is no argparse compatibility layer, so rely
|
|
186
187
|
on the documented commands/flags and run `etlplus <command> --help` for current options.
|
|
187
188
|
|
|
189
|
+
**Example error messages:**
|
|
190
|
+
|
|
191
|
+
- If you omit a required argument: `Error: Missing required argument 'SOURCE'.`
|
|
192
|
+
- If you place an option before its argument: `Error: Option '--source-format' must follow the 'SOURCE' argument.`
|
|
193
|
+
|
|
194
|
+
#### Argument Order and Required Options
|
|
195
|
+
|
|
196
|
+
For each command, positional arguments must precede options. Required options must follow their
|
|
197
|
+
associated argument:
|
|
198
|
+
|
|
199
|
+
- **extract**: `etlplus extract SOURCE [--source-format ...] [--source-type ...]`
|
|
200
|
+
- `SOURCE` is required. `--source-format` and `--source-type` must follow `SOURCE`.
|
|
201
|
+
- **transform**: `etlplus transform [--operations ...] SOURCE [--source-format ...] [--source-type ...] TARGET [--target-format ...] [--target-type ...]`
|
|
202
|
+
- `SOURCE` and `TARGET` are required. Format/type options must follow their respective argument.
|
|
203
|
+
- **load**: `etlplus load TARGET [--target-format ...] [--target-type ...] [--source-format ...]`
|
|
204
|
+
- `TARGET` is required. `--target-format` and `--target-type` must follow `TARGET`.
|
|
205
|
+
- **validate**: `etlplus validate SOURCE [--rules ...] [--source-format ...] [--source-type ...]`
|
|
206
|
+
- `SOURCE` is required. `--rules` and format/type options must follow `SOURCE`.
|
|
207
|
+
|
|
208
|
+
If required arguments or options are missing, or if options are placed before their associated argument, the CLI will display a clear error message.
|
|
209
|
+
|
|
188
210
|
#### Check Pipelines
|
|
189
211
|
|
|
190
212
|
Use `etlplus check` to explore pipeline YAML definitions without running them. The command can print
|
|
@@ -251,7 +273,7 @@ etlplus extract api https://api.example.com/data
|
|
|
251
273
|
|
|
252
274
|
Save extracted data to file:
|
|
253
275
|
```bash
|
|
254
|
-
etlplus extract file examples/data/sample.csv
|
|
276
|
+
etlplus extract file examples/data/sample.csv > temp/sample_output.json
|
|
255
277
|
```
|
|
256
278
|
|
|
257
279
|
#### Validate Data
|
|
@@ -270,59 +292,67 @@ etlplus validate examples/data/sample.json --rules '{"email": {"type": "string",
|
|
|
270
292
|
|
|
271
293
|
When piping data through `etlplus transform`, use `--source-format` whenever the SOURCE argument is
|
|
272
294
|
`-` or a literal payload, mirroring the `etlplus extract` semantics. Use `--target-format` to
|
|
273
|
-
control the emitted format for
|
|
274
|
-
paths continue to infer formats from their extensions. Use `--
|
|
275
|
-
connector type and `--
|
|
276
|
-
extract`/`etlplus load` behavior.
|
|
295
|
+
control the emitted format for STDOUT or other non-file outputs, just like `etlplus load`. File
|
|
296
|
+
paths continue to infer formats from their extensions. Use `--source-type` to override the inferred
|
|
297
|
+
source connector type and `--target-type` to override the inferred target connector type, matching
|
|
298
|
+
the `etlplus extract`/`etlplus load` behavior.
|
|
277
299
|
|
|
278
300
|
Transform file inputs while overriding connector types:
|
|
279
301
|
```bash
|
|
280
|
-
etlplus transform
|
|
302
|
+
etlplus transform \
|
|
281
303
|
--operations '{"select": ["name", "email"]}' \
|
|
282
|
-
--
|
|
304
|
+
examples/data/sample.json --source-type file \
|
|
305
|
+
temp/selected_output.json --target-type file
|
|
283
306
|
```
|
|
284
307
|
|
|
285
308
|
Filter and select fields:
|
|
286
309
|
```bash
|
|
287
|
-
etlplus transform
|
|
288
|
-
--operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}'
|
|
310
|
+
etlplus transform \
|
|
311
|
+
--operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}' \
|
|
312
|
+
'[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]'
|
|
289
313
|
```
|
|
290
314
|
|
|
291
315
|
Sort data:
|
|
292
316
|
```bash
|
|
293
|
-
etlplus transform
|
|
317
|
+
etlplus transform \
|
|
318
|
+
--operations '{"sort": {"field": "age", "reverse": true}}' \
|
|
319
|
+
examples/data/sample.json
|
|
294
320
|
```
|
|
295
321
|
|
|
296
322
|
Aggregate data:
|
|
297
323
|
```bash
|
|
298
|
-
etlplus transform
|
|
324
|
+
etlplus transform \
|
|
325
|
+
--operations '{"aggregate": {"field": "age", "func": "sum"}}' \
|
|
326
|
+
examples/data/sample.json
|
|
299
327
|
```
|
|
300
328
|
|
|
301
329
|
Map/rename fields:
|
|
302
330
|
```bash
|
|
303
|
-
etlplus transform
|
|
331
|
+
etlplus transform \
|
|
332
|
+
--operations '{"map": {"name": "new_name"}}' \
|
|
333
|
+
examples/data/sample.json
|
|
304
334
|
```
|
|
305
335
|
|
|
306
336
|
#### Load Data
|
|
307
337
|
|
|
308
|
-
`etlplus load` consumes JSON from
|
|
338
|
+
`etlplus load` consumes JSON from STDIN; provide only the target argument plus optional flags.
|
|
309
339
|
|
|
310
340
|
Load to JSON file:
|
|
311
341
|
```bash
|
|
312
342
|
etlplus extract file examples/data/sample.json \
|
|
313
|
-
| etlplus load
|
|
343
|
+
| etlplus load temp/sample_output.json --target-type file
|
|
314
344
|
```
|
|
315
345
|
|
|
316
346
|
Load to CSV file:
|
|
317
347
|
```bash
|
|
318
348
|
etlplus extract file examples/data/sample.csv \
|
|
319
|
-
| etlplus load
|
|
349
|
+
| etlplus load temp/sample_output.csv --target-type file
|
|
320
350
|
```
|
|
321
351
|
|
|
322
352
|
Load to REST API:
|
|
323
353
|
```bash
|
|
324
354
|
cat examples/data/sample.json \
|
|
325
|
-
| etlplus load
|
|
355
|
+
| etlplus load https://api.example.com/endpoint --target-type api
|
|
326
356
|
```
|
|
327
357
|
|
|
328
358
|
### Python API
|
|
@@ -375,20 +405,22 @@ etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
|
|
|
375
405
|
|
|
376
406
|
```bash
|
|
377
407
|
# 1. Extract from CSV
|
|
378
|
-
etlplus extract file examples/data/sample.csv
|
|
408
|
+
etlplus extract file examples/data/sample.csv > temp/sample_extracted.json
|
|
379
409
|
|
|
380
410
|
# 2. Transform (filter and select fields)
|
|
381
|
-
etlplus transform
|
|
411
|
+
etlplus transform \
|
|
382
412
|
--operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
|
|
383
|
-
|
|
413
|
+
temp/sample_extracted.json \
|
|
414
|
+
temp/sample_transformed.json
|
|
384
415
|
|
|
385
416
|
# 3. Validate transformed data
|
|
386
|
-
etlplus validate
|
|
387
|
-
--rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}'
|
|
417
|
+
etlplus validate \
|
|
418
|
+
--rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}' \
|
|
419
|
+
temo/sample_transformed.json
|
|
388
420
|
|
|
389
421
|
# 4. Load to CSV
|
|
390
422
|
cat temp/sample_transformed.json \
|
|
391
|
-
| etlplus load
|
|
423
|
+
| etlplus load temp/sample_output.csv
|
|
392
424
|
```
|
|
393
425
|
|
|
394
426
|
### Format Overrides
|
|
@@ -401,14 +433,14 @@ Examples (zsh):
|
|
|
401
433
|
|
|
402
434
|
```zsh
|
|
403
435
|
# Force CSV parsing for an extension-less file
|
|
404
|
-
etlplus extract
|
|
436
|
+
etlplus extract data.txt --source-type file --source-format csv
|
|
405
437
|
|
|
406
438
|
# Write CSV to a file without the .csv suffix
|
|
407
|
-
etlplus load
|
|
439
|
+
etlplus load output.bin --target-type file --target-format csv < data.json
|
|
408
440
|
|
|
409
441
|
# Leave the flags off when extensions already match the desired format
|
|
410
|
-
etlplus extract --
|
|
411
|
-
etlplus load
|
|
442
|
+
etlplus extract data.csv --source-type file
|
|
443
|
+
etlplus load data.json --target-type file < data.json
|
|
412
444
|
```
|
|
413
445
|
|
|
414
446
|
## Transformation Operations
|
|
@@ -19,6 +19,7 @@ package and command-line interface for data extraction, validation, transformati
|
|
|
19
19
|
- [Quickstart](#quickstart)
|
|
20
20
|
- [Usage](#usage)
|
|
21
21
|
- [Command Line Interface](#command-line-interface)
|
|
22
|
+
- [Argument Order and Required Options](#argument-order-and-required-options)
|
|
22
23
|
- [Check Pipelines](#check-pipelines)
|
|
23
24
|
- [Render SQL DDL](#render-sql-ddl)
|
|
24
25
|
- [Extract Data](#extract-data)
|
|
@@ -106,8 +107,8 @@ etlplus --version
|
|
|
106
107
|
|
|
107
108
|
# One-liner: extract CSV, filter, select, and write JSON
|
|
108
109
|
etlplus extract file examples/data/sample.csv \
|
|
109
|
-
| etlplus transform
|
|
110
|
-
-
|
|
110
|
+
| etlplus transform --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
|
|
111
|
+
- temp/sample_output.json
|
|
111
112
|
```
|
|
112
113
|
|
|
113
114
|
[Python API](#python-api):
|
|
@@ -140,6 +141,27 @@ etlplus --version
|
|
|
140
141
|
The CLI is implemented with Typer (Click-based). There is no argparse compatibility layer, so rely
|
|
141
142
|
on the documented commands/flags and run `etlplus <command> --help` for current options.
|
|
142
143
|
|
|
144
|
+
**Example error messages:**
|
|
145
|
+
|
|
146
|
+
- If you omit a required argument: `Error: Missing required argument 'SOURCE'.`
|
|
147
|
+
- If you place an option before its argument: `Error: Option '--source-format' must follow the 'SOURCE' argument.`
|
|
148
|
+
|
|
149
|
+
#### Argument Order and Required Options
|
|
150
|
+
|
|
151
|
+
For each command, positional arguments must precede options. Required options must follow their
|
|
152
|
+
associated argument:
|
|
153
|
+
|
|
154
|
+
- **extract**: `etlplus extract SOURCE [--source-format ...] [--source-type ...]`
|
|
155
|
+
- `SOURCE` is required. `--source-format` and `--source-type` must follow `SOURCE`.
|
|
156
|
+
- **transform**: `etlplus transform [--operations ...] SOURCE [--source-format ...] [--source-type ...] TARGET [--target-format ...] [--target-type ...]`
|
|
157
|
+
- `SOURCE` and `TARGET` are required. Format/type options must follow their respective argument.
|
|
158
|
+
- **load**: `etlplus load TARGET [--target-format ...] [--target-type ...] [--source-format ...]`
|
|
159
|
+
- `TARGET` is required. `--target-format` and `--target-type` must follow `TARGET`.
|
|
160
|
+
- **validate**: `etlplus validate SOURCE [--rules ...] [--source-format ...] [--source-type ...]`
|
|
161
|
+
- `SOURCE` is required. `--rules` and format/type options must follow `SOURCE`.
|
|
162
|
+
|
|
163
|
+
If required arguments or options are missing, or if options are placed before their associated argument, the CLI will display a clear error message.
|
|
164
|
+
|
|
143
165
|
#### Check Pipelines
|
|
144
166
|
|
|
145
167
|
Use `etlplus check` to explore pipeline YAML definitions without running them. The command can print
|
|
@@ -206,7 +228,7 @@ etlplus extract api https://api.example.com/data
|
|
|
206
228
|
|
|
207
229
|
Save extracted data to file:
|
|
208
230
|
```bash
|
|
209
|
-
etlplus extract file examples/data/sample.csv
|
|
231
|
+
etlplus extract file examples/data/sample.csv > temp/sample_output.json
|
|
210
232
|
```
|
|
211
233
|
|
|
212
234
|
#### Validate Data
|
|
@@ -225,59 +247,67 @@ etlplus validate examples/data/sample.json --rules '{"email": {"type": "string",
|
|
|
225
247
|
|
|
226
248
|
When piping data through `etlplus transform`, use `--source-format` whenever the SOURCE argument is
|
|
227
249
|
`-` or a literal payload, mirroring the `etlplus extract` semantics. Use `--target-format` to
|
|
228
|
-
control the emitted format for
|
|
229
|
-
paths continue to infer formats from their extensions. Use `--
|
|
230
|
-
connector type and `--
|
|
231
|
-
extract`/`etlplus load` behavior.
|
|
250
|
+
control the emitted format for STDOUT or other non-file outputs, just like `etlplus load`. File
|
|
251
|
+
paths continue to infer formats from their extensions. Use `--source-type` to override the inferred
|
|
252
|
+
source connector type and `--target-type` to override the inferred target connector type, matching
|
|
253
|
+
the `etlplus extract`/`etlplus load` behavior.
|
|
232
254
|
|
|
233
255
|
Transform file inputs while overriding connector types:
|
|
234
256
|
```bash
|
|
235
|
-
etlplus transform
|
|
257
|
+
etlplus transform \
|
|
236
258
|
--operations '{"select": ["name", "email"]}' \
|
|
237
|
-
--
|
|
259
|
+
examples/data/sample.json --source-type file \
|
|
260
|
+
temp/selected_output.json --target-type file
|
|
238
261
|
```
|
|
239
262
|
|
|
240
263
|
Filter and select fields:
|
|
241
264
|
```bash
|
|
242
|
-
etlplus transform
|
|
243
|
-
--operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}'
|
|
265
|
+
etlplus transform \
|
|
266
|
+
--operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}' \
|
|
267
|
+
'[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]'
|
|
244
268
|
```
|
|
245
269
|
|
|
246
270
|
Sort data:
|
|
247
271
|
```bash
|
|
248
|
-
etlplus transform
|
|
272
|
+
etlplus transform \
|
|
273
|
+
--operations '{"sort": {"field": "age", "reverse": true}}' \
|
|
274
|
+
examples/data/sample.json
|
|
249
275
|
```
|
|
250
276
|
|
|
251
277
|
Aggregate data:
|
|
252
278
|
```bash
|
|
253
|
-
etlplus transform
|
|
279
|
+
etlplus transform \
|
|
280
|
+
--operations '{"aggregate": {"field": "age", "func": "sum"}}' \
|
|
281
|
+
examples/data/sample.json
|
|
254
282
|
```
|
|
255
283
|
|
|
256
284
|
Map/rename fields:
|
|
257
285
|
```bash
|
|
258
|
-
etlplus transform
|
|
286
|
+
etlplus transform \
|
|
287
|
+
--operations '{"map": {"name": "new_name"}}' \
|
|
288
|
+
examples/data/sample.json
|
|
259
289
|
```
|
|
260
290
|
|
|
261
291
|
#### Load Data
|
|
262
292
|
|
|
263
|
-
`etlplus load` consumes JSON from
|
|
293
|
+
`etlplus load` consumes JSON from STDIN; provide only the target argument plus optional flags.
|
|
264
294
|
|
|
265
295
|
Load to JSON file:
|
|
266
296
|
```bash
|
|
267
297
|
etlplus extract file examples/data/sample.json \
|
|
268
|
-
| etlplus load
|
|
298
|
+
| etlplus load temp/sample_output.json --target-type file
|
|
269
299
|
```
|
|
270
300
|
|
|
271
301
|
Load to CSV file:
|
|
272
302
|
```bash
|
|
273
303
|
etlplus extract file examples/data/sample.csv \
|
|
274
|
-
| etlplus load
|
|
304
|
+
| etlplus load temp/sample_output.csv --target-type file
|
|
275
305
|
```
|
|
276
306
|
|
|
277
307
|
Load to REST API:
|
|
278
308
|
```bash
|
|
279
309
|
cat examples/data/sample.json \
|
|
280
|
-
| etlplus load
|
|
310
|
+
| etlplus load https://api.example.com/endpoint --target-type api
|
|
281
311
|
```
|
|
282
312
|
|
|
283
313
|
### Python API
|
|
@@ -330,20 +360,22 @@ etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
|
|
|
330
360
|
|
|
331
361
|
```bash
|
|
332
362
|
# 1. Extract from CSV
|
|
333
|
-
etlplus extract file examples/data/sample.csv
|
|
363
|
+
etlplus extract file examples/data/sample.csv > temp/sample_extracted.json
|
|
334
364
|
|
|
335
365
|
# 2. Transform (filter and select fields)
|
|
336
|
-
etlplus transform
|
|
366
|
+
etlplus transform \
|
|
337
367
|
--operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
|
|
338
|
-
|
|
368
|
+
temp/sample_extracted.json \
|
|
369
|
+
temp/sample_transformed.json
|
|
339
370
|
|
|
340
371
|
# 3. Validate transformed data
|
|
341
|
-
etlplus validate
|
|
342
|
-
--rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}'
|
|
372
|
+
etlplus validate \
|
|
373
|
+
--rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}' \
|
|
374
|
+
temo/sample_transformed.json
|
|
343
375
|
|
|
344
376
|
# 4. Load to CSV
|
|
345
377
|
cat temp/sample_transformed.json \
|
|
346
|
-
| etlplus load
|
|
378
|
+
| etlplus load temp/sample_output.csv
|
|
347
379
|
```
|
|
348
380
|
|
|
349
381
|
### Format Overrides
|
|
@@ -356,14 +388,14 @@ Examples (zsh):
|
|
|
356
388
|
|
|
357
389
|
```zsh
|
|
358
390
|
# Force CSV parsing for an extension-less file
|
|
359
|
-
etlplus extract
|
|
391
|
+
etlplus extract data.txt --source-type file --source-format csv
|
|
360
392
|
|
|
361
393
|
# Write CSV to a file without the .csv suffix
|
|
362
|
-
etlplus load
|
|
394
|
+
etlplus load output.bin --target-type file --target-format csv < data.json
|
|
363
395
|
|
|
364
396
|
# Leave the flags off when extensions already match the desired format
|
|
365
|
-
etlplus extract --
|
|
366
|
-
etlplus load
|
|
397
|
+
etlplus extract data.csv --source-type file
|
|
398
|
+
etlplus load data.json --target-type file < data.json
|
|
367
399
|
```
|
|
368
400
|
|
|
369
401
|
## Transformation Operations
|