datajunction 0.0.240__tar.gz → 0.0.243__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 (184) hide show
  1. {datajunction-0.0.240 → datajunction-0.0.243}/PKG-INFO +1 -1
  2. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/__about__.py +1 -1
  3. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/models.py +44 -1
  4. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_deploy.py +104 -11
  5. {datajunction-0.0.240 → datajunction-0.0.243}/.coveragerc +0 -0
  6. {datajunction-0.0.240 → datajunction-0.0.243}/.gitignore +0 -0
  7. {datajunction-0.0.240 → datajunction-0.0.243}/.pre-commit-config.yaml +0 -0
  8. {datajunction-0.0.240 → datajunction-0.0.243}/LICENSE.txt +0 -0
  9. {datajunction-0.0.240 → datajunction-0.0.243}/Makefile +0 -0
  10. {datajunction-0.0.240 → datajunction-0.0.243}/README.md +0 -0
  11. {datajunction-0.0.240 → datajunction-0.0.243}/claude_desktop_config.example.json +0 -0
  12. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/__init__.py +0 -0
  13. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/_base.py +0 -0
  14. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/_internal.py +0 -0
  15. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/admin.py +0 -0
  16. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/builder.py +0 -0
  17. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/cli.py +0 -0
  18. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/client.py +0 -0
  19. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/compile.py +0 -0
  20. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/deployment.py +0 -0
  21. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/exceptions.py +0 -0
  22. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/mcp/__init__.py +0 -0
  23. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/mcp/cli.py +0 -0
  24. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/mcp/config.py +0 -0
  25. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/nodes.py +0 -0
  26. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/rendering.py +0 -0
  27. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/init_system_nodes.py +0 -0
  28. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/date.yaml +0 -0
  29. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/deployment.yaml +0 -0
  30. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/dimension_link.yaml +0 -0
  31. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/distinct_node_authors.yaml +0 -0
  32. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/distinct_node_editors.yaml +0 -0
  33. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/dj.yaml +0 -0
  34. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/is_active.yaml +0 -0
  35. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/materialization.yaml +0 -0
  36. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/median_deployment_duration_seconds.yaml +0 -0
  37. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/median_dim_links_per_node.yaml +0 -0
  38. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/median_dimension_indegree.yaml +0 -0
  39. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/median_downstream_count.yaml +0 -0
  40. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/median_revisions_per_node.yaml +0 -0
  41. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/median_upstream_count.yaml +0 -0
  42. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/namespace.yaml +0 -0
  43. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/node_revision.yaml +0 -0
  44. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/node_type.yaml +0 -0
  45. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/nodes.yaml +0 -0
  46. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_branches.yaml +0 -0
  47. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_deployments.yaml +0 -0
  48. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_materializations.yaml +0 -0
  49. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_namespaces.yaml +0 -0
  50. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_nodes.yaml +0 -0
  51. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_orphan_nodes.yaml +0 -0
  52. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/number_of_unused_dimensions.yaml +0 -0
  53. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/repo.yaml +0 -0
  54. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/seed/nodes/user.yaml +0 -0
  55. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/skills/datajunction/SKILL.md +0 -0
  56. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/skills/datajunction-api/SKILL.md +0 -0
  57. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/skills/datajunction-query/SKILL.md +0 -0
  58. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/skills/datajunction-repo/SKILL.md +0 -0
  59. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/skills/datajunction-semantic-model/SKILL.md +0 -0
  60. {datajunction-0.0.240 → datajunction-0.0.243}/datajunction/tags.py +0 -0
  61. {datajunction-0.0.240 → datajunction-0.0.243}/pyproject.toml +0 -0
  62. {datajunction-0.0.240 → datajunction-0.0.243}/setup.cfg +0 -0
  63. {datajunction-0.0.240 → datajunction-0.0.243}/tests/__init__.py +0 -0
  64. {datajunction-0.0.240 → datajunction-0.0.243}/tests/conftest.py +0 -0
  65. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/dj.yaml +0 -0
  66. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/roads/companies.yaml +0 -0
  67. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/roads/companies_dim.yaml +0 -0
  68. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/roads/contractor.yaml +0 -0
  69. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/roads/contractors.yaml +0 -0
  70. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/roads/us_state.yaml +0 -0
  71. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/deploy0/roads/us_states.yaml +0 -0
  72. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/dj.yaml +0 -0
  73. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/avg_length_of_employment.metric.yaml +0 -0
  74. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/avg_repair_price.metric.yaml +0 -0
  75. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/avg_time_to_dispatch.metric.yaml +0 -0
  76. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/contractor.dimension.yaml +0 -0
  77. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/contractors.source.yaml +0 -0
  78. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/date.source.yaml +0 -0
  79. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/date_dim.dimension.yaml +0 -0
  80. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/dispatcher.dimension.yaml +0 -0
  81. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/dispatchers.source.yaml +0 -0
  82. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/hard_hat.dimension.yaml +0 -0
  83. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/hard_hat_state.source.yaml +0 -0
  84. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/hard_hats.source.yaml +0 -0
  85. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/local_hard_hats.dimension.yaml +0 -0
  86. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/municipality.source.yaml +0 -0
  87. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/municipality_dim.dimension.yaml +0 -0
  88. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/municipality_municipality_type.source.yaml +0 -0
  89. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/municipality_type.source.yaml +0 -0
  90. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/national_level_agg.transform.yaml +0 -0
  91. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/num_repair_orders.metric.yaml +0 -0
  92. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/regional_level_agg.transform.yaml +0 -0
  93. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/regional_repair_efficiency.metric.yaml +0 -0
  94. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/repair_order.dimension.yaml +0 -0
  95. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/repair_order_details.source.yaml +0 -0
  96. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/repair_order_transform.transform.yaml +0 -0
  97. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/repair_orders.source.yaml +0 -0
  98. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/repair_orders_cube.cube.yaml +0 -0
  99. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/repair_type.source.yaml +0 -0
  100. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/total_repair_cost.metric.yaml +0 -0
  101. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/total_repair_order_discounts.metric.yaml +0 -0
  102. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/us_region.source.yaml +0 -0
  103. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/us_state.dimension.yaml +0 -0
  104. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project1/roads/us_states.source.yaml +0 -0
  105. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project10/dj.yaml +0 -0
  106. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/avg_length_of_employment.metric.yaml +0 -0
  107. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/avg_repair_price.metric.yaml +0 -0
  108. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/avg_time_to_dispatch.metric.yaml +0 -0
  109. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/contractor.dimension.yaml +0 -0
  110. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/contractors.source.yaml +0 -0
  111. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/date.source.yaml +0 -0
  112. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/date_dim.dimension.yaml +0 -0
  113. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/dispatcher.dimension.yaml +0 -0
  114. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/dispatchers.source.yaml +0 -0
  115. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/dj.yaml +0 -0
  116. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/hard_hat.dimension.yaml +0 -0
  117. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/hard_hat_state.source.yaml +0 -0
  118. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/hard_hats.source.yaml +0 -0
  119. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/local_hard_hats.dimension.yaml +0 -0
  120. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/municipality.source.yaml +0 -0
  121. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/municipality_dim.dimension.yaml +0 -0
  122. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/municipality_municipality_type.source.yaml +0 -0
  123. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/municipality_type.source.yaml +0 -0
  124. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/national_level_agg.transform.yaml +0 -0
  125. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/num_repair_orders.metric.yaml +0 -0
  126. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/regional_level_agg.transform.yaml +0 -0
  127. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/regional_repair_efficiency.metric.yaml +0 -0
  128. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/repair_order.dimension.yaml +0 -0
  129. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/repair_order_details.source.yaml +0 -0
  130. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/repair_order_transform.transform.yaml +0 -0
  131. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/repair_orders.source.yaml +0 -0
  132. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/repair_orders_cube.cube.yaml +0 -0
  133. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/repair_type.source.yaml +0 -0
  134. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/total_repair_cost.metric.yaml +0 -0
  135. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/total_repair_order_discounts.metric.yaml +0 -0
  136. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/us_region.source.yaml +0 -0
  137. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/us_state.dimension.yaml +0 -0
  138. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project11/us_states.source.yaml +0 -0
  139. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/dj.yaml +0 -0
  140. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/roads/companies.source.yaml +0 -0
  141. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/roads/companies_dim.dimension.yaml +0 -0
  142. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/roads/contractor.dimension.yaml +0 -0
  143. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/roads/contractors.source.yaml +0 -0
  144. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/roads/us_state.dimension.yaml +0 -0
  145. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project12/roads/us_states.source.yaml +0 -0
  146. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project2/dj.yaml +0 -0
  147. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project2/some_node.source.yaml +0 -0
  148. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project3/dj.yaml +0 -0
  149. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project3/some_node.yaml +0 -0
  150. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project4/dj.yaml +0 -0
  151. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project4/very/very/deeply/nested/namespace/some_node.source.yaml +0 -0
  152. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project5/dj.yaml +0 -0
  153. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project5/some_node.a.b.c.source.yaml +0 -0
  154. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project6/dj.yaml +0 -0
  155. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project6/roads/contractor.dimension.yaml +0 -0
  156. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project6/roads/contractors.source.yaml +0 -0
  157. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project7/dj.yaml +0 -0
  158. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project7/roads/contractor.dimension.yaml +0 -0
  159. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project7/roads/contractors.source.yaml +0 -0
  160. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project8/dj.yaml +0 -0
  161. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/dj.yaml +0 -0
  162. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/roads/companies.source.yaml +0 -0
  163. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/roads/companies_dim.dimension.yaml +0 -0
  164. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/roads/contractor.dimension.yaml +0 -0
  165. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/roads/contractors.source.yaml +0 -0
  166. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/roads/us_state.dimension.yaml +0 -0
  167. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples/project9/roads/us_states.source.yaml +0 -0
  168. {datajunction-0.0.240 → datajunction-0.0.243}/tests/examples.py +0 -0
  169. {datajunction-0.0.240 → datajunction-0.0.243}/tests/mcp/README.md +0 -0
  170. {datajunction-0.0.240 → datajunction-0.0.243}/tests/mcp/__init__.py +0 -0
  171. {datajunction-0.0.240 → datajunction-0.0.243}/tests/mcp/test_cli.py +0 -0
  172. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test__internal.py +0 -0
  173. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_admin.py +0 -0
  174. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_base.py +0 -0
  175. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_builder.py +0 -0
  176. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_cli.py +0 -0
  177. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_client.py +0 -0
  178. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_compile.py +0 -0
  179. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_generated_client.py +0 -0
  180. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_integration.py +0 -0
  181. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_models.py +0 -0
  182. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_plugin_mirror.py +0 -0
  183. {datajunction-0.0.240 → datajunction-0.0.243}/tests/test_skills.py +0 -0
  184. {datajunction-0.0.240 → datajunction-0.0.243}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: datajunction
