floe-python 0.4.0__tar.gz → 0.4.1__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.
- {floe_python-0.4.0 → floe_python-0.4.1}/Cargo.lock +3 -3
- {floe_python-0.4.0 → floe_python-0.4.1}/PKG-INFO +1 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/Cargo.toml +2 -2
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/mod.rs +4 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/parse.rs +2 -2
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/storage.rs +1 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/validate.rs +7 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg.rs +35 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/lib.rs +26 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/profile/parse.rs +14 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/profile/types.rs +3 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/profile/validate.rs +102 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/context.rs +14 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/mod.rs +8 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/config_validation.rs +32 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/Cargo.toml +2 -2
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/functions.rs +5 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/pyproject.toml +1 -1
- {floe_python-0.4.0 → floe_python-0.4.1}/Cargo.toml +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/README.md +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/README.md +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/add_entity.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/checks/cast.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/checks/mismatch.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/checks/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/checks/normalize.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/checks/not_null.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/checks/unique.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/catalog.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/location.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/template.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/types.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/config/yaml_decode.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/errors.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/format.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/avro.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/csv.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/fixed_width.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/json.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/json_selector.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/orc.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/parquet.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/xlsx.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/xml.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/read/xml_selector.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/extensions.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/paths.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/placement.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/planner.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/uri.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/core/validation.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/object_store.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/ops/archive.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/ops/inputs.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/ops/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/ops/output.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/providers/adls.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/providers/gcs.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/providers/local.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/providers/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/providers/s3.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/storage/target.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/unique_seed/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/accepted.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/arrow_convert.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/csv.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/delta/commit_metrics.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/delta/options.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/delta/record_batch.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/delta/unity.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/delta/unity_tests.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/delta.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg/context.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg/data_files.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg/glue.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg/metadata.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg/rest.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/iceberg/schema.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/metrics.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/parquet.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/parts.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/sink_format.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/append.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/merge/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/merge/scd1.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/merge/scd2.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/merge/shared.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/io/write/strategy/overwrite.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/lineage/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/log.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/manifest/builder.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/manifest/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/manifest/model.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/profile/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/report/build.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/report/entity.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/report/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/report/output.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/accepted_write.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/incremental.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/pii.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/precheck.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/process.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/resolve.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/entity/validate_split.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/events.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/file.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/output.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/run/perf.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/runner/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/runner/outcome.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/runtime.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/state/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/vars/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/vars/resolve.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/src/warnings.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/archive_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/composite_unique.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/delta_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/dry_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/fixed_width.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/iceberg_gcs_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/iceberg_glue_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/iceberg_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/iceberg_s3_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/json_selectors.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/local_run.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/path_normalization.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration/run_entities_filter.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/integration.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/common.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/add_entity.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/adls_storage.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/adls_validation.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/catalogs.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/gcs_storage.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/gcs_validation.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/lineage_validation.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/local_storage.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/parse.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/pii_validation.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/remote_base.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/templating.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/format.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/avro_input.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/csv_nulls.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/json_array.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/json_ndjson.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/json_selector.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/orc_input.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/parquet_input.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/tsv.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/xlsx_input.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/xml.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/read/xml_selector.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/adls.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/adls_integration.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/gcs.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/inputs.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/local.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/paths.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/planner.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/s3.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/storage/target.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/delta_merge.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/delta_write.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/iceberg_write.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/metrics.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/object_store.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/parquet_write.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/parts.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/io/write/rejected_csv.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/manifest/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/profile/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/profile/parse.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/profile/validate.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/report/accepted_output.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/report/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/report/storage.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/check_order.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/checks.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/entity/accepted_output.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/entity/incremental.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/entity/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/lineage.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/normalize.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/pii.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/report.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/run/schema_mismatch.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/runner/adapter.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/runner/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/state/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/vars/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/vars/resolve.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/.gitignore +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/README.md +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/lib.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/observer.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/types/config.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/types/errors.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/types/mod.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/src/types/outcome.rs +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/tests/fixtures/config.yml +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/tests/fixtures/in/customer/customers_valid.csv +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/tests/fixtures/invalid_config.yml +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/tests/fixtures/profile.yml +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-python/tests/test_floe.py +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/python/floe/__init__.py +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/python/floe/_floe.pyi +0 -0
- {floe_python-0.4.0 → floe_python-0.4.1}/python/floe/py.typed +0 -0
|
@@ -3399,7 +3399,7 @@ dependencies = [
|
|
|
3399
3399
|
|
|
3400
3400
|
[[package]]
|
|
3401
3401
|
name = "floe-cli"
|
|
3402
|
-
version = "0.4.
|
|
3402
|
+
version = "0.4.1"
|
|
3403
3403
|
dependencies = [
|
|
3404
3404
|
"assert_cmd",
|
|
3405
3405
|
"clap",
|
|
@@ -3412,7 +3412,7 @@ dependencies = [
|
|
|
3412
3412
|
|
|
3413
3413
|
[[package]]
|
|
3414
3414
|
name = "floe-core"
|
|
3415
|
-
version = "0.4.
|
|
3415
|
+
version = "0.4.1"
|
|
3416
3416
|
dependencies = [
|
|
3417
3417
|
"apache-avro 0.16.0",
|
|
3418
3418
|
"arrow",
|
|
@@ -3455,7 +3455,7 @@ dependencies = [
|
|
|
3455
3455
|
|
|
3456
3456
|
[[package]]
|
|
3457
3457
|
name = "floe-python"
|
|
3458
|
-
version = "0.4.
|
|
3458
|
+
version = "0.4.1"
|
|
3459
3459
|
dependencies = [
|
|
3460
3460
|
"floe-core",
|
|
3461
3461
|
"pyo3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "floe-core"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Core library for Floe, a YAML-driven technical ingestion tool."
|
|
6
6
|
license = "MIT"
|
|
@@ -41,7 +41,7 @@ uuid = "1"
|
|
|
41
41
|
arrow = "57"
|
|
42
42
|
iceberg = "0.9.0"
|
|
43
43
|
iceberg-catalog-rest = "0.9.0"
|
|
44
|
-
iceberg-storage-opendal = { version = "0.9.1", features = ["opendal-gcs"] }
|
|
44
|
+
iceberg-storage-opendal = { version = "0.9.1", features = ["opendal-s3", "opendal-gcs"] }
|
|
45
45
|
df-interchange = { version = "0.3.2", features = ["arrow_57", "polars_0_52"] }
|
|
46
46
|
orc-rust = "0.7.1"
|
|
47
47
|
|
|
@@ -13,6 +13,9 @@ pub use storage::{resolve_local_path, ConfigBase, ResolvedPath, StorageResolver}
|
|
|
13
13
|
pub use types::*;
|
|
14
14
|
|
|
15
15
|
pub use parse::extract_raw_env_vars;
|
|
16
|
-
pub(crate) use parse::{
|
|
16
|
+
pub(crate) use parse::{
|
|
17
|
+
parse_catalogs_with_context, parse_config, parse_config_with_vars, parse_lineage_config,
|
|
18
|
+
parse_storages,
|
|
19
|
+
};
|
|
17
20
|
pub(crate) use template::apply_templates_with_vars;
|
|
18
21
|
pub(crate) use validate::{extract_first_n, extract_last_n, validate_config};
|
|
@@ -658,7 +658,7 @@ fn parse_sink_delta_options(value: &Yaml, ctx: &str) -> FloeResult<DeltaSinkTarg
|
|
|
658
658
|
})
|
|
659
659
|
}
|
|
660
660
|
|
|
661
|
-
fn parse_storages(value: &Yaml) -> FloeResult<StoragesConfig> {
|
|
661
|
+
pub(crate) fn parse_storages(value: &Yaml) -> FloeResult<StoragesConfig> {
|
|
662
662
|
let hash = yaml_hash(value, "storages")?;
|
|
663
663
|
validate_known_keys(hash, "storages", &["default", "definitions"])?;
|
|
664
664
|
let definitions_yaml = match hash_get(hash, "definitions") {
|
|
@@ -1129,7 +1129,7 @@ fn parse_pii_column(value: &Yaml) -> FloeResult<PiiColumnConfig> {
|
|
|
1129
1129
|
})
|
|
1130
1130
|
}
|
|
1131
1131
|
|
|
1132
|
-
fn parse_lineage_config(value: &Yaml) -> FloeResult<LineageConfig> {
|
|
1132
|
+
pub(crate) fn parse_lineage_config(value: &Yaml) -> FloeResult<LineageConfig> {
|
|
1133
1133
|
let hash = yaml_hash(value, "lineage")?;
|
|
1134
1134
|
validate_known_keys(
|
|
1135
1135
|
hash,
|
|
@@ -615,7 +615,7 @@ fn parent_prefix(key: &str) -> String {
|
|
|
615
615
|
}
|
|
616
616
|
}
|
|
617
617
|
|
|
618
|
-
fn is_remote_uri(value: &str) -> bool {
|
|
618
|
+
pub(crate) fn is_remote_uri(value: &str) -> bool {
|
|
619
619
|
value.starts_with("s3://") || value.starts_with("gs://") || value.starts_with("abfs://")
|
|
620
620
|
}
|
|
621
621
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
use std::collections::HashSet;
|
|
2
2
|
|
|
3
|
+
use crate::config::storage::is_remote_uri;
|
|
3
4
|
use crate::config::{
|
|
4
5
|
CatalogDefinition, CatalogTypeConfig, EntityConfig, IncrementalMode, PolicySeverity,
|
|
5
6
|
RootConfig, SourceOptions, StorageDefinition,
|
|
@@ -128,6 +129,12 @@ fn validate_report(
|
|
|
128
129
|
) -> FloeResult<()> {
|
|
129
130
|
let storage_name = storages.resolve_report_name(report.storage.as_deref())?;
|
|
130
131
|
storages.validate_report_reference("report.storage", &storage_name)?;
|
|
132
|
+
if storages.definition_type(&storage_name) == Some("local") && is_remote_uri(&report.path) {
|
|
133
|
+
return Err(Box::new(ConfigError(format!(
|
|
134
|
+
"report.path must be a local path (got {})",
|
|
135
|
+
report.path
|
|
136
|
+
))));
|
|
137
|
+
}
|
|
131
138
|
Ok(())
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -8,6 +8,7 @@ use iceberg::memory::{MemoryCatalogBuilder, MEMORY_CATALOG_WAREHOUSE};
|
|
|
8
8
|
use iceberg::spec::{Schema, UnboundPartitionSpec};
|
|
9
9
|
use iceberg::transaction::{ApplyTransactionAction, Transaction};
|
|
10
10
|
use iceberg::{Catalog, CatalogBuilder, NamespaceIdent, TableIdent};
|
|
11
|
+
use iceberg_storage_opendal::OpenDalStorageFactory;
|
|
11
12
|
use polars::prelude::DataFrame;
|
|
12
13
|
|
|
13
14
|
use crate::errors::RunError;
|
|
@@ -375,6 +376,7 @@ async fn write_iceberg_table_async(
|
|
|
375
376
|
catalog_props.insert(MEMORY_CATALOG_WAREHOUSE.to_string(), table_root_uri.clone());
|
|
376
377
|
|
|
377
378
|
let is_local = !table_root_uri.starts_with("s3://")
|
|
379
|
+
&& !table_root_uri.starts_with("s3a://")
|
|
378
380
|
&& !table_root_uri.starts_with("gs://")
|
|
379
381
|
&& !table_root_uri.starts_with("az://")
|
|
380
382
|
&& !table_root_uri.starts_with("abfss://");
|
|
@@ -382,6 +384,20 @@ async fn write_iceberg_table_async(
|
|
|
382
384
|
if is_local {
|
|
383
385
|
catalog_builder =
|
|
384
386
|
catalog_builder.with_storage_factory(std::sync::Arc::new(LocalFsStorageFactory));
|
|
387
|
+
} else if table_root_uri.starts_with("s3://") || table_root_uri.starts_with("s3a://") {
|
|
388
|
+
let scheme = table_root_uri
|
|
389
|
+
.split("://")
|
|
390
|
+
.next()
|
|
391
|
+
.unwrap_or("s3")
|
|
392
|
+
.to_string();
|
|
393
|
+
catalog_builder =
|
|
394
|
+
catalog_builder.with_storage_factory(std::sync::Arc::new(OpenDalStorageFactory::S3 {
|
|
395
|
+
configured_scheme: scheme,
|
|
396
|
+
customized_credential_load: None,
|
|
397
|
+
}));
|
|
398
|
+
} else if table_root_uri.starts_with("gs://") {
|
|
399
|
+
catalog_builder =
|
|
400
|
+
catalog_builder.with_storage_factory(std::sync::Arc::new(OpenDalStorageFactory::Gcs));
|
|
385
401
|
}
|
|
386
402
|
let catalog = catalog_builder
|
|
387
403
|
.load(catalog_name, catalog_props)
|
|
@@ -698,17 +714,35 @@ async fn collect_iceberg_batches(
|
|
|
698
714
|
use iceberg::{Catalog, CatalogBuilder, NamespaceIdent, TableIdent};
|
|
699
715
|
|
|
700
716
|
let is_local = !warehouse_location.starts_with("s3://")
|
|
717
|
+
&& !warehouse_location.starts_with("s3a://")
|
|
701
718
|
&& !warehouse_location.starts_with("gs://")
|
|
702
719
|
&& !warehouse_location.starts_with("az://")
|
|
703
720
|
&& !warehouse_location.starts_with("abfss://");
|
|
704
721
|
|
|
705
722
|
let mut props = catalog_props;
|
|
706
|
-
props.insert(
|
|
723
|
+
props.insert(
|
|
724
|
+
MEMORY_CATALOG_WAREHOUSE.to_string(),
|
|
725
|
+
warehouse_location.clone(),
|
|
726
|
+
);
|
|
707
727
|
|
|
708
728
|
let mut catalog_builder = MemoryCatalogBuilder::default();
|
|
709
729
|
if is_local {
|
|
710
730
|
catalog_builder =
|
|
711
731
|
catalog_builder.with_storage_factory(std::sync::Arc::new(LocalFsStorageFactory));
|
|
732
|
+
} else if warehouse_location.starts_with("s3://") || warehouse_location.starts_with("s3a://") {
|
|
733
|
+
let scheme = warehouse_location
|
|
734
|
+
.split("://")
|
|
735
|
+
.next()
|
|
736
|
+
.unwrap_or("s3")
|
|
737
|
+
.to_string();
|
|
738
|
+
catalog_builder =
|
|
739
|
+
catalog_builder.with_storage_factory(std::sync::Arc::new(OpenDalStorageFactory::S3 {
|
|
740
|
+
configured_scheme: scheme,
|
|
741
|
+
customized_credential_load: None,
|
|
742
|
+
}));
|
|
743
|
+
} else if warehouse_location.starts_with("gs://") {
|
|
744
|
+
catalog_builder =
|
|
745
|
+
catalog_builder.with_storage_factory(std::sync::Arc::new(OpenDalStorageFactory::Gcs));
|
|
712
746
|
}
|
|
713
747
|
let catalog = catalog_builder.load(ICEBERG_CATALOG_NAME, props).await?;
|
|
714
748
|
|
|
@@ -40,6 +40,8 @@ pub struct ValidateOptions {
|
|
|
40
40
|
pub entities: Vec<String>,
|
|
41
41
|
pub profile_vars: std::collections::HashMap<String, String>,
|
|
42
42
|
pub profile_catalogs: Option<config::CatalogsConfig>,
|
|
43
|
+
pub profile_storages: Option<config::StoragesConfig>,
|
|
44
|
+
pub profile_lineage: Option<config::LineageConfig>,
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
#[derive(Debug, Default)]
|
|
@@ -62,6 +64,8 @@ pub fn validate_with_base(
|
|
|
62
64
|
) -> FloeResult<()> {
|
|
63
65
|
let mut config = config::parse_config_with_vars(config_path, &options.profile_vars)?;
|
|
64
66
|
apply_profile_catalogs(&mut config, options.profile_catalogs.as_ref());
|
|
67
|
+
apply_profile_storages(&mut config, options.profile_storages.as_ref());
|
|
68
|
+
apply_profile_lineage(&mut config, options.profile_lineage.as_ref());
|
|
65
69
|
config::validate_config(&config)?;
|
|
66
70
|
|
|
67
71
|
if !options.entities.is_empty() {
|
|
@@ -86,9 +90,13 @@ pub fn load_config_with_profile_overrides(
|
|
|
86
90
|
config_path: &Path,
|
|
87
91
|
profile_vars: &std::collections::HashMap<String, String>,
|
|
88
92
|
profile_catalogs: Option<&config::CatalogsConfig>,
|
|
93
|
+
profile_storages: Option<&config::StoragesConfig>,
|
|
94
|
+
profile_lineage: Option<&config::LineageConfig>,
|
|
89
95
|
) -> FloeResult<config::RootConfig> {
|
|
90
96
|
let mut config = config::parse_config_with_vars(config_path, profile_vars)?;
|
|
91
97
|
apply_profile_catalogs(&mut config, profile_catalogs);
|
|
98
|
+
apply_profile_storages(&mut config, profile_storages);
|
|
99
|
+
apply_profile_lineage(&mut config, profile_lineage);
|
|
92
100
|
Ok(config)
|
|
93
101
|
}
|
|
94
102
|
|
|
@@ -107,6 +115,24 @@ pub(crate) fn apply_profile_catalogs(
|
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
|
|
118
|
+
pub(crate) fn apply_profile_storages(
|
|
119
|
+
config: &mut config::RootConfig,
|
|
120
|
+
profile_storages: Option<&config::StoragesConfig>,
|
|
121
|
+
) {
|
|
122
|
+
if let Some(storages) = profile_storages {
|
|
123
|
+
config.storages = Some(storages.clone());
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
pub(crate) fn apply_profile_lineage(
|
|
128
|
+
config: &mut config::RootConfig,
|
|
129
|
+
profile_lineage: Option<&config::LineageConfig>,
|
|
130
|
+
) {
|
|
131
|
+
if let Some(lineage) = profile_lineage {
|
|
132
|
+
config.lineage = Some(lineage.clone());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
110
136
|
pub fn extract_config_env_vars(
|
|
111
137
|
config_path: &Path,
|
|
112
138
|
) -> FloeResult<std::collections::HashMap<String, String>> {
|
|
@@ -56,6 +56,8 @@ fn parse_profile_doc(doc: &Yaml) -> FloeResult<ProfileConfig> {
|
|
|
56
56
|
"execution",
|
|
57
57
|
"variables",
|
|
58
58
|
"catalogs",
|
|
59
|
+
"storages",
|
|
60
|
+
"lineage",
|
|
59
61
|
"validation",
|
|
60
62
|
],
|
|
61
63
|
)?;
|
|
@@ -98,6 +100,16 @@ fn parse_profile_doc(doc: &Yaml) -> FloeResult<ProfileConfig> {
|
|
|
98
100
|
None => None,
|
|
99
101
|
};
|
|
100
102
|
|
|
103
|
+
let storages = match hash_get(root, "storages") {
|
|
104
|
+
Some(value) => Some(crate::config::parse_storages(value)?),
|
|
105
|
+
None => None,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let lineage = match hash_get(root, "lineage") {
|
|
109
|
+
Some(value) => Some(crate::config::parse_lineage_config(value)?),
|
|
110
|
+
None => None,
|
|
111
|
+
};
|
|
112
|
+
|
|
101
113
|
let validation = match hash_get(root, "validation") {
|
|
102
114
|
Some(value) => Some(parse_validation(value)?),
|
|
103
115
|
None => None,
|
|
@@ -110,6 +122,8 @@ fn parse_profile_doc(doc: &Yaml) -> FloeResult<ProfileConfig> {
|
|
|
110
122
|
execution,
|
|
111
123
|
variables,
|
|
112
124
|
catalogs,
|
|
125
|
+
storages,
|
|
126
|
+
lineage,
|
|
113
127
|
validation,
|
|
114
128
|
})
|
|
115
129
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
|
-
use crate::config::CatalogsConfig;
|
|
3
|
+
use crate::config::{CatalogsConfig, LineageConfig, StoragesConfig};
|
|
4
4
|
|
|
5
5
|
/// Top-level profile document (apiVersion + kind + sections).
|
|
6
6
|
#[derive(Debug, Clone)]
|
|
@@ -11,6 +11,8 @@ pub struct ProfileConfig {
|
|
|
11
11
|
pub execution: Option<ProfileExecution>,
|
|
12
12
|
pub variables: HashMap<String, String>,
|
|
13
13
|
pub catalogs: Option<CatalogsConfig>,
|
|
14
|
+
pub storages: Option<StoragesConfig>,
|
|
15
|
+
pub lineage: Option<LineageConfig>,
|
|
14
16
|
pub validation: Option<ProfileValidation>,
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
use std::collections::{HashMap, HashSet};
|
|
2
2
|
|
|
3
|
+
use crate::config::{LineageConfig, StoragesConfig};
|
|
3
4
|
use crate::profile::types::{ProfileConfig, ProfileRunner};
|
|
4
5
|
use crate::{ConfigError, FloeResult};
|
|
5
6
|
|
|
@@ -25,6 +26,12 @@ pub fn validate_profile(profile: &ProfileConfig) -> FloeResult<()> {
|
|
|
25
26
|
|
|
26
27
|
validate_no_malformed_vars(&profile.variables)?;
|
|
27
28
|
validate_profile_catalogs(profile)?;
|
|
29
|
+
if let Some(storages) = &profile.storages {
|
|
30
|
+
validate_profile_storages(storages)?;
|
|
31
|
+
}
|
|
32
|
+
if let Some(lineage) = &profile.lineage {
|
|
33
|
+
validate_profile_lineage(lineage)?;
|
|
34
|
+
}
|
|
28
35
|
|
|
29
36
|
Ok(())
|
|
30
37
|
}
|
|
@@ -66,6 +73,101 @@ fn validate_profile_catalogs(profile: &ProfileConfig) -> FloeResult<()> {
|
|
|
66
73
|
Ok(())
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
fn validate_profile_storages(storages: &StoragesConfig) -> FloeResult<()> {
|
|
77
|
+
if storages.definitions.is_empty() {
|
|
78
|
+
return Err(Box::new(ConfigError(
|
|
79
|
+
"profile.storages.definitions must not be empty".to_string(),
|
|
80
|
+
)));
|
|
81
|
+
}
|
|
82
|
+
const ALLOWED_STORAGE_TYPES: &[&str] = &["local", "s3", "adls", "gcs"];
|
|
83
|
+
let mut names = HashSet::new();
|
|
84
|
+
for definition in &storages.definitions {
|
|
85
|
+
if definition.name.trim().is_empty() {
|
|
86
|
+
return Err(Box::new(ConfigError(
|
|
87
|
+
"profile.storages.definitions.name must not be empty".to_string(),
|
|
88
|
+
)));
|
|
89
|
+
}
|
|
90
|
+
if !names.insert(definition.name.as_str()) {
|
|
91
|
+
return Err(Box::new(ConfigError(format!(
|
|
92
|
+
"profile.storages.definitions name={} is duplicated",
|
|
93
|
+
definition.name
|
|
94
|
+
))));
|
|
95
|
+
}
|
|
96
|
+
if !ALLOWED_STORAGE_TYPES.contains(&definition.fs_type.as_str()) {
|
|
97
|
+
return Err(Box::new(ConfigError(format!(
|
|
98
|
+
"profile.storages.definitions name={} type={} is unsupported (allowed: {})",
|
|
99
|
+
definition.name,
|
|
100
|
+
definition.fs_type,
|
|
101
|
+
ALLOWED_STORAGE_TYPES.join(", ")
|
|
102
|
+
))));
|
|
103
|
+
}
|
|
104
|
+
if definition.fs_type == "s3" {
|
|
105
|
+
if definition.bucket.is_none() {
|
|
106
|
+
return Err(Box::new(ConfigError(format!(
|
|
107
|
+
"profile.storages.definitions name={} requires bucket for type s3",
|
|
108
|
+
definition.name
|
|
109
|
+
))));
|
|
110
|
+
}
|
|
111
|
+
if definition.region.is_none() {
|
|
112
|
+
return Err(Box::new(ConfigError(format!(
|
|
113
|
+
"profile.storages.definitions name={} requires region for type s3",
|
|
114
|
+
definition.name
|
|
115
|
+
))));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if definition.fs_type == "adls" {
|
|
119
|
+
if definition.account.is_none() {
|
|
120
|
+
return Err(Box::new(ConfigError(format!(
|
|
121
|
+
"profile.storages.definitions name={} requires account for type adls",
|
|
122
|
+
definition.name
|
|
123
|
+
))));
|
|
124
|
+
}
|
|
125
|
+
if definition.container.is_none() {
|
|
126
|
+
return Err(Box::new(ConfigError(format!(
|
|
127
|
+
"profile.storages.definitions name={} requires container for type adls",
|
|
128
|
+
definition.name
|
|
129
|
+
))));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if definition.fs_type == "gcs" && definition.bucket.is_none() {
|
|
133
|
+
return Err(Box::new(ConfigError(format!(
|
|
134
|
+
"profile.storages.definitions name={} requires bucket for type gcs",
|
|
135
|
+
definition.name
|
|
136
|
+
))));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
let Some(default_name) = &storages.default else {
|
|
140
|
+
return Err(Box::new(ConfigError(
|
|
141
|
+
"profile.storages.default is required when storages is set".to_string(),
|
|
142
|
+
)));
|
|
143
|
+
};
|
|
144
|
+
if !names.contains(default_name.as_str()) {
|
|
145
|
+
return Err(Box::new(ConfigError(format!(
|
|
146
|
+
"profile.storages.default={default_name} does not match any definition"
|
|
147
|
+
))));
|
|
148
|
+
}
|
|
149
|
+
Ok(())
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn validate_profile_lineage(lineage: &LineageConfig) -> FloeResult<()> {
|
|
153
|
+
if lineage.url.trim().is_empty() {
|
|
154
|
+
return Err(Box::new(ConfigError(
|
|
155
|
+
"profile.lineage.url must not be empty".to_string(),
|
|
156
|
+
)));
|
|
157
|
+
}
|
|
158
|
+
if lineage.namespace.trim().is_empty() {
|
|
159
|
+
return Err(Box::new(ConfigError(
|
|
160
|
+
"profile.lineage.namespace must not be empty".to_string(),
|
|
161
|
+
)));
|
|
162
|
+
}
|
|
163
|
+
if lineage.max_failures == Some(0) {
|
|
164
|
+
return Err(Box::new(ConfigError(
|
|
165
|
+
"profile.lineage.max_failures must be at least 1".to_string(),
|
|
166
|
+
)));
|
|
167
|
+
}
|
|
168
|
+
Ok(())
|
|
169
|
+
}
|
|
170
|
+
|
|
69
171
|
/// Validate a variable map produced by merging sources in precedence order:
|
|
70
172
|
///
|
|
71
173
|
/// config variables > CLI overrides > profile variables
|
|
@@ -33,6 +33,20 @@ impl RunContext {
|
|
|
33
33
|
.as_ref()
|
|
34
34
|
.and_then(|profile| profile.catalogs.as_ref()),
|
|
35
35
|
);
|
|
36
|
+
crate::apply_profile_storages(
|
|
37
|
+
&mut config,
|
|
38
|
+
options
|
|
39
|
+
.profile
|
|
40
|
+
.as_ref()
|
|
41
|
+
.and_then(|profile| profile.storages.as_ref()),
|
|
42
|
+
);
|
|
43
|
+
crate::apply_profile_lineage(
|
|
44
|
+
&mut config,
|
|
45
|
+
options
|
|
46
|
+
.profile
|
|
47
|
+
.as_ref()
|
|
48
|
+
.and_then(|profile| profile.lineage.as_ref()),
|
|
49
|
+
);
|
|
36
50
|
let storage_resolver = config::StorageResolver::new(&config, config_base)?;
|
|
37
51
|
let catalog_resolver = config::CatalogResolver::new(&config)?;
|
|
38
52
|
let config_dir =
|
|
@@ -120,6 +120,14 @@ pub fn run_with_runtime(
|
|
|
120
120
|
.profile
|
|
121
121
|
.as_ref()
|
|
122
122
|
.and_then(|profile| profile.catalogs.clone()),
|
|
123
|
+
profile_storages: options
|
|
124
|
+
.profile
|
|
125
|
+
.as_ref()
|
|
126
|
+
.and_then(|profile| profile.storages.clone()),
|
|
127
|
+
profile_lineage: options
|
|
128
|
+
.profile
|
|
129
|
+
.as_ref()
|
|
130
|
+
.and_then(|profile| profile.lineage.clone()),
|
|
123
131
|
};
|
|
124
132
|
crate::validate_with_base(config_path, config_base.clone(), validate_options)?;
|
|
125
133
|
let context = RunContext::new(config_path, config_base, &options, profile_vars)?;
|
{floe_python-0.4.0 → floe_python-0.4.1}/crates/floe-core/tests/unit/config/config_validation.rs
RENAMED
|
@@ -109,6 +109,38 @@ entities:
|
|
|
109
109
|
assert_validation_ok(&yaml);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
#[test]
|
|
113
|
+
fn remote_report_path_rejected_when_storage_is_local() {
|
|
114
|
+
let yaml = format!(
|
|
115
|
+
r#"version: "0.1"
|
|
116
|
+
report:
|
|
117
|
+
path: "s3://floe-demo-bucket/reports"
|
|
118
|
+
entities:
|
|
119
|
+
{}"#,
|
|
120
|
+
base_entity("customer")
|
|
121
|
+
);
|
|
122
|
+
assert_validation_error(
|
|
123
|
+
&yaml,
|
|
124
|
+
&["report.path must be a local path (got s3://floe-demo-bucket/reports)"],
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
fn remote_report_path_rejected_for_gcs() {
|
|
130
|
+
let yaml = format!(
|
|
131
|
+
r#"version: "0.1"
|
|
132
|
+
report:
|
|
133
|
+
path: "gs://floe-demo-bucket/reports"
|
|
134
|
+
entities:
|
|
135
|
+
{}"#,
|
|
136
|
+
base_entity("customer")
|
|
137
|
+
);
|
|
138
|
+
assert_validation_error(
|
|
139
|
+
&yaml,
|
|
140
|
+
&["report.path must be a local path (got gs://floe-demo-bucket/reports)"],
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
112
144
|
#[test]
|
|
113
145
|
fn supported_config_versions_are_valid() {
|
|
114
146
|
for version in &["0.2", "0.3"] {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "floe-python"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Python bindings for floe-core (PyO3-based)"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -14,7 +14,7 @@ name = "_floe"
|
|
|
14
14
|
crate-type = ["cdylib"]
|
|
15
15
|
|
|
16
16
|
[dependencies]
|
|
17
|
-
floe-core = { path = "../floe-core", version = "0.4.
|
|
17
|
+
floe-core = { path = "../floe-core", version = "0.4.1" }
|
|
18
18
|
pyo3 = { version = "0.22", features = ["extension-module", "abi3-py310"] }
|
|
19
19
|
serde_json = "1"
|
|
20
20
|
|
|
@@ -38,6 +38,8 @@ fn load_optional_profile(
|
|
|
38
38
|
variables: vars,
|
|
39
39
|
validation: None,
|
|
40
40
|
catalogs: None,
|
|
41
|
+
storages: None,
|
|
42
|
+
lineage: None,
|
|
41
43
|
})),
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -59,7 +61,9 @@ pub fn validate(
|
|
|
59
61
|
.as_ref()
|
|
60
62
|
.map(|p| p.variables.clone())
|
|
61
63
|
.unwrap_or_default(),
|
|
62
|
-
profile_catalogs: profile.and_then(|p| p.catalogs),
|
|
64
|
+
profile_catalogs: profile.as_ref().and_then(|p| p.catalogs.clone()),
|
|
65
|
+
profile_storages: profile.as_ref().and_then(|p| p.storages.clone()),
|
|
66
|
+
profile_lineage: profile.and_then(|p| p.lineage),
|
|
63
67
|
};
|
|
64
68
|
py.allow_threads(|| floe_core::validate(&path, options))
|
|
65
69
|
.map_err(to_py_err)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|