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