Skip to content

Commit fce4aa1

Browse files
makegov-mark[bot]vdavezclaude
authored
Sync to Tango API v4.6.9: budget surface, singleton GETs, bug fixes (v1.1.0) (#31)
* feat: sync to Tango API v4.6.9 (budget surface, singleton GETs, bug fixes) Phase 1 (bug fixes): - Contract model: drop dead fields (id, award_id, recipient_name, award_amount, awarding_agency, funding_agency); add real ContractListSerializer fields. Old fields kept Optional/None + deprecation note, to be removed in 2.0.0. - list_contracts: stop sending page=1 to the cursor-only /api/contracts/. - add list_otidv_awards (parity with Node). Phase 2 (additive): - budget accounts surface: list_budget_accounts, get_budget_account, get_budget_account_quarters, get_budget_account_recipients (+ BudgetAccount model + shape schema). - singleton detail GETs: get_contract, get_contract_subawards, get_contract_transactions, get_forecast, get_grant, get_notice, get_opportunity, get_subaward. - get_entity_budget_flows(uei). - grant_id filter on list_grants. Phase 3 (CI): - re-enable lint.yml as a PR gate; conformance split into a non-blocking job. Version bump to 1.1.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: re-record contract integration cassettes for cursor-only pagination The list_contracts page=1 removal changed the recorded request URI, so 32 contract/edge-case cassettes no longer matched. Re-recorded against live API (v4.6.9). Two expensive filter queries (awarding_agency, multi-param search) 504 at the gateway on re-record, so those two cassettes retain their prior valid 200 response with only the stale `&page=1` stripped from the request URI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(shapes): register ContractOrIDVCompetition nested schema alias CONTRACT_SCHEMA/IDV_SCHEMA reference the competition leaf as "ContractOrIDVCompetition" but only "Competition" was registered in EXPLICIT_SCHEMAS, so competition(extent_competed,...) nested selections on contract/IDV shapes raised ShapeValidationError. Register the name as an alias of COMPETITION_SCHEMA. Fixes tango-python#29 item 1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(lint): make mypy advisory and conformance skip without token Re-enabling lint.yml surfaced two pre-existing blockers unrelated to the API sync: ~28 mypy errors that predate CI enforcement, and a conformance job that fails because the private makegov/tango checkout needs an unconfigured TANGO_API_REPO_ACCESS_TOKEN secret. Keep ruff format + ruff check as hard gates (clean today). Make mypy advisory (continue-on-error) pending a debt burn-down. Gate the conformance steps on the token being present so the job skips cleanly (green) instead of failing red, and becomes a real gate automatically once the secret exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(types): align webhook alert parser with WebhookAlert contract Clears the 4 mypy errors the v1.1.0 branch introduced over main's baseline. _parse_webhook_alert now emits str/dict/Literal values matching the model's declared types (sparse payloads hydrate "" / {} instead of None); cli sample payload cast to dict for assignment. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: V. David Zvenyach <dave@zvenyach.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d4ccb02 commit fce4aa1

43 files changed

Lines changed: 3825 additions & 54 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/lint.yml

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,96 @@
11
name: Linting
22

3-
# Disabled: requires checkout of private makegov/tango repo for filter_shape conformance check.
4-
# Re-enable when that repo is accessible (e.g. add TANGO_API_REPO_ACCESS_TOKEN secret).
3+
# Lint gate runs on every PR and push to main.
4+
#
5+
# - ruff format + ruff check are HARD gates (block the PR).
6+
# - mypy is ADVISORY for now (continue-on-error): the package carries ~28
7+
# pre-existing type errors that predate CI enforcement. Tracked for burn-down
8+
# in makegov/tango-python; flip `continue-on-error` off once that's clear.
9+
# - The SDK filter/shape conformance check needs the canonical manifest from the
10+
# private makegov/tango repo, which requires a TANGO_API_REPO_ACCESS_TOKEN
11+
# secret the public CI does not have. The conformance job SKIPS cleanly when
12+
# the token is absent (rather than failing red) and becomes a real gate the
13+
# moment the secret is configured.
514
on:
615
workflow_dispatch:
7-
# push:
8-
# branches: [ main, develop ]
9-
# pull_request:
10-
# branches: [ main, develop ]
16+
push:
17+
branches: [ main ]
18+
pull_request:
19+
branches: [ main ]
1120

1221
jobs:
1322
lint:
1423
runs-on: ubuntu-latest
15-
24+
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- name: Install uv
29+
uses: astral-sh/setup-uv@v4
30+
with:
31+
version: "latest"
32+
33+
- name: Set up Python
34+
run: uv python install 3.12
35+
36+
- name: Install dependencies
37+
run: uv sync --all-extras
38+
39+
- name: Check formatting with ruff
40+
run: uv run ruff format --check tango/
41+
42+
- name: Lint with ruff
43+
run: uv run ruff check tango/
44+
45+
- name: Type check with mypy (advisory)
46+
continue-on-error: true
47+
run: uv run mypy tango/
48+
49+
conformance:
50+
# Requires the canonical filter_shape manifest from the private makegov/tango
51+
# repo. When TANGO_API_REPO_ACCESS_TOKEN is not configured, every real step
52+
# is skipped and the job passes (rather than failing on an empty token).
53+
# Configure the secret to turn this into a hard gate automatically.
54+
runs-on: ubuntu-latest
55+
1656
steps:
57+
- name: Determine token availability
58+
id: gate
59+
env:
60+
TANGO_API_REPO_ACCESS_TOKEN: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
61+
run: |
62+
if [ -n "$TANGO_API_REPO_ACCESS_TOKEN" ]; then
63+
echo "ready=true" >> "$GITHUB_OUTPUT"
64+
else
65+
echo "ready=false" >> "$GITHUB_OUTPUT"
66+
echo "::notice::Skipping SDK conformance check — TANGO_API_REPO_ACCESS_TOKEN not configured."
67+
fi
68+
1769
- uses: actions/checkout@v4
70+
if: steps.gate.outputs.ready == 'true'
1871

1972
- name: Checkout tango API repo (manifest source)
73+
if: steps.gate.outputs.ready == 'true'
2074
uses: actions/checkout@v4
2175
with:
2276
repository: makegov/tango
2377
path: tango-api
24-
78+
token: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
79+
2580
- name: Install uv
81+
if: steps.gate.outputs.ready == 'true'
2682
uses: astral-sh/setup-uv@v4
2783
with:
2884
version: "latest"
29-
85+
3086
- name: Set up Python
87+
if: steps.gate.outputs.ready == 'true'
3188
run: uv python install 3.12
32-
89+
3390
- name: Install dependencies
91+
if: steps.gate.outputs.ready == 'true'
3492
run: uv sync --all-extras
35-
36-
- name: Check formatting with ruff
37-
run: uv run ruff format --check tango/
38-
39-
- name: Lint with ruff
40-
run: uv run ruff check tango/
4193

4294
- name: Check SDK filter/shape conformance
95+
if: steps.gate.outputs.ready == 'true'
4396
run: uv run python scripts/check_filter_shape_conformance.py --manifest tango-api/contracts/filter_shape_contract.json

CHANGELOG.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
- `_parse_webhook_alert`: aligned the parser output with the `WebhookAlert`
12+
type contract. Sparse server payloads now hydrate `query_type` and `filters`
13+
as `""` / `{}` (matching their declared non-null types) rather than `None`,
14+
and `status` is typed as the `Literal["active", "paused"]` the model promises.
15+
Clears the four type-check errors this introduced; no change for full payloads.
16+
- `Contract` model: removed dead fields (`id`, `award_id`, `recipient_name`,
17+
`award_amount`, `awarding_agency`, `funding_agency`) and added the real API
18+
fields (`key`, `piid`, `obligated`, `total_contract_value`,
19+
`base_and_exercised_options_value`, `awarding_office`, `funding_office`,
20+
`naics_code`, `psc_code`, `set_aside`, `solicitation_identifier`,
21+
`parent_award`, `legislative_mandates`, `subawards_summary`,
22+
`place_of_performance`). The deprecated fields remain declared with `None`
23+
defaults for one minor cycle (they never returned data) and will be removed
24+
in `2.0.0`. New `OrganizationOfficePayload` dataclass for the office fields.
25+
(`Contract` is a documentation-only dataclass — not instantiated or exported
26+
— so there is no runtime impact.)
27+
- `list_contracts`: no longer sends `page=1` to the cursor-only `/api/contracts/`
28+
endpoint. When no cursor is supplied, neither `page` nor `cursor` is sent and
29+
the API returns the first page by default.
30+
- Shape validation: registered the `ContractOrIDVCompetition` nested schema
31+
(alias of `Competition`) so nested selections like
32+
`competition(extent_competed,number_of_offers_received)` on contract / IDV
33+
shapes validate instead of raising `ShapeValidationError`.
34+
35+
### Added
36+
- Budget accounts surface (tango v4.6.8): `list_budget_accounts`,
37+
`get_budget_account`, `get_budget_account_quarters`,
38+
`get_budget_account_recipients`. New `BudgetAccount` dataclass exported from
39+
the top-level package, plus `ShapeConfig.BUDGET_ACCOUNTS_MINIMAL`.
40+
- Singleton detail GETs: `get_contract`, `get_contract_subawards`,
41+
`get_contract_transactions`, `get_forecast`, `get_grant`, `get_notice`,
42+
`get_opportunity`, `get_subaward`.
43+
- `get_entity_budget_flows(uei)` for `/api/entities/{uei}/budget-flows/`.
44+
- `list_otidv_awards(key)` for `/api/otidvs/{key}/awards/` (parity with Node).
45+
- `grant_id` filter kwarg on `list_grants` (supports multi-value OR via `|`).
46+
47+
### CI
48+
- Re-enabled `lint.yml` as a PR + push-to-main gate. `ruff format` and
49+
`ruff check` are hard gates; `mypy` runs advisory (continue-on-error) pending
50+
burn-down of ~28 pre-existing type errors. The filter/shape conformance check
51+
is a separate job that skips cleanly until a `TANGO_API_REPO_ACCESS_TOKEN`
52+
secret for the private manifest repo is configured, at which point it becomes
53+
a hard gate.
54+
1055
## [1.0.0] - 2026-05-13
1156

1257
> First stable release. `tango-python` is now at full API parity with the

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "tango-python"
7-
version = "1.0.0"
7+
version = "1.1.0"
88
description = "Python SDK for the Tango API"
99
readme = "README.md"
1010
requires-python = ">=3.12"

scripts/check_filter_shape_conformance.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"naics": "list_naics",
5252
"gsa_elibrary_contracts": "list_gsa_elibrary_contracts",
5353
"itdashboard": "list_itdashboard_investments",
54+
"budget_accounts": "list_budget_accounts",
5455
# Resources not yet implemented in SDK
5556
"offices": None,
5657
}
@@ -62,6 +63,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]:
6263
IDV,
6364
OTA,
6465
OTIDV,
66+
BudgetAccount,
6567
Contract,
6668
Entity,
6769
Forecast,
@@ -110,6 +112,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]:
110112
ShapeConfig.ITDASHBOARD_INVESTMENTS_COMPREHENSIVE,
111113
ITDashboardInvestment,
112114
),
115+
("BUDGET_ACCOUNTS_MINIMAL", ShapeConfig.BUDGET_ACCOUNTS_MINIMAL, BudgetAccount),
113116
]
114117
for name, shape_str, model_cls in configs:
115118
entries.append((name, shape_str, model_cls))

tango/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
TangoValidationError,
1010
)
1111
from .models import (
12+
BudgetAccount,
1213
GsaElibraryContract,
1314
ITDashboardInvestment,
1415
PaginatedResponse,
@@ -43,7 +44,7 @@
4344
)
4445
from .webhooks.receiver import Delivery, WebhookReceiver
4546

46-
__version__ = "1.0.0"
47+
__version__ = "1.1.0"
4748
__all__ = [
4849
"TangoClient",
4950
"TangoAPIError",
@@ -54,6 +55,7 @@
5455
"RateLimitInfo",
5556
"ResolveCandidate",
5657
"ResolveResult",
58+
"BudgetAccount",
5759
"GsaElibraryContract",
5860
"ITDashboardInvestment",
5961
"PaginatedResponse",

0 commit comments

Comments
 (0)