floe-python 0.6.8__tar.gz → 0.6.10__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.
Files changed (241) hide show
  1. {floe_python-0.6.8 → floe_python-0.6.10}/Cargo.lock +11 -11
  2. {floe_python-0.6.8 → floe_python-0.6.10}/PKG-INFO +1 -1
  3. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/Cargo.toml +1 -1
  4. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/parse.rs +4 -0
  5. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/template.rs +14 -0
  6. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/types.rs +2 -0
  7. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/lineage/mod.rs +103 -4
  8. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/lineage_validation.rs +18 -0
  9. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/templating.rs +79 -0
  10. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/lineage.rs +128 -2
  11. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/Cargo.toml +2 -2
  12. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/pyproject.duckdb.toml +2 -2
  13. {floe_python-0.6.8 → floe_python-0.6.10}/pyproject.toml +1 -1
  14. {floe_python-0.6.8 → floe_python-0.6.10}/Cargo.toml +0 -0
  15. {floe_python-0.6.8 → floe_python-0.6.10}/README.md +0 -0
  16. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/README.md +0 -0
  17. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/add_entity.rs +0 -0
  18. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/checks/cast.rs +0 -0
  19. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/checks/mismatch.rs +0 -0
  20. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/checks/mod.rs +0 -0
  21. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/checks/normalize.rs +0 -0
  22. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/checks/not_null.rs +0 -0
  23. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/checks/unique.rs +0 -0
  24. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/catalog.rs +0 -0
  25. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/location.rs +0 -0
  26. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/mod.rs +0 -0
  27. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/storage.rs +0 -0
  28. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/validate.rs +0 -0
  29. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/config/yaml_decode.rs +0 -0
  30. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/errors.rs +0 -0
  31. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/format.rs +0 -0
  32. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/mod.rs +0 -0
  33. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/avro.rs +0 -0
  34. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/csv.rs +0 -0
  35. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/fixed_width.rs +0 -0
  36. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/json.rs +0 -0
  37. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/json_selector.rs +0 -0
  38. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/mod.rs +0 -0
  39. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/orc.rs +0 -0
  40. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/parquet.rs +0 -0
  41. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/xlsx.rs +0 -0
  42. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/xml.rs +0 -0
  43. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/read/xml_selector.rs +0 -0
  44. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/extensions.rs +0 -0
  45. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/mod.rs +0 -0
  46. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/paths.rs +0 -0
  47. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/placement.rs +0 -0
  48. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/planner.rs +0 -0
  49. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/uri.rs +0 -0
  50. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/core/validation.rs +0 -0
  51. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/mod.rs +0 -0
  52. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/object_store.rs +0 -0
  53. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/ops/archive.rs +0 -0
  54. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/ops/inputs.rs +0 -0
  55. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/ops/mod.rs +0 -0
  56. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/ops/output.rs +0 -0
  57. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/providers/adls.rs +0 -0
  58. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/providers/gcs.rs +0 -0
  59. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/providers/local.rs +0 -0
  60. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/providers/mod.rs +0 -0
  61. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/providers/s3.rs +0 -0
  62. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/storage/target.rs +0 -0
  63. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/unique_seed/mod.rs +0 -0
  64. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/accepted.rs +0 -0
  65. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/arrow_convert.rs +0 -0
  66. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/csv.rs +0 -0
  67. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/delta/commit_metrics.rs +0 -0
  68. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/delta/options.rs +0 -0
  69. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/delta/record_batch.rs +0 -0
  70. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/delta/unity.rs +0 -0
  71. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/delta/unity_tests.rs +0 -0
  72. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/delta.rs +0 -0
  73. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/duckdb/conn.rs +0 -0
  74. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/duckdb/merge.rs +0 -0
  75. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/duckdb.rs +0 -0
  76. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg/context.rs +0 -0
  77. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg/data_files.rs +0 -0
  78. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg/glue.rs +0 -0
  79. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg/metadata.rs +0 -0
  80. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg/rest.rs +0 -0
  81. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg/schema.rs +0 -0
  82. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/iceberg.rs +0 -0
  83. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/metrics.rs +0 -0
  84. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/mod.rs +0 -0
  85. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/parquet.rs +0 -0
  86. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/parts.rs +0 -0
  87. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/sink_format.rs +0 -0
  88. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/append.rs +0 -0
  89. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/merge/keys.rs +0 -0
  90. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/merge/mod.rs +0 -0
  91. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/merge/scd1.rs +0 -0
  92. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/merge/scd2.rs +0 -0
  93. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/merge/shared.rs +0 -0
  94. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/mod.rs +0 -0
  95. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/io/write/strategy/overwrite.rs +0 -0
  96. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/lib.rs +0 -0
  97. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/log.rs +0 -0
  98. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/manifest/builder.rs +0 -0
  99. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/manifest/mod.rs +0 -0
  100. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/manifest/model.rs +0 -0
  101. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/manifest/reconstruct.rs +0 -0
  102. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/manifest/runtime.rs +0 -0
  103. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/profile/mod.rs +0 -0
  104. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/profile/parse.rs +0 -0
  105. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/profile/types.rs +0 -0
  106. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/profile/validate.rs +0 -0
  107. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/report/build.rs +0 -0
  108. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/report/entity.rs +0 -0
  109. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/report/mod.rs +0 -0
  110. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/report/output.rs +0 -0
  111. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/context.rs +0 -0
  112. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/accepted_buffer.rs +0 -0
  113. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/accepted_write.rs +0 -0
  114. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/incremental.rs +0 -0
  115. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/mod.rs +0 -0
  116. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/pii.rs +0 -0
  117. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/precheck.rs +0 -0
  118. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/process.rs +0 -0
  119. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/resolve.rs +0 -0
  120. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/entity/validate_split.rs +0 -0
  121. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/events.rs +0 -0
  122. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/file.rs +0 -0
  123. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/mod.rs +0 -0
  124. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/output.rs +0 -0
  125. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/run/perf.rs +0 -0
  126. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/runner/mod.rs +0 -0
  127. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/runner/outcome.rs +0 -0
  128. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/runtime.rs +0 -0
  129. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/secret.rs +0 -0
  130. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/state/mod.rs +0 -0
  131. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/vars/mod.rs +0 -0
  132. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/vars/resolve.rs +0 -0
  133. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/src/warnings.rs +0 -0
  134. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/archive_run.rs +0 -0
  135. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/composite_unique.rs +0 -0
  136. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/delta_run.rs +0 -0
  137. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/dry_run.rs +0 -0
  138. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/duckdb_run.rs +0 -0
  139. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/fixed_width.rs +0 -0
  140. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/iceberg_gcs_run.rs +0 -0
  141. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/iceberg_glue_run.rs +0 -0
  142. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/iceberg_run.rs +0 -0
  143. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/iceberg_s3_run.rs +0 -0
  144. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/json_selectors.rs +0 -0
  145. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/local_run.rs +0 -0
  146. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/mod.rs +0 -0
  147. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/path_normalization.rs +0 -0
  148. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration/run_entities_filter.rs +0 -0
  149. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/integration.rs +0 -0
  150. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/common.rs +0 -0
  151. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/add_entity.rs +0 -0
  152. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/adls_storage.rs +0 -0
  153. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/adls_validation.rs +0 -0
  154. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/catalogs.rs +0 -0
  155. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/config_validation.rs +0 -0
  156. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/duckdb_validation.rs +0 -0
  157. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/gcs_storage.rs +0 -0
  158. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/gcs_validation.rs +0 -0
  159. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/local_storage.rs +0 -0
  160. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/location.rs +0 -0
  161. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/mod.rs +0 -0
  162. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/parse.rs +0 -0
  163. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/pii_validation.rs +0 -0
  164. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/remote_base.rs +0 -0
  165. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/config/storage_resolver_uri.rs +0 -0
  166. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/format.rs +0 -0
  167. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/mod.rs +0 -0
  168. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/avro_input.rs +0 -0
  169. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/csv_nulls.rs +0 -0
  170. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/json_array.rs +0 -0
  171. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/json_ndjson.rs +0 -0
  172. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/json_selector.rs +0 -0
  173. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/mod.rs +0 -0
  174. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/orc_input.rs +0 -0
  175. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/parquet_input.rs +0 -0
  176. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/tsv.rs +0 -0
  177. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/xlsx_input.rs +0 -0
  178. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/xml.rs +0 -0
  179. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/read/xml_selector.rs +0 -0
  180. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/adls.rs +0 -0
  181. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/adls_integration.rs +0 -0
  182. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/gcs.rs +0 -0
  183. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/inputs.rs +0 -0
  184. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/local.rs +0 -0
  185. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/mod.rs +0 -0
  186. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/paths.rs +0 -0
  187. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/planner.rs +0 -0
  188. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/s3.rs +0 -0
  189. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/storage/target.rs +0 -0
  190. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/delta_merge.rs +0 -0
  191. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/delta_write.rs +0 -0
  192. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/iceberg_write.rs +0 -0
  193. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/metrics.rs +0 -0
  194. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/mod.rs +0 -0
  195. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/object_store.rs +0 -0
  196. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/parquet_write.rs +0 -0
  197. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/parts.rs +0 -0
  198. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/io/write/rejected_csv.rs +0 -0
  199. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/manifest/mod.rs +0 -0
  200. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/manifest/runtime.rs +0 -0
  201. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/mod.rs +0 -0
  202. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/profile/mod.rs +0 -0
  203. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/profile/parse.rs +0 -0
  204. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/profile/validate.rs +0 -0
  205. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/report/accepted_output.rs +0 -0
  206. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/report/mod.rs +0 -0
  207. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/report/storage.rs +0 -0
  208. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/check_order.rs +0 -0
  209. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/checks.rs +0 -0
  210. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/entity/accepted_output.rs +0 -0
  211. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/entity/incremental.rs +0 -0
  212. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/entity/mod.rs +0 -0
  213. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/mod.rs +0 -0
  214. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/normalize.rs +0 -0
  215. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/pii.rs +0 -0
  216. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/replay_base.rs +0 -0
  217. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/report.rs +0 -0
  218. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/run/schema_mismatch.rs +0 -0
  219. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/runner/adapter.rs +0 -0
  220. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/runner/mod.rs +0 -0
  221. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/state/mod.rs +0 -0
  222. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/vars/mod.rs +0 -0
  223. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit/vars/resolve.rs +0 -0
  224. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-core/tests/unit.rs +0 -0
  225. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/.gitignore +0 -0
  226. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/README.md +0 -0
  227. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/functions.rs +0 -0
  228. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/lib.rs +0 -0
  229. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/observer.rs +0 -0
  230. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/types/config.rs +0 -0
  231. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/types/errors.rs +0 -0
  232. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/types/mod.rs +0 -0
  233. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/src/types/outcome.rs +0 -0
  234. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/tests/fixtures/config.yml +0 -0
  235. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/tests/fixtures/in/customer/customers_valid.csv +0 -0
  236. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/tests/fixtures/invalid_config.yml +0 -0
  237. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/tests/fixtures/profile.yml +0 -0
  238. {floe_python-0.6.8 → floe_python-0.6.10}/crates/floe-python/tests/test_floe.py +0 -0
  239. {floe_python-0.6.8 → floe_python-0.6.10}/python/floe/__init__.py +0 -0
  240. {floe_python-0.6.8 → floe_python-0.6.10}/python/floe/_floe.pyi +0 -0
  241. {floe_python-0.6.8 → floe_python-0.6.10}/python/floe/py.typed +0 -0
