tango-python 0.5.0__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. tango_python-1.0.0/.github/workflows/docs-dispatch.yml +58 -0
  2. {tango_python-0.5.0 → tango_python-1.0.0}/.gitignore +25 -1
  3. tango_python-1.0.0/CHANGELOG.md +177 -0
  4. {tango_python-0.5.0 → tango_python-1.0.0}/PKG-INFO +96 -18
  5. {tango_python-0.5.0 → tango_python-1.0.0}/README.md +93 -17
  6. {tango_python-0.5.0 → tango_python-1.0.0}/docs/API_REFERENCE.md +511 -75
  7. tango_python-1.0.0/docs/CLIENT.md +73 -0
  8. tango_python-1.0.0/docs/DYNAMIC_MODELS.md +205 -0
  9. tango_python-1.0.0/docs/ERRORS.md +158 -0
  10. tango_python-1.0.0/docs/PAGINATION.md +112 -0
  11. {tango_python-0.5.0 → tango_python-1.0.0}/docs/SHAPES.md +35 -29
  12. tango_python-1.0.0/docs/WEBHOOKS.md +449 -0
  13. {tango_python-0.5.0 → tango_python-1.0.0}/pyproject.toml +39 -1
  14. tango_python-1.0.0/scripts/smoke_api_parity.py +397 -0
  15. {tango_python-0.5.0 → tango_python-1.0.0}/tango/__init__.py +32 -5
  16. {tango_python-0.5.0 → tango_python-1.0.0}/tango/client.py +1085 -144
  17. {tango_python-0.5.0 → tango_python-1.0.0}/tango/models.py +156 -30
  18. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/explicit_schemas.py +318 -23
  19. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/parser.py +60 -0
  20. tango_python-1.0.0/tango/webhooks/__init__.py +27 -0
  21. tango_python-1.0.0/tango/webhooks/cli.py +427 -0
  22. tango_python-1.0.0/tango/webhooks/receiver.py +239 -0
  23. tango_python-1.0.0/tango/webhooks/signing.py +63 -0
  24. tango_python-1.0.0/tango/webhooks/simulate.py +103 -0
  25. tango_python-1.0.0/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +161 -0
  26. tango_python-1.0.0/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_with_metrics_expansion +158 -0
  27. tango_python-1.0.0/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +161 -0
  28. tango_python-1.0.0/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_orders_uses_default_shape +184 -0
  29. tango_python-1.0.0/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +150 -0
  30. tango_python-1.0.0/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_with_ordering +78 -0
  31. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_itdashboard_integration.py +6 -18
  32. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_vehicles_idvs_integration.py +156 -11
  33. {tango_python-0.5.0 → tango_python-1.0.0}/tests/production/test_production_smoke.py +23 -8
  34. tango_python-1.0.0/tests/test_api_parity.py +474 -0
  35. {tango_python-0.5.0 → tango_python-1.0.0}/tests/test_client.py +6 -112
  36. {tango_python-0.5.0 → tango_python-1.0.0}/tests/test_shapes.py +136 -0
  37. tango_python-1.0.0/tests/test_webhooks_cli.py +279 -0
  38. tango_python-1.0.0/tests/test_webhooks_receiver.py +128 -0
  39. tango_python-1.0.0/tests/test_webhooks_signing.py +95 -0
  40. tango_python-1.0.0/tests/test_webhooks_simulate.py +62 -0
  41. tango_python-0.5.0/.env.example +0 -16
  42. tango_python-0.5.0/CHANGELOG.md +0 -78
  43. tango_python-0.5.0/ROADMAP.md +0 -22
  44. tango_python-0.5.0/docs/DEVELOPERS.md +0 -666
  45. tango_python-0.5.0/docs/quick_start.ipynb +0 -941
  46. tango_python-0.5.0/tests/cassettes/TestVehiclesIntegration.test_get_vehicle_supports_joiner_and_flat_lists +0 -152
  47. tango_python-0.5.0/tests/cassettes/TestVehiclesIntegration.test_list_vehicle_awardees_uses_default_shape +0 -169
  48. tango_python-0.5.0/tests/cassettes/TestVehiclesIntegration.test_list_vehicles_uses_default_shape_and_search +0 -86
  49. tango_python-0.5.0/uv.lock +0 -2209
  50. {tango_python-0.5.0 → tango_python-1.0.0}/.github/workflows/lint.yml +0 -0
  51. {tango_python-0.5.0 → tango_python-1.0.0}/.github/workflows/publish.yml +0 -0
  52. {tango_python-0.5.0 → tango_python-1.0.0}/.github/workflows/test.yml +0 -0
  53. {tango_python-0.5.0 → tango_python-1.0.0}/LICENSE +0 -0
  54. {tango_python-0.5.0 → tango_python-1.0.0}/scripts/README.md +0 -0
  55. {tango_python-0.5.0 → tango_python-1.0.0}/scripts/check_filter_shape_conformance.py +0 -0
  56. {tango_python-0.5.0 → tango_python-1.0.0}/scripts/fetch_api_schema.py +0 -0
  57. {tango_python-0.5.0 → tango_python-1.0.0}/scripts/generate_schemas_from_api.py +0 -0
  58. {tango_python-0.5.0 → tango_python-1.0.0}/scripts/pr_review.py +0 -0
  59. {tango_python-0.5.0 → tango_python-1.0.0}/scripts/test_production.py +0 -0
  60. {tango_python-0.5.0 → tango_python-1.0.0}/tango/exceptions.py +0 -0
  61. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/__init__.py +0 -0
  62. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/factory.py +0 -0
  63. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/generator.py +0 -0
  64. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/models.py +0 -0
  65. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/schema.py +0 -0
  66. {tango_python-0.5.0 → tango_python-1.0.0}/tango/shapes/types.py +0 -0
  67. {tango_python-0.5.0 → tango_python-1.0.0}/tests/__init__.py +0 -0
  68. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestAgenciesIntegration.test_get_agency +0 -0
  69. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestAgenciesIntegration.test_list_agencies +0 -0
  70. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_field_type_validation +0 -0
  71. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestBusinessTypesIntegration.test_business_type_parsing_consistency +0 -0
  72. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestBusinessTypesIntegration.test_list_business_types +0 -0
  73. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_combined_filters_work_together +0 -0
  74. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_contract_cursor_pagination +0 -0
  75. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_contract_data_object_parsing +0 -0
  76. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_contract_field_types +0 -0
  77. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[keyword-software] +0 -0
  78. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_filter_parameter_mappings[psc_code-R425] +0 -0
  79. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_awarding_agency_filter +0 -0
  80. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_date_range_filter +0 -0
  81. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_flat +0 -0
  82. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_naics_code_filter +0 -0
  83. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[custom-key,piid,recipient(display_name),total_contract_value,award_date] +0 -0
  84. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[default-None] +0 -0
  85. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[detailed-key,piid,award_date,description,total_contract_value,obligated,fiscal_year,set_aside,recipient(display_name,uei),awarding_office(-),place_of_performa...ce114a3c47e2037aaa3c15d00b7031bd +0 -0
  86. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_list_contracts_with_shapes[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  87. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_new_expiring_filters +0 -0
  88. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_new_fiscal_year_range_filters +0 -0
  89. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_new_identifier_filters +0 -0
  90. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_search_contracts_with_filters +0 -0
  91. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_search_filters_object_with_new_parameters +0 -0
  92. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[asc-] +0 -0
  93. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestContractsIntegration.test_sort_and_order_mapped_to_ordering[desc--] +0 -0
  94. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_contracts +0 -0
  95. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_api_schema_stability_detection_entities +0 -0
  96. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_date_field_parsing_edge_cases +0 -0
  97. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_decimal_field_parsing_edge_cases +0 -0
  98. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_empty_list_responses +0 -0
  99. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_entity_parsing_with_various_address_formats +0 -0
  100. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_flattened_responses_with_flat_lists +0 -0
  101. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_list_field_parsing_consistency +0 -0
  102. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_nested_objects_with_missing_data +0 -0
  103. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_null_missing_fields_in_contracts +0 -0
  104. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEdgeCasesIntegration.test_parsing_with_minimal_shape_sparse_data +0 -0
  105. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_field_types +0 -0
  106. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_location_parsing +0 -0
  107. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_parsing_with_business_types +0 -0
  108. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_entity_with_various_identifiers +0 -0
  109. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_get_entity_by_uei +0 -0
  110. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_flat +0 -0
  111. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_search +0 -0
  112. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[comprehensive-uei,legal_business_name,dba_name,cage_code,business_types,primary_naics,naics_codes,psc_codes,email_address,entity_url,description,capabilities,ke...1603a7d52e211cf2b3bc7d32080238aa +0 -0
  113. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[custom-uei,legal_business_name,cage_code] +0 -0
  114. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  115. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestEntitiesIntegration.test_list_entities_with_shapes[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  116. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestForecastsIntegration.test_forecast_field_types +0 -0
  117. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[custom-id,title,anticipated_award_date] +0 -0
  118. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[default-None] +0 -0
  119. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[detailed-id,source_system,external_id,title,description,anticipated_award_date,fiscal_year,naics_code,status,is_active] +0 -0
  120. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestForecastsIntegration.test_list_forecasts_with_shapes[minimal-id,title,anticipated_award_date,fiscal_year,naics_code,status] +0 -0
  121. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_grant_field_types +0 -0
  122. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_grant_pagination +0 -0
  123. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[custom-grant_id,title,opportunity_number] +0 -0
  124. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[default-None] +0 -0
  125. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[detailed-grant_id,opportunity_number,title,status(-),agency_code,description,last_updated,cfda_numbers(number,title),applicant_types(-),funding_categories(-)] +0 -0
  126. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestGrantsIntegration.test_list_grants_with_shapes[minimal-grant_id,opportunity_number,title,status(-),agency_code] +0 -0
  127. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_get_idv_uses_default_shape +0 -0
  128. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_awards_uses_default_shape +0 -0
  129. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_child_idvs_uses_default_shape +0 -0
  130. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idv_transactions +0 -0
  131. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestIDVsIntegration.test_list_idvs_uses_default_shape_and_keyset_params +0 -0
  132. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_code +0 -0
  133. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_agency_name_text +0 -0
  134. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating +0 -0
  135. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_cio_rating_max +0 -0
  136. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_performance_risk +0 -0
  137. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_type_of_investment +0 -0
  138. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_filter_by_updated_time_range +0 -0
  139. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_funding_and_cio_evaluation_expansions +0 -0
  140. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_get_itdashboard_investment_by_uii +0 -0
  141. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_itdashboard_pagination +0 -0
  142. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_search +0 -0
  143. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[custom-uii,agency_name,investment_title,updated_time] +0 -0
  144. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[default-None] +0 -0
  145. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestITDashboardIntegration.test_list_itdashboard_investments_with_shapes[minimal-uii,agency_name,bureau_name,investment_title,type_of_investment,part_of_it_portfolio,updated_time,url] +0 -0
  146. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNaicsIntegration.test_list_naics +0 -0
  147. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[custom-notice_id,title,solicitation_number] +0 -0
  148. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[default-None] +0 -0
  149. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[detailed-notice_id,title,description,solicitation_number,posted_date,naics_code,set_aside,office(-),place_of_performance(-)] +0 -0
  150. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_list_notices_with_shapes[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  151. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_notice_field_types +0 -0
  152. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_notice_pagination +0 -0
  153. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestNoticesIntegration.test_notice_with_meta_fields +0 -0
  154. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOTAsIntegration.test_get_ota +0 -0
  155. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOTAsIntegration.test_list_otas +0 -0
  156. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOTIDVsIntegration.test_get_otidv +0 -0
  157. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOTIDVsIntegration.test_list_otidvs +0 -0
  158. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOfficesIntegration.test_get_office +0 -0
  159. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOfficesIntegration.test_list_offices +0 -0
  160. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[custom-opportunity_id,title,solicitation_number] +0 -0
  161. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[default-None] +0 -0
  162. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[detailed-opportunity_id,title,description,solicitation_number,response_deadline,first_notice_date,last_notice_date,active,naics_code,psc_code,set_asid...23b6b4502ddd665b7184afcff6c6d8d9 +0 -0
  163. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_list_opportunities_with_shapes[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  164. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOpportunitiesIntegration.test_opportunity_field_types +0 -0
  165. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOrganizationsIntegration.test_get_organization +0 -0
  166. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestOrganizationsIntegration.test_list_organizations +0 -0
  167. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_get_protest_by_case_id +0 -0
  168. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_filter +0 -0
  169. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[custom-case_id,title,source_system,outcome] +0 -0
  170. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[default-None] +0 -0
  171. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[minimal-case_id,case_number,title,source_system,outcome,filed_date] +0 -0
  172. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_list_protests_with_shapes[with_dockets-case_id,case_number,title,outcome,filed_date,dockets(docket_number,filed_date,outcome)] +0 -0
  173. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestProtestsIntegration.test_protest_pagination +0 -0
  174. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestSubawardsIntegration.test_list_subawards +0 -0
  175. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[custom-key,piid,description] +0 -0
  176. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[minimal-key,piid,award_date,recipient(display_name),description,total_contract_value] +0 -0
  177. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_contracts_dict_access[ultra_minimal-key,piid,recipient(display_name),total_contract_value] +0 -0
  178. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[minimal-uei,legal_business_name,cage_code,business_types] +0 -0
  179. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_entities_dict_access[with_address-uei,legal_business_name,cage_code,business_types,physical_address] +0 -0
  180. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[detailed-notice_id,title,description,solicitation_number,posted_date,naics_code,set_aside,office(-),place_of_performance(-)] +0 -0
  181. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_notices_dict_access[minimal-notice_id,title,solicitation_number,posted_date] +0 -0
  182. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[detailed-opportunity_id,title,description,solicitation_number,response_deadline,first_notice_date,last_notice_date,active,naics_code,psc_code,set_aside,sam_url,office(-),place_of_performance(-)] +0 -0
  183. {tango_python-0.5.0 → tango_python-1.0.0}/tests/cassettes/TestTypeHintsIntegration.test_opportunities_dict_access[minimal-opportunity_id,title,solicitation_number,response_deadline,active] +0 -0
  184. {tango_python-0.5.0 → tango_python-1.0.0}/tests/conftest.py +0 -0
  185. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/README.md +0 -0
  186. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/__init__.py +0 -0
  187. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/conftest.py +0 -0
  188. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_agencies_integration.py +0 -0
  189. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_contracts_integration.py +0 -0
  190. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_edge_cases_integration.py +0 -0
  191. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_entities_integration.py +0 -0
  192. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_forecasts_integration.py +0 -0
  193. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_grants_integration.py +0 -0
  194. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_naics_integration.py +0 -0
  195. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_notices_integration.py +0 -0
  196. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_offices_integration.py +0 -0
  197. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_opportunities_integration.py +0 -0
  198. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_organizations_integration.py +0 -0
  199. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_otas_otidvs_integration.py +0 -0
  200. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_protests_integration.py +0 -0
  201. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_reference_data_integration.py +0 -0
  202. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/test_subawards_integration.py +0 -0
  203. {tango_python-0.5.0 → tango_python-1.0.0}/tests/integration/validation.py +0 -0
  204. {tango_python-0.5.0 → tango_python-1.0.0}/tests/production/__init__.py +0 -0
  205. {tango_python-0.5.0 → tango_python-1.0.0}/tests/production/conftest.py +0 -0
  206. {tango_python-0.5.0 → tango_python-1.0.0}/tests/test_models.py +0 -0
@@ -0,0 +1,58 @@
1
+ name: Docs dispatch
2
+
3
+ # Fires on push to main when content that affects the published docs site
4
+ # changes (docs/, README, or CHANGELOG). Notifies makegov/docs via
5
+ # repository_dispatch so the docs site rebuilds without waiting for someone
6
+ # to push to the composer.
7
+ #
8
+ # tango-python is a `coloc-source` repo: its docs/ folder is the authoritative
9
+ # source for the Python SDK pages on docs.makegov.com (see makegov/docs#15).
10
+ #
11
+ # Required secrets:
12
+ # DOCS_DISPATCH_TOKEN — GitHub token with contents:write on makegov/docs.
13
+ #
14
+ # Required variables (optional):
15
+ # DOCS_TARGET_REPO — override the dispatch target (default: makegov/docs).
16
+
17
+ on:
18
+ push:
19
+ branches:
20
+ - main
21
+ paths:
22
+ - "docs/**"
23
+ - "README.md"
24
+ - "CHANGELOG.md"
25
+ workflow_dispatch:
26
+
27
+ jobs:
28
+ dispatch:
29
+ runs-on: ubuntu-latest
30
+ permissions:
31
+ contents: read
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ with:
35
+ fetch-depth: 2
36
+
37
+ - name: Detect changed paths
38
+ id: changes
39
+ run: |
40
+ set -euo pipefail
41
+ base=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD)
42
+ external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' | paste -sd, -)
43
+ {
44
+ echo "external=$external"
45
+ echo "has_external=$([ -n "$external" ] && echo true || echo false)"
46
+ } >> "$GITHUB_OUTPUT"
47
+
48
+ - name: Dispatch to docs composer (makegov/docs)
49
+ if: steps.changes.outputs.has_external == 'true'
50
+ env:
51
+ GH_TOKEN: ${{ secrets.DOCS_DISPATCH_TOKEN }}
52
+ TARGET: ${{ vars.DOCS_TARGET_REPO || 'makegov/docs' }}
53
+ run: |
54
+ gh api "repos/$TARGET/dispatches" \
55
+ -f event_type=external_updated \
56
+ -f "client_payload[source_repo]=${{ github.repository }}" \
57
+ -f "client_payload[source_ref]=${{ github.sha }}" \
58
+ -f "client_payload[changed_paths]=${{ steps.changes.outputs.external }}"
@@ -149,4 +149,28 @@ Thumbs.db
149
149
  yoni/
150
150
  .cursor/*
151
151
 
152
- examples/
152
+ examples/
153
+
154
+ .claude-plugin/
155
+ CLAUDE.md
156
+ # >>> mg-tools >>>
157
+ # --- mg-tools (per-developer; re-run 'mg-tools install' after clone) ---
158
+ .claude-plugin/plugin-pointer.json
159
+ .claude/settings.json
160
+ .claude/settings.local.json
161
+ .claude/mg-tools-integration.md
162
+ .mcp.json
163
+ .mcp.disabled.json
164
+ .mg-tools/
165
+ .claude/worktrees/
166
+ .claude/scheduled_tasks.lock
167
+ .claude.bak.*
168
+ .cursor.bak.*
169
+ .mcp.json.bak.*
170
+ # Local IDE/editor config files
171
+ .vscode/
172
+ *.code-workspace
173
+ .zed/
174
+ .idea/
175
+ # <<< mg-tools <<<
176
+ >>>>>>> Stashed changes
@@ -0,0 +1,177 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2026-05-13
11
+
12
+ > First stable release. `tango-python` is now at full API parity with the
13
+ > Tango HTTP surface, the legacy subject-based webhook subscription
14
+ > mechanism has been removed in favor of filter alerts, the shape parser
15
+ > agrees byte-for-byte with the server's expand-alias handling, and the
16
+ > SDK's docs are auto-published to `docs.makegov.com/sdks/python/` via the
17
+ > composer pipeline (makegov/docs#15 / makegov/docs#16). From `1.x` on,
18
+ > we'll only do breaking changes on a major bump.
19
+ >
20
+ > Originally tracked as: API parity (PR #25), subject-based webhook
21
+ > removal (PR #27 / issue #2275), shape-validator alias support (PR #28 /
22
+ > issue #2266), and the docs-only content port (makegov/docs#16).
23
+
24
+ ### Added
25
+ - `ordering` parameter on `list_forecasts`, `list_grants`, `list_subawards`, `list_gsa_elibrary_contracts`, and `list_opportunities`. Prefix with `-` for descending. Closes a parity gap with the API surface (these endpoints all accept `?ordering=` server-side).
26
+ - `create_webhook_endpoint` accepts `name=` (keyword-only) and now **requires** it. The Tango API enforces unique `(user, name)` on endpoints; omitting `name` returns a 400 server-side, so the SDK raises `TangoValidationError` client-side instead of round-tripping. (0.7.0 — never publicly released — emitted a `DeprecationWarning` instead.)
27
+ - `update_webhook_endpoint` accepts `name=` for renaming an endpoint.
28
+ - Webhook alerts (filter subscriptions): `list_webhook_alerts`, `get_webhook_alert`, `create_webhook_alert`, `update_webhook_alert`, `delete_webhook_alert` — the canonical write surface over `/api/webhooks/alerts/`. New `WebhookAlert` dataclass exported from the top-level package.
29
+ - `resolve(name, target_type, ...)` — POST `/api/resolve/` to rank entity / organization candidates from a free-text name. Returns `ResolveResult` with `ResolveCandidate` entries (both exported).
30
+ - `validate(identifier_type, value)` — POST `/api/validate/` to validate the format of a PIID, solicitation number, or UEI. Returns `ValidateResult` (exported).
31
+ - Reference data: `list_departments`, `get_department`, `list_psc`, `get_psc`, `get_psc_metrics`, `get_naics`, `get_naics_metrics`, `get_business_type`, `list_assistance_listings`, `get_assistance_listing`, `list_mas_sins`, `get_mas_sin`.
32
+ - Entity sub-resources: `list_entity_contracts`, `list_entity_idvs`, `list_entity_otas`, `list_entity_otidvs`, `list_entity_subawards`, `list_entity_lcats`, `get_entity_metrics`. All shape-aware where the underlying endpoint supports shaping.
33
+ - IDV sub-resources: `list_idv_lcats`.
34
+ - Agency sub-resources: `list_agency_awarding_contracts`, `list_agency_funding_contracts`.
35
+ - Misc: `search_opportunity_attachments(q, top_k, include_extracted_text)` for `/api/opportunities/attachment-search/`; `get_version()` for `/api/version/`; `list_api_keys()` for `/api/api-keys/`.
36
+
37
+ ### Changed
38
+ - `create_webhook_alert` accepts `endpoint=` (keyword-only). Required for accounts with multiple webhook endpoints; auto-resolves for single-endpoint accounts. Closes the multi-endpoint smoke-test gap (tango#2256).
39
+ - `test_webhook_delivery` now sends the canonical `endpoint` body key instead of the deprecated `endpoint_id` alias (tango#2252). The Python kwarg name stays `endpoint_id=` for backwards compatibility; the wire payload is what changed.
40
+ - **`generate_signature(body, secret)` now returns the full wire form `"sha256=<hex>"`** instead of bare hex. Callers can assign the return value directly to the `X-Tango-Signature` header without wrapping in a format string. This is a breaking change for code that relied on the bare-hex return; pass it through `parse_signature_header()` to recover the previous form. `verify_signature` accepts both prefixed and bare-hex inputs (unchanged), so receivers continue to work either way.
41
+
42
+ ### Removed
43
+ - **Subject-based webhook subscription surface** (tango#2275). Migrate to `create_webhook_alert(...)` and the alerts API.
44
+ - Methods: `list_webhook_subscriptions`, `get_webhook_subscription`, `create_webhook_subscription`, `update_webhook_subscription`, `delete_webhook_subscription`.
45
+ - Dataclasses: `WebhookSubscription`, `WebhookSubjectTypeDefinition`. Both are no longer exported from the top-level `tango` package — importing them raises `ImportError`.
46
+ - Fields: `default_subject_type` removed from `WebhookEventType`; `subject_types` and `subject_type_definitions` removed from `WebhookEventTypesResponse`. The server's `/api/webhooks/event-types/` response no longer carries these.
47
+ - CLI: the entire `tango webhooks subscriptions` Click subgroup (`list` / `get` / `create` / `delete`). Use the SDK's `client.create_webhook_alert(...)` etc. directly — there is no CLI subgroup for alerts.
48
+ - `ordering` kwarg from `list_notices` and `list_protests`. The notices and protests viewsets reject every `?ordering=` value at runtime (tango#2254); the kwarg silently sent unsupported values. Other five list methods retain `ordering`.
49
+
50
+ ### Fixed
51
+ - `TangoClient._post()` and `_patch()` accept both `json_data=` (positional) and `json=` (keyword) for backward compatibility. Internal callers and docs examples that use `json=` no longer fail with `TypeError`. Passing **both** now raises `TangoValidationError` rather than silently preferring one — that ambiguity would hide caller bugs.
52
+ - `get_psc_metrics` / `get_naics_metrics` / `get_entity_metrics` docstrings — `period_grouping` values are `"month"` / `"quarter"` / `"year"` (the path-segment values the API accepts), not `"monthly"` / `"quarterly"`.
53
+ - `docs/API_REFERENCE.md#get_agency` — example uses `client.get_agency("GSA")` consistently and notes the parameter accepts CGAC / FPDS / short code / abbreviation / canonical name.
54
+ - `README.md` Quick Start — `get_agency()` returns an `Agency` dataclass, so the example uses attribute access (`agency.name`) instead of `agency['name']` which would `TypeError`.
55
+ - `scripts/smoke_api_parity.py` — `list_business_types(limit=1)` is now wrapped in the `run(...)` helper so a failure on that call records FAIL instead of aborting the smoke run.
56
+ - `tango webhooks endpoints create` CLI now accepts and requires `--name` (passed through to `create_webhook_endpoint(name=...)`). Previously the option was absent, meaning the CLI could never set a custom endpoint name and every call would 400 server-side (the server enforces `unique(user, name)`).
57
+ - `WebhookAlert.query_type` and `WebhookAlert.filters` tightened from `Optional` to non-optional (`str` and `dict[str, Any]` respectively). Legacy nullable rows were purged by the tango#2275 migration; the server model and serializer guarantee non-null values for all current data. `WebhookAlert.status` narrowed from `str` to `Literal["active", "paused"]` — the server serializer produces exactly those two values.
58
+ - **Shape validator agrees with server on `naics(...)` / `psc(...)` expansions.** The client-side `ShapeParser.validate()` previously rejected the canonical `shape=naics(code,description)` form (which the server has always accepted) and also rejected the alias `shape=naics_code(code,description)`. The parser now mirrors the server's `_EXPAND_ALIASES` (introduced in Tango PR makegov/tango#2259) and rewrites `naics_code(...)` / `psc_code(...)` to their canonical `naics(...)` / `psc(...)` form at parse time. Bare scalar leaves (`shape=naics_code` / `shape=psc_code`) are left untouched and still return the raw column value, matching the server. Schemas for `Contract`, `Forecast`, `Opportunity`, `Notice`, and `Vehicle` gained explicit `naics` / `psc` expand entries backed by the existing `CodeDescription` nested model. Fixes makegov/tango#2266.
59
+ - **`Subaward` schema matches the server's `SubawardSerializer`.** The previous `SUBAWARD_SCHEMA` declared two fields the server has never exposed (`id`, `amount`) and was missing every real field on the resource — including `piid`, `key`, `awarding_office` / `funding_office` / `place_of_performance` / `subaward_details` / `fsrs_details` / `highly_compensated_officers` / `usaspending_permalink`, and the denormalized `prime_awardee_*` / `recipient_*` lookup columns. Shape strings that referenced any real field (e.g. `shape="piid"`) would fail client-side validation with `unknown_field`, and conversely the SDK happily passed `shape="id"` / `shape="amount"` through to the server, where they were rejected. `SUBAWARD_SCHEMA` is now derived directly from `awards.serializers.subawards.SubawardSerializer` and the resource's runtime `available_fields`. The `Subaward` dataclass in `tango/models.py` was updated to match. New nested schemas `SubawardDetails`, `FsrsDetails`, `SubawardPlaceOfPerformance`, and `HighlyCompensatedOfficer` are registered so the corresponding shape expansions validate end-to-end.
60
+
61
+ ### Documentation
62
+ - New `docs/ERRORS.md` — full exception hierarchy, recovery patterns, and the shape-error classes (`ShapeValidationError`, `ShapeParseError`, `TypeGenerationError`, `ModelInstantiationError`). Ported from `docs.makegov.com/sdks/python/errors.md` ahead of the docs-site auto-pull cutover (makegov/docs#15 / makegov/docs#16).
63
+ - New `docs/PAGINATION.md` — page-based vs cursor-based strategies, iteration patterns, and the `PaginatedResponse` field reference. Ported from `docs.makegov.com/sdks/python/pagination.md`.
64
+ - New `docs/CLIENT.md` — `TangoClient` constructor reference, `rate_limit_info` / `last_response_headers` properties, and retry-semantics note (the SDK has no built-in retry). Ported from `docs.makegov.com/sdks/python/client.md`.
65
+
66
+ ### CI
67
+ - New `.github/workflows/docs-dispatch.yml` — fires on push to `main` when `docs/**`, `README.md`, or `CHANGELOG.md` changes and dispatches `external_updated` at `makegov/docs` so the public docs site rebuilds with the latest SDK content. Required for the makegov/docs#15 auto-pull pipeline.
68
+
69
+ ## [0.6.0] - 2026-05-07
70
+
71
+ ### Added
72
+ - Vehicles: new top-level fields `program_acronym`, `idv_count`, `total_obligated`, `is_synthetic_solicitation`, `latest_award_date`, `description`, `opportunity_id`.
73
+ - Vehicles: new `metrics(*)` shape expansion bundling 12 computed metrics: `avg_offers_received`, `award_concentration_hhi`, `order_concentration_hhi`, `competed_rate`, `using_agency_count`, `avg_order_value`, `max_order_value`, `top_recipient_share`, `recent_obligations_24mo`, `recent_orders_24mo`, `days_since_last_order`, `obligation_to_ceiling_ratio`. Backed by a new `VehicleMetrics` schema.
74
+ - `list_vehicle_orders(uuid, ...)` for the new `/api/vehicles/{uuid}/orders/` endpoint, returning task orders under the vehicle's IDVs with two-phase pagination.
75
+ - `list_vehicles` gained 21 explicit filter parameters per API 4.3.0: `vehicle_type`, `type_of_idc`, `contract_type`, `set_aside` (multi-value via `|`), `who_can_use`, `naics_code`, `psc_code`, `program_acronym`, `agency`, `organization_id`, `total_obligated_min`/`max`, `idv_count_min`/`max`, `order_count_min`/`max`, `fiscal_year`, `award_date_after`/`before`, `last_date_to_order_after`/`before`.
76
+ - `list_vehicle_awardees` gained a `search` parameter for entity-aware full-text search across IDV fields and recipient entity details (API 4.3.0).
77
+ - `ordering` parameter on `list_vehicles` (whitelist: `vehicle_obligations`, `latest_award_date`, `total_obligated`, `award_date`, `last_date_to_order`, `fiscal_year`, `idv_count`, `order_count`) and on `list_vehicle_orders` (whitelist: `award_date`, `obligated`, `total_contract_value`). Prefix with `-` for descending.
78
+ - `ShapeConfig.VEHICLE_ORDERS_MINIMAL` default for the new orders endpoint.
79
+ - Shaping: New `organization(*)` expand on `Vehicle`, `Forecast`, `Grant`, `ITDashboardInvestment`, and `Protest` schemas — returns the canonical 7-key office payload (`organization_id`, `office_code`, `office_name`, `agency_code`, `agency_name`, `department_code`, `department_name`). Selectable as the bare leaf (`shape=...,organization`) or as a sub-selectable expansion (`shape=...,organization(office_code,...)`).
80
+ - Shaping: New `vehicle(*)` expand on `Contract` — request the parent vehicle inline from `/api/contracts/` (API 4.2.0).
81
+ - `Vehicle` and `VehicleMetrics` are now exported from the top-level `tango` package.
82
+ - `tango.webhooks` subpackage with HMAC-SHA256 signing helpers (`verify_signature`, `generate_signature`, `parse_signature_header`) that mirror the canonical Tango server scheme byte-for-byte. Importable from a default `pip install tango-python` (pure stdlib).
83
+ - `WebhookReceiver`: a stdlib-based local HTTP listener for development and integration tests. Verifies signatures, optionally forwards each delivery to a downstream URL, and records deliveries in memory for inspection. Usable as a context manager (`with WebhookReceiver(secret=...).run() as rx: ...`).
84
+ - `tango.webhooks.simulate.deliver(...)`: locally sign and POST a payload to any URL — no Tango involvement. Useful for offline iteration on receiver code.
85
+ - New `tango[webhooks]` extra (adds `click`) ships a `tango` console script covering the full webhook lifecycle for developer integrations:
86
+ - `listen` — local receiver
87
+ - `simulate` — sign a payload locally; with `--to`, also POST it
88
+ - `trigger` — ask Tango to send a real test delivery
89
+ - `fetch-sample` — print the canonical payload Tango emits for an event type
90
+ - `list-event-types` — discover what's subscribable
91
+ - `endpoints list|get|create|delete` — manage delivery endpoints
92
+ - `subscriptions list|get|create|delete` — manage what events you receive
93
+ Together these let a developer go from zero to receiving real Tango webhooks without leaving the shell or dropping into Python.
94
+
95
+ ### Changed
96
+ - `ShapeConfig.VEHICLES_MINIMAL` and `VEHICLES_COMPREHENSIVE` now include the new top-level fields and the `organization` expansion. `VEHICLES_COMPREHENSIVE` defaults to `metrics(*)` and no longer pulls the deprecated `competition_details(*)` blob.
97
+
98
+ ### Deprecated
99
+ - Vehicles shape fields `agency_details`, `competition_details`, and the `opportunity` expansion. The upstream API now sends a `Deprecation: true` header for these and recomputes them at request time. Explicit use in `shape=...` emits a Python `DeprecationWarning`. Sunset timeline TBD upstream.
100
+
101
+ ### Notes
102
+ - Console script name `tango` may be revisited in a future release if it conflicts with sibling tooling (`tango-scripts` reuses the bare name).
103
+
104
+ ### Documentation
105
+ - New `docs/WEBHOOKS.md` — comprehensive guide covering install, concepts, a zero-to-receiving quickstart, full CLI reference, and programmatic patterns for `WebhookReceiver` / `simulate.sign` / `simulate.deliver` in pytest fixtures.
106
+ - `docs/API_REFERENCE.md`: filled in `get_webhook_subscription`, replaced the hand-rolled signature-verification snippet with a pointer to `tango.webhooks.verify_signature`, and added a new "Webhook tooling (`tango.webhooks`)" section that documents every importable from the new subpackage.
107
+ - `README.md`: new "Webhook Tooling" section under Advanced Features, plus the new guide is linked from the Documentation index.
108
+
109
+ ## [0.5.0] - 2026-04-08
110
+
111
+ ### Added
112
+ - IT Dashboard investments: `list_itdashboard_investments`, `get_itdashboard_investment` (`/api/itdashboard/`) with shaping and filter params (`search`, `agency_code`, `agency_name`, `type_of_investment`, `updated_time_after`, `updated_time_before`, `cio_rating`, `cio_rating_max`, `performance_risk`). Tier-gated by the API: free tier gets `search`, pro adds structured filters, business+ adds CIO/performance analytics. New `ITDashboardInvestment` model and `ShapeConfig.ITDASHBOARD_INVESTMENTS_MINIMAL` / `ITDASHBOARD_INVESTMENTS_COMPREHENSIVE` defaults.
113
+
114
+ ## [0.4.4] - 2026-03-25
115
+
116
+ ### Added
117
+ - `parent_piid` filter parameter on `list_contracts` for filtering orders under a specific parent IDV PIID.
118
+ - `user_agent` and `extra_headers` parameters on `TangoClient` for custom request headers.
119
+ - `TangoClient.last_response_headers` property for accessing full HTTP headers from the most recent API response.
120
+
121
+ ## [0.4.3] - 2026-03-21
122
+
123
+ ### Added
124
+ - `TangoRateLimitError` now exposes `wait_in_seconds`, `detail`, and `limit_type` properties parsed from the API's 429 response body.
125
+ - `RateLimitInfo` dataclass for structured access to rate limit headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and per-window daily/burst variants).
126
+ - `TangoClient.rate_limit_info` property returns rate limit info from the most recent API response.
127
+
128
+ ### Changed
129
+ - `_request` now passes the full 429 response body to `TangoRateLimitError` (previously discarded), enabling callers to access `wait_in_seconds` and the specific limit type that was exceeded.
130
+
131
+ ## [0.4.2] - 2026-03-04
132
+
133
+ ### Added
134
+ - Protests endpoints: `list_protests`, `get_protest` with shaping and filter params (`source_system`, `outcome`, `case_type`, `agency`, `case_number`, `solicitation_number`, `protester`, `filed_date_after`, `filed_date_before`, `decision_date_after`, `decision_date_before`, `search`).
135
+
136
+ ### Changed
137
+ - Lint CI workflow disabled for push/PR (runs only on manual trigger) until the private `makegov/tango` repo is accessible to the workflow.
138
+ - Updated documents to reflect changes since v0.4.0
139
+ - Entities: `ENTITIES_COMPREHENSIVE` now uses `federal_obligations(*)` expansion; the API treats federal obligations as an expansion rather than a plain shape field.
140
+ - Docs: `SHAPES.md` documents `federal_obligations(*)` as an expansion for entity shaping.
141
+ - Integration tests: `test_parsing_nested_objects_with_missing_data` accepts award office fields (`office_code`, `agency_code`, `department_code`) and empty nested objects when the API returns partial data.
142
+
143
+ ### Removed
144
+ - Assistance: `list_assistance` endpoint and all related tests, docs, and references.
145
+ - IDV summaries: `get_idv_summary` and `list_idv_summary_awards` endpoints and related integration tests, cassettes, and API reference section.
146
+
147
+ ## [0.4.1] - 2026-03-03
148
+
149
+ ### Added
150
+ - GSA eLibrary contracts: `list_gsa_elibrary_contracts`, `get_gsa_elibrary_contract` with shaping and filter params (`contract_number`, `key`, `piid`, `schedule`, `search`, `sin`, `uei`).
151
+
152
+ ### Changed
153
+ - Conformance: replaced `**kwargs`/`**filters` with explicit filter parameters on `list_contracts`, `list_idvs`, `list_entities`, `list_forecasts`, `list_grants`, `list_notices`, `list_opportunities` for full filter/shape conformance. Backward compatibility preserved for `list_contracts(filters=SearchFilters(...))`.
154
+
155
+ ## [0.4.0] - 2026-02-24
156
+
157
+ ### Added
158
+ - Offices, Organizations, OTAs, OTIDVs, Subawards, NAICS, and Assistance endpoints.
159
+ - Filter/shape conformance tooling and documentation.
160
+
161
+ ### Changed
162
+ - CI lint workflow runs filter/shape conformance when the manifest is available.
163
+
164
+ ## [0.3.0] - 2026-02-09
165
+
166
+ ### Added
167
+ - Vehicles endpoints: `list_vehicles`, `get_vehicle`, and `list_vehicle_awardees` (supports shaping + flattening). (refs `makegov/tango#1328`)
168
+ - IDV endpoints: `list_idvs`, `get_idv`, `list_idv_awards`, `list_idv_child_idvs`, `list_idv_transactions`, `get_idv_summary`, `list_idv_summary_awards`. (refs `makegov/tango#1328`)
169
+ - Webhooks v2 client support: event type discovery, subscription CRUD, endpoint management, test delivery, and sample payload helpers. (refs `makegov/tango#1274`)
170
+
171
+ ### Changed
172
+ - Expanded explicit schemas to support common IDV shaping expansions (award offices, officers, period of performance, etc.).
173
+ - HTTP client now supports PATCH/DELETE helpers for webhook management endpoints.
174
+
175
+ ## [0.2.0] - 2025-11-16
176
+
177
+ - Entirely refactored SDK
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tango-python
3
- Version: 0.5.0
3
+ Version: 1.0.0
4
4
  Summary: Python SDK for the Tango API
5
5
  Project-URL: Homepage, https://github.com/makegov/tango-python
6
6
  Project-URL: Documentation, https://docs.makegov.com/tango-python
@@ -51,6 +51,8 @@ Requires-Dist: ruff>=0.3.0; extra == 'dev'
51
51
  Provides-Extra: notebooks
52
52
  Requires-Dist: ipykernel>=6.25.0; extra == 'notebooks'
53
53
  Requires-Dist: jupyter>=1.0.0; extra == 'notebooks'
54
+ Provides-Extra: webhooks
55
+ Requires-Dist: click>=8.1; extra == 'webhooks'
54
56
  Description-Content-Type: text/markdown
55
57
 
56
58
  # Tango Python SDK
@@ -94,12 +96,14 @@ print(f"Found {agencies.count} agencies")
94
96
 
95
97
  # Get specific agency
96
98
  agency = client.get_agency("GSA")
97
- print(f"Agency: {agency['name']}")
99
+ print(f"Agency: {agency.name}")
98
100
 
99
101
  # Search contracts
100
102
  contracts = client.list_contracts(
101
103
  limit=10
102
104
  )
105
+ ```
106
+
103
107
  ## Authentication
104
108
 
105
109
  Most endpoints require an API key. You can obtain one from the [Tango API portal](https://tango.makegov.com).
@@ -224,9 +228,14 @@ otidvs = client.list_otidvs(limit=25)
224
228
  ### Vehicles
225
229
 
226
230
  ```python
227
- vehicles = client.list_vehicles(search="GSA schedule", shape=ShapeConfig.VEHICLES_MINIMAL)
231
+ vehicles = client.list_vehicles(
232
+ search="GSA schedule",
233
+ ordering="-vehicle_obligations",
234
+ shape=ShapeConfig.VEHICLES_MINIMAL,
235
+ )
228
236
  vehicle = client.get_vehicle("UUID", shape=ShapeConfig.VEHICLES_COMPREHENSIVE)
229
237
  awardees = client.list_vehicle_awardees("UUID")
238
+ orders = client.list_vehicle_orders("UUID", ordering="-obligated")
230
239
  ```
231
240
 
232
241
  ### Entities (Vendors/Recipients)
@@ -254,13 +263,13 @@ opportunities = client.list_opportunities(agency="DOD", active=True, limit=25)
254
263
  ### Notices
255
264
 
256
265
  ```python
257
- notices = client.list_notices(agency="DOD", notice_type="award", limit=25)
266
+ notices = client.list_notices(agency="DOD", notice_type="Presolicitation", limit=25)
258
267
  ```
259
268
 
260
269
  ### Grants
261
270
 
262
271
  ```python
263
- grants = client.list_grants(agency="HHS", status="forecasted", limit=25)
272
+ grants = client.list_grants(agency="HHS", status="F", limit=25) # F = Forecasted
264
273
  ```
265
274
 
266
275
  ### Protests
@@ -280,12 +289,45 @@ contract = client.get_gsa_elibrary_contract("UUID")
280
289
  ### Reference Data
281
290
 
282
291
  ```python
283
- # Offices, organizations, NAICS, subawards, business types
292
+ # Offices, organizations, NAICS, PSC, subawards, business types
284
293
  offices = client.list_offices(search="acquisitions")
285
294
  organizations = client.list_organizations(level=1)
286
295
  naics = client.list_naics(search="software")
296
+ get_naics = client.get_naics("541511")
297
+ psc = client.list_psc()
287
298
  subawards = client.list_subawards(prime_uei="UEI123")
288
299
  business_types = client.list_business_types()
300
+ mas_sins = client.list_mas_sins()
301
+ assistance = client.list_assistance_listings()
302
+ departments = client.list_departments()
303
+ ```
304
+
305
+ ### Resolve / Validate
306
+
307
+ ```python
308
+ # Resolve a name to entity/org candidates
309
+ result = client.resolve(name="Lockheed Martin", target_type="entity")
310
+ for c in result.candidates:
311
+ print(c.identifier, c.display_name)
312
+
313
+ # Validate an identifier
314
+ result = client.validate(identifier_type="uei", value="ABCDEF123456")
315
+ ```
316
+
317
+ ### IT Dashboard
318
+
319
+ ```python
320
+ investments = client.list_itdashboard_investments(search="cloud", limit=25)
321
+ investment = client.get_itdashboard_investment("023-000001234")
322
+ ```
323
+
324
+ ### Entity Sub-resources
325
+
326
+ ```python
327
+ contracts = client.list_entity_contracts("ABCDEF123456", limit=25)
328
+ idvs = client.list_entity_idvs("ABCDEF123456")
329
+ otas = client.list_entity_otas("ABCDEF123456")
330
+ metrics = client.get_entity_metrics("ABCDEF123456", months=12, period_grouping="month")
289
331
  ```
290
332
 
291
333
  ## Pagination
@@ -303,9 +345,9 @@ print(f"Previous page URL: {response.previous}")
303
345
  for contract in response.results:
304
346
  print(contract['description'])
305
347
 
306
- # Get next page
348
+ # Get next page (contracts use keyset/cursor pagination)
307
349
  if response.next:
308
- next_response = client.list_contracts(page=2, limit=25)
350
+ next_response = client.list_contracts(cursor=response.cursor, limit=25)
309
351
  ```
310
352
 
311
353
  ## Error Handling
@@ -332,7 +374,7 @@ except TangoNotFoundError:
332
374
  print("Resource not found")
333
375
  except TangoValidationError as e:
334
376
  print(f"Invalid parameters: {e.message}")
335
- print(f"Details: {e.details}")
377
+ print(f"Details: {e.response_data}")
336
378
  except TangoRateLimitError:
337
379
  print("Rate limit exceeded")
338
380
  except TangoAPIError as e:
@@ -364,24 +406,59 @@ contracts = client.list_contracts(
364
406
 
365
407
  ### Flattened Responses
366
408
 
367
- Enable flattening to get dot-notation field names:
409
+ The `flat=True` parameter is passed to the API, which returns dot-notation keys in the raw response. The SDK still wraps the result in a `ShapedModel` — access nested fields via attribute or dict syntax, not dot-notation string keys:
368
410
 
369
411
  ```python
370
412
  contracts = client.list_contracts(
371
413
  shape="key,piid,recipient(display_name,uei)",
372
414
  flat=True
373
415
  )
374
- # Returns: {"key": "...", "piid": "...", "recipient.display_name": "...", "recipient.uei": "..."}
416
+ for contract in contracts.results:
417
+ # Attribute access
418
+ print(contract.recipient.display_name)
419
+ # Dict access (nested, not flat string keys)
420
+ print(contract['recipient']['display_name'])
421
+ ```
375
422
 
376
- # Flatten arrays with indexed keys
377
- contracts = client.list_contracts(
378
- shape="key,transactions(*)",
379
- flat=True,
380
- flat_lists=True
381
- )
382
- # Returns: {"key": "...", "transactions.0.action_date": "...", "transactions.0.obligated": "..."}
423
+ ### Webhook Tooling
424
+
425
+ The SDK ships first-class tooling for **building and testing webhook integrations against the Tango API** — including signing helpers, a local receiver, and a command-line tool covering the full lifecycle:
426
+
427
+ ```bash
428
+ pip install 'tango-python[webhooks]'
429
+ ```
430
+
431
+ This adds a `tango` console script with subcommands for the full webhook lifecycle:
432
+
433
+ ```bash
434
+ # Discover what's available
435
+ tango webhooks list-event-types
436
+ tango webhooks fetch-sample --event-type entities.updated
437
+
438
+ # Local development
439
+ tango webhooks listen --port 8011 --secret $SECRET # receiver
440
+ tango webhooks simulate --secret $SECRET --event-type entities.updated # sign + print
441
+ tango webhooks simulate --secret $SECRET --event-type entities.updated \
442
+ --to http://127.0.0.1:8011/tango/webhooks # also POST
443
+
444
+ # Manage delivery endpoints
445
+ tango webhooks endpoints create|list|get|delete
446
+
447
+ # Force a real test delivery from Tango
448
+ tango webhooks trigger
383
449
  ```
384
450
 
451
+ The signing helpers (`verify_signature`, `generate_signature`) are pure stdlib and importable from the default install — your receiver code doesn't need the extra:
452
+
453
+ ```python
454
+ from tango.webhooks import verify_signature
455
+
456
+ if not verify_signature(raw_body, secret, request.headers.get("X-Tango-Signature")):
457
+ return 401, "invalid signature"
458
+ ```
459
+
460
+ For the full guide — workflow, CLI reference, and programmatic patterns for pytest fixtures — see [`docs/WEBHOOKS.md`](docs/WEBHOOKS.md).
461
+
385
462
  ### Type Hints with IDE Support
386
463
 
387
464
  Import TypedDict types for IDE autocomplete:
@@ -531,6 +608,7 @@ tango-python/
531
608
  - [Shape System Guide](docs/SHAPES.md) - Comprehensive guide to response shaping
532
609
  - [API Reference](docs/API_REFERENCE.md) - Detailed API documentation
533
610
  - [Developer Guide](docs/DEVELOPERS.md) - Technical documentation for developers
611
+ - [Webhooks Guide](docs/WEBHOOKS.md) - Workflow, CLI reference, and programmatic patterns for webhook integrations
534
612
  - [Quick Start Notebook](docs/quick_start.ipynb) - Interactive Jupyter notebook with examples
535
613
 
536
614
  ## Requirements