3
- Version: 0.0.240
3
+ Version: 0.0.243
4
4
  Summary: DataJunction client library for connecting to a DataJunction server
5
5
  Project-URL: repository, https://github.com/DataJunction/dj
6
6
  Author-email: DataJunction Authors <yian.shang@gmail.com>
@@ -2,4 +2,4 @@
2
2
  Version for Hatch
3
3
  """
4
4
 
5
- __version__ = "0.0.240"
5
+ __version__ = "0.0.243"
@@ -3,8 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import enum
6
+ import re
6
7
  from dataclasses import dataclass, field
7
- from typing import TYPE_CHECKING, Any
8
+ from typing import TYPE_CHECKING, Any, Literal, TypeAlias
8
9
 
9
10
  from datajunction._base import SerializableMixin
10
11
 
@@ -361,6 +362,38 @@ class BranchInfo(SerializableMixin):
361
362
  # ---------------------------------------------------------------------------
362
363
 
363
364
 
365
+ @dataclass
366
+ class SemanticFingerprint:
367
+ """A semantic node digest returned by the server."""
368
+
369
+ digest: str
370
+ version: int = 1
371
+
372
+ def __post_init__(self) -> None:
373
+ if re.fullmatch(r"[0-9a-f]{64}", self.digest) is None:
374
+ raise ValueError(
375
+ "Semantic fingerprint digest must be 64 lowercase hexadecimal characters",
376
+ )
377
+
378
+ @classmethod
379
+ def from_dict(cls, d: dict) -> SemanticFingerprint:
380
+ return cls(
381
+ version=d.get("version", 1),
382
+ digest=d.get("digest", ""),
383
+ )
384
+
385
+
386
+ SemanticFingerprintValue: TypeAlias = SemanticFingerprint | Literal["unknown"]
387
+
388
+
389
+ def _parse_semantic_fingerprint(value: Any) -> SemanticFingerprintValue | None:
390
+ if value is None or value == "unknown":
391
+ return value
392
+ if not isinstance(value, dict):
393
+ raise ValueError("Semantic fingerprint must be an object or 'unknown'")
394
+ return SemanticFingerprint.from_dict(value)
395
+
396
+
364
397
  @dataclass
365
398
  class DeploymentResult:
366
399
  """A single node-level result within a deployment."""
@@ -370,15 +403,22 @@ class DeploymentResult:
370
403
  status: str
371
404
  message: str = ""
372
405
  changed_fields: list[str] = field(default_factory=list)
406
+ deploy_type: str = ""
407
+ change_tier: Literal["none", "minor", "major"] | None = None
408
+ semantic_fingerprint: SemanticFingerprintValue | None = None
373
409
 
374
410
  @classmethod
375
411
  def from_dict(cls, d: dict) -> DeploymentResult:
412
+ fingerprint = d.get("semantic_fingerprint")
376
413
  return cls(
377
414
  name=d.get("name", ""),
378
415
  operation=d.get("operation", ""),
379
416
  status=d.get("status", ""),
380
417
  message=d.get("message", ""),
381
418
  changed_fields=d.get("changed_fields") or [],
419
+ deploy_type=d.get("deploy_type", ""),
420
+ change_tier=d.get("change_tier"),
421
+ semantic_fingerprint=_parse_semantic_fingerprint(fingerprint),
382
422
  )
383
423
 
384
424
 
@@ -392,9 +432,11 @@ class DownstreamImpact:
392
432
  caused_by: list[str] = field(default_factory=list)
393
433
  depth: int = 0
394
434
  impact_type: str = ""
435
+ semantic_fingerprint: SemanticFingerprintValue | None = None
395
436
 
396
437
  @classmethod
397
438
  def from_dict(cls, d: dict) -> DownstreamImpact:
439
+ fingerprint = d.get("semantic_fingerprint")
398
440
  return cls(
399
441
  name=d.get("name", ""),
400
442
  node_type=d.get("node_type", ""),
@@ -402,6 +444,7 @@ class DownstreamImpact:
402
444
  caused_by=d.get("caused_by") or [],
403
445
  depth=d.get("depth", 0),
404
446
  impact_type=d.get("impact_type", ""),
447
+ semantic_fingerprint=_parse_semantic_fingerprint(fingerprint),
405
448
  )
406
449
 
407
450
 
@@ -1,22 +1,23 @@
1
1
  import importlib.metadata
2
2
  import io
3
3
  import json
4
- from pathlib import Path
5
4
  import time
6
5
  import zipfile
6
+ from pathlib import Path
7
7
  from unittest import mock
8
- import pytest
9
8
  from unittest.mock import MagicMock, patch
9
+
10
+ import pytest
11
+ import yaml
10
12
  from datajunction.deployment import DeploymentService
11
13
  from datajunction.exceptions import DJClientException, DJDeploymentFailure
12
- from datajunction.models import DeploymentInfo
14
+ from datajunction.models import DeploymentInfo, SemanticFingerprint
13
15
  from datajunction.rendering import (
14
16
  _render_error_bullets,
15
17
  _strip_summary_lines,
16
18
  print_deployment_header,
17
19
  print_results,
18
20
  )
19
- import yaml
20
21
  from rich.console import Console
21
22
 
22
23
 
@@ -1401,24 +1402,41 @@ class TestGetImpact:
1401
1402
  monkeypatch.delenv("DJ_DEPLOY_REPO", raising=False)
1402
1403
 
1403
1404
  # Create mock client
1404
- mock_client = MagicMock()
1405
- mock_client.get_deployment_impact.return_value = {
1405
+ response = {
1406
1406
  "uuid": "dry_run",
1407
1407
  "namespace": "test.ns",
1408
1408
  "status": "success",
1409
1409
  "results": [
1410
1410
  {
1411
1411
  "name": "test.ns.my_node",
1412
+ "deploy_type": "node",
1412
1413
  "operation": "noop",
1413
1414
  "status": "success",
1414
1415
  "message": "",
1416
+ "change_tier": "none",
1417
+ "semantic_fingerprint": {
1418
+ "version": 1,
1419
+ "digest": "a" * 64,
1420
+ },
1421
+ },
1422
+ ],
1423
+ "downstream_impacts": [
1424
+ {
1425
+ "name": "external.metric",
1426
+ "node_type": "metric",
1427
+ "predicted_status": "valid",
1428
+ "semantic_fingerprint": {
1429
+ "version": 1,
1430
+ "digest": "b" * 64,
1431
+ },
1415
1432
  },
1416
1433
  ],
1417
- "downstream_impacts": [],
1418
1434
  }
1435
+ mock_client = MagicMock()
1436
+ mock_client.get_deployment_impact.return_value = response
1419
1437
 
1420
1438
  svc = DeploymentService(mock_client)
1421
- result = svc.get_impact(tmp_path)
1439
+ result = svc.get_impact(tmp_path, display=False)
1422
1440
 
1423
1441
  # Verify the API was called
1424
1442
  mock_client.get_deployment_impact.assert_called_once()
@@ -1426,9 +1444,84 @@ class TestGetImpact:
1426
1444
  assert call_args["namespace"] == "test.ns"
1427
1445
  assert "nodes" in call_args
1428
1446
 
1429
- # Verify the result is returned
1430
- assert result["namespace"] == "test.ns"
1431
- assert result["uuid"] == "dry_run"
1447
+ assert result is response
1448
+ parsed = DeploymentInfo.from_dict(result)
1449
+ assert parsed.results[0].deploy_type == "node"
1450
+ assert parsed.results[0].change_tier == "none"
1451
+ assert parsed.results[0].semantic_fingerprint == SemanticFingerprint(
1452
+ digest="a" * 64,
1453
+ )
1454
+ assert parsed.downstream_impacts[0].semantic_fingerprint == SemanticFingerprint(
1455
+ digest="b" * 64,
1456
+ )
1457
+
1458
+ def test_deployment_info_parses_unknown_fingerprints(self):
1459
+ parsed = DeploymentInfo.from_dict(
1460
+ {
1461
+ "uuid": "dry_run",
1462
+ "namespace": "test.ns",
1463
+ "status": "success",
1464
+ "results": [{"semantic_fingerprint": "unknown"}],
1465
+ "downstream_impacts": [{"semantic_fingerprint": "unknown"}],
1466
+ },
1467
+ )
1468
+
1469
+ assert parsed.results[0].semantic_fingerprint == "unknown"
1470
+ assert parsed.downstream_impacts[0].semantic_fingerprint == "unknown"
1471
+
1472
+ with pytest.raises(
1473
+ ValueError,
1474
+ match="Semantic fingerprint must be an object or 'unknown'",
1475
+ ):
1476
+ DeploymentInfo.from_dict(
1477
+ {
1478
+ "results": [{"semantic_fingerprint": "invalid"}],
1479
+ },
1480
+ )
1481
+
1482
+ def test_deployment_info_parses_older_impact_response(self):
1483
+ parsed = DeploymentInfo.from_dict(
1484
+ {
1485
+ "uuid": "dry_run",
1486
+ "namespace": "test.ns",
1487
+ "status": "success",
1488
+ "results": [
1489
+ {
1490
+ "name": "test.ns.my_node",
1491
+ "operation": "noop",
1492
+ "status": "skipped",
1493
+ },
1494
+ ],
1495
+ },
1496
+ )
1497
+ assert parsed.results[0].deploy_type == ""
1498
+ assert parsed.results[0].change_tier is None
1499
+ assert parsed.results[0].semantic_fingerprint is None
1500
+ assert parsed.downstream_impacts == []
1501
+
1502
+ @pytest.mark.parametrize("digest", ["a" * 63, "A" * 64, "g" * 64])
1503
+ def test_semantic_fingerprint_rejects_invalid_digest(self, digest):
1504
+ with pytest.raises(ValueError, match="64 lowercase hexadecimal"):
1505
+ SemanticFingerprint(digest=digest)
1506
+
1507
+ def test_semantic_fingerprint_preserves_unknown_version(self):
1508
+ parsed = DeploymentInfo.from_dict(
1509
+ {
1510
+ "results": [
1511
+ {
1512
+ "semantic_fingerprint": {
1513
+ "version": 2,
1514
+ "digest": "a" * 64,
1515
+ },
1516
+ },
1517
+ ],
1518
+ },
1519
+ )
1520
+
1521
+ assert parsed.results[0].semantic_fingerprint == SemanticFingerprint(
1522
+ version=2,
1523
+ digest="a" * 64,
1524
+ )
1432
1525
 
1433
1526
  def test_get_impact_with_namespace_override(self, tmp_path, monkeypatch):
1434
1527
  """get_impact should respect namespace override."""
File without changes
File without changes
File without changes
File without changes