@@ -1805,7 +1805,7 @@ dependencies = [
1805
1805
  "crc",
1806
1806
  "digest",
1807
1807
  "rustversion",
1808
- "spin 0.10.0",
1808
+ "spin 0.10.1",
1809
1809
  ]
1810
1810
 
1811
1811
  [[package]]
@@ -3205,9 +3205,9 @@ dependencies = [
3205
3205
 
3206
3206
  [[package]]
3207
3207
  name = "ethnum"
3208
- version = "1.5.2"
3208
+ version = "1.5.3"
3209
3209
  source = "registry+https://github.com/rust-lang/crates.io-index"
3210
- checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b"
3210
+ checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f"
3211
3211
 
3212
3212
  [[package]]
3213
3213
  name = "event-listener"
@@ -3340,7 +3340,7 @@ dependencies = [
3340
3340
 
3341
3341
  [[package]]
3342
3342
  name = "floe-cli"
3343
- version = "0.6.8"
3343
+ version = "0.6.10"
3344
3344
  dependencies = [
3345
3345
  "assert_cmd",
3346
3346
  "clap",
@@ -3353,7 +3353,7 @@ dependencies = [
3353
3353
 
3354
3354
  [[package]]
3355
3355
  name = "floe-core"
3356
- version = "0.6.8"
3356
+ version = "0.6.10"
3357
3357
  dependencies = [
3358
3358
  "apache-avro",
3359
3359
  "arrow",
@@ -3399,7 +3399,7 @@ dependencies = [
3399
3399
 
3400
3400
  [[package]]
3401
3401
  name = "floe-python"
3402
- version = "0.6.8"
3402
+ version = "0.6.10"
3403
3403
  dependencies = [
3404
3404
  "floe-core",
3405
3405
  "pyo3",
@@ -4535,7 +4535,7 @@ version = "1.5.0"
4535
4535
  source = "registry+https://github.com/rust-lang/crates.io-index"
4536
4536
  checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
4537
4537
  dependencies = [
4538
- "spin 0.9.8",
4538
+ "spin 0.9.9",
4539
4539
  ]
4540
4540
 
4541
4541
  [[package]]
@@ -7535,15 +7535,15 @@ dependencies = [
7535
7535
 
7536
7536
  [[package]]
7537
7537
  name = "spin"
7538
- version = "0.9.8"
7538
+ version = "0.9.9"
7539
7539
  source = "registry+https://github.com/rust-lang/crates.io-index"
7540
- checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
7540
+ checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
7541
7541
 
7542
7542
  [[package]]
7543
7543
  name = "spin"
7544
- version = "0.10.0"
7544
+ version = "0.10.1"
7545
7545
  source = "registry+https://github.com/rust-lang/crates.io-index"
7546
- checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
7546
+ checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3"
7547
7547
 
7548
7548
  [[package]]
7549
7549
  name = "spki"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: floe-python
3
- Version: 0.6.8
3
+ Version: 0.6.10
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "floe-core"
3
- version = "0.6.8"
3
+ version = "0.6.10"
4
4
  edition = "2021"
5
5
  description = "Core library for Floe, a YAML-driven technical ingestion tool."
6
6
  license = "MIT"
@@ -1190,9 +1190,11 @@ pub(crate) fn parse_lineage_config(value: &Yaml) -> FloeResult<LineageConfig> {
1190
1190
  "lineage",
1191
1191
  &[
1192
1192
  "url",
1193
+ "endpoint",
1193
1194
  "api_key",
1194
1195
  "timeout_secs",
1195
1196
  "namespace",
1197
+ "dataset_namespace",
1196
1198
  "producer",
1197
1199
  "max_failures",
1198
1200
  "job_name",
@@ -1200,9 +1202,11 @@ pub(crate) fn parse_lineage_config(value: &Yaml) -> FloeResult<LineageConfig> {
1200
1202
  )?;
1201
1203
  Ok(LineageConfig {
1202
1204
  url: get_string(hash, "url", "lineage")?,
1205
+ endpoint: opt_string(hash, "endpoint", "lineage")?,
1203
1206
  api_key: opt_string(hash, "api_key", "lineage")?,
1204
1207
  timeout_secs: opt_u64(hash, "timeout_secs", "lineage")?,
1205
1208
  namespace: get_string(hash, "namespace", "lineage")?,
1209
+ dataset_namespace: opt_string(hash, "dataset_namespace", "lineage")?,
1206
1210
  producer: opt_string(hash, "producer", "lineage")?,
1207
1211
  max_failures: opt_u32(hash, "max_failures", "lineage")?,
1208
1212
  job_name: opt_string(hash, "job_name", "lineage")?,
@@ -34,9 +34,11 @@ pub fn apply_templates_with_vars(
34
34
 
35
35
  if let Some(lineage) = config.lineage.as_mut() {
36
36
  lineage.url = replace_placeholders(&lineage.url, &vars, "lineage.url", None)?;
37
+ replace_lineage_field(&mut lineage.endpoint, &vars, "endpoint")?;
37
38
  if let Some(api_key) = lineage.api_key.as_mut() {
38
39
  *api_key = replace_placeholders(api_key, &vars, "lineage.api_key", None)?;
39
40
  }
41
+ replace_lineage_field(&mut lineage.dataset_namespace, &vars, "dataset_namespace")?;
40
42
  }
41
43
 
42
44
  if let Some(storages) = config.storages.as_mut() {
@@ -96,6 +98,18 @@ pub fn apply_templates_with_vars(
96
98
  Ok(())
97
99
  }
98
100
 
101
+ fn replace_lineage_field(
102
+ value: &mut Option<String>,
103
+ vars: &HashMap<String, String>,
104
+ field_name: &str,
105
+ ) -> FloeResult<()> {
106
+ if let Some(current) = value.as_mut() {
107
+ let field = format!("lineage.{field_name}");
108
+ *current = replace_placeholders(current, vars, &field, None)?;
109
+ }
110
+ Ok(())
111
+ }
112
+
99
113
  fn replace_storage_definition_field(
100
114
  value: &mut Option<String>,
101
115
  vars: &HashMap<String, String>,
@@ -28,9 +28,11 @@ pub struct RootConfig {
28
28
  #[derive(Debug, Clone, Serialize, Deserialize)]
29
29
  pub struct LineageConfig {
30
30
  pub url: String,
31
+ pub endpoint: Option<String>,
31
32
  pub api_key: Option<String>,
32
33
  pub timeout_secs: Option<u64>,
33
34
  pub namespace: String,
35
+ pub dataset_namespace: Option<String>,
34
36
  pub producer: Option<String>,
35
37
  pub max_failures: Option<u32>,
36
38
  pub job_name: Option<String>,
@@ -8,12 +8,15 @@ use uuid::Uuid;
8
8
 
9
9
  use crate::config::{EntityConfig, LineageConfig};
10
10
  use crate::run::events::{RunEvent, RunObserver};
11
+ use crate::secret::Secret;
11
12
 
12
13
  const DEFAULT_PRODUCER: &str = concat!(
13
14
  "https://github.com/malon64/floe/releases/tag/v",
14
15
  env!("CARGO_PKG_VERSION")
15
16
  );
16
17
 
18
+ const OPENLINEAGE_API_KEY_ENV: &str = "OPENLINEAGE_API_KEY";
19
+
17
20
  #[derive(Clone)]
18
21
  struct ColumnMapping {
19
22
  output_name: String,
@@ -32,9 +35,48 @@ struct EntityUris {
32
35
  rejected: Option<OlDataset>,
33
36
  }
34
37
 
38
+ fn resolve_lineage_url(base: &str, endpoint: Option<&str>) -> String {
39
+ let path = endpoint
40
+ .map(str::trim)
41
+ .filter(|s| !s.is_empty())
42
+ .unwrap_or("api/v1/lineage");
43
+ format!(
44
+ "{}/{}",
45
+ base.trim_end_matches('/'),
46
+ path.trim_start_matches('/')
47
+ )
48
+ }
49
+
50
+ fn is_unresolved_placeholder(value: &str) -> bool {
51
+ let value = value.trim();
52
+ value.starts_with("{{") && value.ends_with("}}")
53
+ }
54
+
55
+ /// Effective Bearer token for lineage POSTs.
56
+ ///
57
+ /// An explicit `lineage.api_key` wins for backward compatibility. Otherwise Floe falls back
58
+ /// to the ambient `OPENLINEAGE_API_KEY`, mirroring the OpenLineage Python client so a replayed
59
+ /// manifest authenticates from a runner-mounted Secret without the credential ever being
60
+ /// persisted in the manifest. An unexpanded `{{...}}` placeholder is treated as absent:
61
+ /// profile-supplied lineage is merged after config templating (see `apply_profile_lineage`),
62
+ /// so the raw placeholder would otherwise be sent verbatim as the token.
63
+ fn resolve_api_key(configured: Option<&str>, ambient: Option<String>) -> Option<Secret> {
64
+ let explicit = configured
65
+ .map(str::trim)
66
+ .filter(|key| !key.is_empty() && !is_unresolved_placeholder(key));
67
+ match explicit {
68
+ Some(key) => Some(Secret::from(key)),
69
+ None => ambient
70
+ .map(|value| value.trim().to_string())
71
+ .filter(|value| !value.is_empty())
72
+ .map(Secret::from),
73
+ }
74
+ }
75
+
35
76
  pub struct OpenLineageObserver {
36
77
  client: reqwest::blocking::Client,
37
78
  config: LineageConfig,
79
+ api_key: Option<Secret>,
38
80
  entity_start_ms: Mutex<HashMap<String, u128>>,
39
81
  entity_run_ids: Mutex<HashMap<String, String>>,
40
82
  run_start_ms: Mutex<Option<u128>>,
@@ -91,6 +133,12 @@ impl OpenLineageObserver {
91
133
  })
92
134
  .collect();
93
135
 
136
+ let dataset_namespace = config
137
+ .dataset_namespace
138
+ .clone()
139
+ .filter(|s| !s.trim().is_empty())
140
+ .unwrap_or_else(|| config.namespace.clone());
141
+
94
142
  let entity_uris = entities
95
143
  .iter()
96
144
  .map(|e| {
@@ -105,7 +153,7 @@ impl OpenLineageObserver {
105
153
  let ns = iceberg.namespace.as_deref().unwrap_or(e.name.as_str());
106
154
  let tbl = iceberg.table.as_deref().unwrap_or(e.name.as_str());
107
155
  OlDataset {
108
- namespace: config.namespace.clone(),
156
+ namespace: dataset_namespace.clone(),
109
157
  name: format!("{ns}.{tbl}"),
110
158
  }
111
159
  }
@@ -137,9 +185,15 @@ impl OpenLineageObserver {
137
185
  })
138
186
  .collect();
139
187
 
188
+ let api_key = resolve_api_key(
189
+ config.api_key.as_deref(),
190
+ std::env::var(OPENLINEAGE_API_KEY_ENV).ok(),
191
+ );
192
+
140
193
  Ok(Self {
141
194
  client,
142
195
  config: config.clone(),
196
+ api_key,
143
197
  entity_start_ms: Mutex::new(HashMap::new()),
144
198
  entity_run_ids: Mutex::new(HashMap::new()),
145
199
  run_start_ms: Mutex::new(None),
@@ -154,8 +208,8 @@ impl OpenLineageObserver {
154
208
 
155
209
  fn attempt_post(&self, url: &str, body: &Value) -> Result<(), bool> {
156
210
  let mut req = self.client.post(url).json(body);
157
- if let Some(api_key) = self.config.api_key.as_deref() {
158
- req = req.bearer_auth(api_key);
211
+ if let Some(api_key) = self.api_key.as_ref() {
212
+ req = req.bearer_auth(api_key.expose());
159
213
  }
160
214
  match req.send() {
161
215
  Err(_) => Err(true),
@@ -177,7 +231,7 @@ impl OpenLineageObserver {
177
231
  return;
178
232
  }
179
233
 
180
- let url = format!("{}/api/v1/lineage", self.config.url.trim_end_matches('/'));
234
+ let url = resolve_lineage_url(&self.config.url, self.config.endpoint.as_deref());
181
235
  let max_failures = self.config.max_failures.unwrap_or(3) as usize;
182
236
  let retry_delays_ms: &[u64] = &[0, 100, 500];
183
237
 
@@ -590,3 +644,48 @@ impl OpenLineageObserver {
590
644
  self.consecutive_failures.load(Ordering::Relaxed)
591
645
  }
592
646
  }
647
+
648
+ #[cfg(test)]
649
+ mod tests {
650
+ use super::*;
651
+
652
+ fn expose(key: Option<Secret>) -> Option<String> {
653
+ key.map(|s| s.expose().to_string())
654
+ }
655
+
656
+ #[test]
657
+ fn explicit_config_key_wins_over_env() {
658
+ let key = resolve_api_key(Some("cfg-token"), Some("env-token".to_string()));
659
+ assert_eq!(expose(key), Some("cfg-token".to_string()));
660
+ }
661
+
662
+ #[test]
663
+ fn falls_back_to_env_when_config_absent() {
664
+ let key = resolve_api_key(None, Some("env-token".to_string()));
665
+ assert_eq!(expose(key), Some("env-token".to_string()));
666
+ }
667
+
668
+ #[test]
669
+ fn unresolved_placeholder_falls_back_to_env() {
670
+ let key = resolve_api_key(
671
+ Some("{{OPENLINEAGE_API_KEY}}"),
672
+ Some("env-token".to_string()),
673
+ );
674
+ assert_eq!(expose(key), Some("env-token".to_string()));
675
+ }
676
+
677
+ #[test]
678
+ fn unresolved_placeholder_without_env_is_none() {
679
+ assert!(resolve_api_key(Some("{{OPENLINEAGE_API_KEY}}"), None).is_none());
680
+ }
681
+
682
+ #[test]
683
+ fn none_when_neither_source_present() {
684
+ assert!(resolve_api_key(None, None).is_none());
685
+ }
686
+
687
+ #[test]
688
+ fn blank_values_are_ignored() {
689
+ assert!(resolve_api_key(Some(" "), Some(" ".to_string())).is_none());
690
+ }
691
+ }
@@ -74,7 +74,9 @@ report:
74
74
  path: "/tmp/reports"
75
75
  lineage:
76
76
  url: "http://marquez:5000"
77
+ endpoint: "/api/v1/openlineage/lineage"
77
78
  namespace: "my-namespace"
79
+ dataset_namespace: "iceberg.prod"
78
80
  timeout_secs: 10
79
81
  producer: "https://github.com/myorg/floe"
80
82
  entities:
@@ -95,6 +97,20 @@ entities:
95
97
  "#;
96
98
  let path = write_temp_config(config);
97
99
  validate(&path, ValidateOptions::default()).expect("valid lineage config");
100
+ let parsed = load_config_with_profile_overrides(
101
+ &path,
102
+ &std::collections::HashMap::new(),
103
+ None,
104
+ None,
105
+ None,
106
+ )
107
+ .expect("lineage config should parse");
108
+ let lineage = parsed.lineage.expect("lineage should be present");
109
+ assert_eq!(
110
+ lineage.endpoint.as_deref(),
111
+ Some("/api/v1/openlineage/lineage")
112
+ );
113
+ assert_eq!(lineage.dataset_namespace.as_deref(), Some("iceberg.prod"));
98
114
  }
99
115
 
100
116
  #[test]
@@ -180,7 +196,9 @@ entities:
180
196
  let path = write_temp_config(base);
181
197
  let profile_lineage = LineageConfig {
182
198
  url: "http://localhost:5000".to_string(),
199
+ endpoint: None,
183
200
  namespace: "floe-demo-local".to_string(),
201
+ dataset_namespace: None,
184
202
  api_key: None,
185
203
  timeout_secs: Some(2),
186
204
  producer: None,
@@ -88,6 +88,85 @@ entities:
88
88
  );
89
89
  }
90
90
 
91
+ #[test]
92
+ fn env_vars_apply_to_lineage_endpoint_and_dataset_namespace() {
93
+ let root = temp_dir("floe-lineage-vars");
94
+ let config_yaml = format!(
95
+ r#"version: "0.1"
96
+ env:
97
+ vars:
98
+ LINEAGE_ENDPOINT: "api/v1/openlineage/lineage"
99
+ DATASET_NAMESPACE: "iceberg.prod"
100
+ lineage:
101
+ url: "http://openmetadata:8585"
102
+ endpoint: "{{{{LINEAGE_ENDPOINT}}}}"
103
+ namespace: "dagster"
104
+ dataset_namespace: "{{{{DATASET_NAMESPACE}}}}"
105
+ entities:
106
+ - name: "orders"
107
+ source:
108
+ format: "csv"
109
+ path: "{root}/in/orders.csv"
110
+ sink:
111
+ accepted:
112
+ format: "parquet"
113
+ path: "{root}/out/orders"
114
+ policy:
115
+ severity: "warn"
116
+ schema:
117
+ columns:
118
+ - name: "id"
119
+ type: "string"
120
+ "#,
121
+ root = root.display(),
122
+ );
123
+ let config_path = write_config(&root, &config_yaml);
124
+ let parsed = load_config(&config_path).expect("parse config");
125
+ let lineage = parsed.lineage.expect("lineage");
126
+
127
+ assert_eq!(
128
+ lineage.endpoint.as_deref(),
129
+ Some("api/v1/openlineage/lineage")
130
+ );
131
+ assert_eq!(lineage.dataset_namespace.as_deref(), Some("iceberg.prod"));
132
+ }
133
+
134
+ #[test]
135
+ fn unresolved_lineage_placeholder_errors() {
136
+ let root = temp_dir("floe-lineage-unresolved");
137
+ let config_yaml = format!(
138
+ r#"version: "0.1"
139
+ lineage:
140
+ url: "http://openmetadata:8585"
141
+ endpoint: "{{{{OPENLINEAGE_ENDPOINT}}}}"
142
+ namespace: "dagster"
143
+ entities:
144
+ - name: "orders"
145
+ source:
146
+ format: "csv"
147
+ path: "{root}/in/orders.csv"
148
+ sink:
149
+ accepted:
150
+ format: "parquet"
151
+ path: "{root}/out/orders"
152
+ policy:
153
+ severity: "warn"
154
+ schema:
155
+ columns:
156
+ - name: "id"
157
+ type: "string"
158
+ "#,
159
+ root = root.display(),
160
+ );
161
+ let config_path = write_config(&root, &config_yaml);
162
+
163
+ let err = load_config(&config_path).expect_err("placeholder error");
164
+ assert_eq!(
165
+ err.to_string(),
166
+ "lineage.endpoint references unknown variable OPENLINEAGE_ENDPOINT"
167
+ );
168
+ }
169
+
91
170
  #[test]
92
171
  fn env_file_vars_apply_to_source_path() {
93
172
  let root = temp_dir("floe-env-file");
@@ -9,15 +9,34 @@ use serde_json::json;
9
9
  fn make_config(server_url: &str, max_failures: Option<u32>) -> LineageConfig {
10
10
  LineageConfig {
11
11
  url: server_url.to_string(),
12
+ endpoint: None,
12
13
  api_key: None,
13
14
  timeout_secs: Some(2),
14
15
  namespace: "test-ns".to_string(),
16
+ dataset_namespace: None,
15
17
  producer: None,
16
18
  max_failures,
17
19
  job_name: None,
18
20
  }
19
21
  }
20
22
 
23
+ #[test]
24
+ fn custom_endpoint_is_used_for_posts() {
25
+ let mut server = mockito::Server::new();
26
+ let mock = server
27
+ .mock("POST", "/api/v1/openlineage/lineage")
28
+ .with_status(200)
29
+ .expect(1)
30
+ .create();
31
+
32
+ let mut config = make_config(&format!("{}/", server.url()), None);
33
+ config.endpoint = Some("/api/v1/openlineage/lineage".to_string());
34
+ let obs = OpenLineageObserver::new(&config, &[], "config.yml").unwrap();
35
+
36
+ obs.on_event(run_started_event());
37
+ mock.assert();
38
+ }
39
+
21
40
  fn run_started_event() -> RunEvent {
22
41
  RunEvent::RunStarted {
23
42
  run_id: "test-run-1".to_string(),
@@ -987,7 +1006,8 @@ fn iceberg_accepted_uses_catalog_namespace_and_table_name() {
987
1006
  .match_body(mockito::Matcher::PartialJson(json!({
988
1007
  "eventType": "COMPLETE",
989
1008
  "inputs": [{ "namespace": "s3://iceberg-data", "name": "bronze/sales/customers" }],
990
- "outputs": [{ "namespace": "test-ns", "name": "sales_dev.customers" }]
1009
+ "job": { "namespace": "test-ns" },
1010
+ "outputs": [{ "namespace": "iceberg.prod", "name": "sales_dev.customers" }]
991
1011
  })))
992
1012
  .with_status(200)
993
1013
  .expect(1)
@@ -1006,7 +1026,8 @@ fn iceberg_accepted_uses_catalog_namespace_and_table_name() {
1006
1026
  location: None,
1007
1027
  });
1008
1028
 
1009
- let config = make_config(&server.url(), None);
1029
+ let mut config = make_config(&server.url(), None);
1030
+ config.dataset_namespace = Some("iceberg.prod".to_string());
1010
1031
  let obs = OpenLineageObserver::new(&config, &[entity], "config.yml").unwrap();
1011
1032
 
1012
1033
  obs.on_event(RunEvent::EntityStarted {
@@ -1031,3 +1052,108 @@ fn iceberg_accepted_uses_catalog_namespace_and_table_name() {
1031
1052
  _start_mock.assert();
1032
1053
  _complete_mock.assert();
1033
1054
  }
1055
+
1056
+ #[test]
1057
+ fn iceberg_accepted_dataset_namespace_defaults_to_job_namespace() {
1058
+ let mut server = mockito::Server::new();
1059
+ let _start_mock = server
1060
+ .mock("POST", "/api/v1/lineage")
1061
+ .with_status(200)
1062
+ .expect(1)
1063
+ .create();
1064
+ let _complete_mock = server
1065
+ .mock("POST", "/api/v1/lineage")
1066
+ .match_body(mockito::Matcher::PartialJson(json!({
1067
+ "eventType": "COMPLETE",
1068
+ "job": { "namespace": "test-ns" },
1069
+ "outputs": [{ "namespace": "test-ns", "name": "sales_dev.customers" }]
1070
+ })))
1071
+ .with_status(200)
1072
+ .expect(1)
1073
+ .create();
1074
+
1075
+ let mut entity = make_entity(
1076
+ "customers",
1077
+ "s3://iceberg-data/bronze/sales/customers",
1078
+ "s3://warehouse/silver/customers",
1079
+ None,
1080
+ );
1081
+ entity.sink.accepted.iceberg = Some(IcebergSinkTargetConfig {
1082
+ catalog: None,
1083
+ namespace: Some("sales_dev".to_string()),
1084
+ table: Some("customers".to_string()),
1085
+ location: None,
1086
+ });
1087
+
1088
+ let config = make_config(&server.url(), None);
1089
+ let obs = OpenLineageObserver::new(&config, &[entity], "config.yml").unwrap();
1090
+ obs.on_event(entity_started_event());
1091
+ obs.on_event(entity_finished_event("customers", "success"));
1092
+ _start_mock.assert();
1093
+ _complete_mock.assert();
1094
+ }
1095
+
1096
+ // Sole owner of the process-wide OPENLINEAGE_API_KEY variable. The crate has no
1097
+ // serial-test harness, so all three auth paths are asserted inside one test that
1098
+ // controls the variable's lifecycle; no other test reads it, and every other lineage
1099
+ // test either sets an explicit api_key (which wins over the env regardless) or does not
1100
+ // match on request headers, so this cannot race them.
1101
+ #[test]
1102
+ fn api_key_resolution_over_the_wire() {
1103
+ std::env::remove_var("OPENLINEAGE_API_KEY");
1104
+
1105
+ // No config api_key and no env var => request carries no Authorization header.
1106
+ {
1107
+ let mut server = mockito::Server::new();
1108
+ let mock = server
1109
+ .mock("POST", "/api/v1/lineage")
1110
+ .match_header("authorization", mockito::Matcher::Missing)
1111
+ .with_status(200)
1112
+ .expect(1)
1113
+ .create();
1114
+ let config = make_config(&server.url(), None);
1115
+ let obs = OpenLineageObserver::new(&config, &[], "config.yml").unwrap();
1116
+ obs.on_event(run_started_event());
1117
+ mock.assert();
1118
+ }
1119
+
1120
+ // Manifest replay with api_key omitted + env var set => Bearer token from the env.
1121
+ {
1122
+ let mut server = mockito::Server::new();
1123
+ let mock = server
1124
+ .mock("POST", "/api/v1/lineage")
1125
+ .match_header("authorization", "Bearer env-replay-token")
1126
+ .with_status(200)
1127
+ .expect(1)
1128
+ .create();
1129
+ let manifest = format!(
1130
+ r#"{{"spec_version":"0.3","report_base_uri":"file:///tmp","entities":[],"lineage":{{"url":"{}","namespace":"test-ns"}}}}"#,
1131
+ server.url()
1132
+ );
1133
+ std::env::set_var("OPENLINEAGE_API_KEY", "env-replay-token");
1134
+ let (config, _) =
1135
+ floe_core::config_from_manifest_json(&manifest).expect("reconstruct manifest config");
1136
+ let lineage = config.lineage.expect("manifest lineage block");
1137
+ let obs = OpenLineageObserver::new(&lineage, &[], "").unwrap();
1138
+ obs.on_event(run_started_event());
1139
+ mock.assert();
1140
+ }
1141
+
1142
+ // Explicit config api_key wins even when the env var is present (backward compatible).
1143
+ {
1144
+ let mut server = mockito::Server::new();
1145
+ let mock = server
1146
+ .mock("POST", "/api/v1/lineage")
1147
+ .match_header("authorization", "Bearer cfg-token")
1148
+ .with_status(200)
1149
+ .expect(1)
1150
+ .create();
1151
+ let mut config = make_config(&server.url(), None);
1152
+ config.api_key = Some("cfg-token".to_string());
1153
+ let obs = OpenLineageObserver::new(&config, &[], "config.yml").unwrap();
1154
+ obs.on_event(run_started_event());
1155
+ mock.assert();
1156
+ }
1157
+
1158
+ std::env::remove_var("OPENLINEAGE_API_KEY");
1159
+ }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "floe-python"
3
- version = "0.6.8"
3
+ version = "0.6.10"
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.6.8", default-features = false }
17
+ floe-core = { path = "../floe-core", version = "0.6.10", default-features = false }
18
18
  pyo3 = { version = "0.29", features = ["extension-module", "abi3-py310"] }
19
19
  serde_json = "1"
20
20
 
@@ -22,7 +22,7 @@ build-backend = "maturin"
22
22
 
23
23
  [project]
24
24
  name = "floe-duckdb"
25
- version = "0.6.8"
25
+ version = "0.6.10"
26
26
  description = "DuckDB companion extension for floe — adds the native DuckDB sink to the floe Python package"
27
27
  readme = "README.md"
28
28
  requires-python = ">=3.10"
@@ -46,7 +46,7 @@ classifiers = [
46
46
  # only contributes the compiled DuckDB extension module into that package. Pin to
47
47
  # the exact lean version so the companion's `_floe_duckdb` ABI always matches the
48
48
  # `floe` package it plugs into.
49
- dependencies = ["floe-python==0.6.8"]
49
+ dependencies = ["floe-python==0.6.10"]
50
50
 
51
51
  [project.urls]
52
52
  Homepage = "https://github.com/malon64/floe"
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "floe-python"
7
- version = "0.6.8"
7
+ version = "0.6.10"
8
8
  description = "Python bindings for floe-core — run floe pipelines at Rust speed from Python and notebooks"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes