Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ This project follows [Semantic Versioning](https://semver.org/).

`attachments(extracted_text)` returns the text extracted from an attachment's document (Tango API 5.9.1). The field joins `EbuyAttachmentRecord` and the `EbuyAttachment` shape schema. It must be named: the default detail shape and `attachments(*)` do not carry it. The key is absent rather than null for a link, an empty scan, or a document not yet extracted.

- **Vehicle company counts** (Tango API 5.8.0). `Vehicle` gains `holder_count` (distinct companies holding one of the vehicle's IDVs) and `order_winner_count` (distinct companies that have won a task order under it), and both join the `VEHICLES_MINIMAL` and `VEHICLES_COMPREHENSIVE` defaults next to `awardee_count`, so `listVehicles()` and `getVehicle()` return them without a custom `shape`. Neither is orderable or filterable.

- **Boards-of-contract-appeals decisions** (Tango API 4.26.0). `listContractAppeals(options)` and `getContractAppeal(uuid, options)` over `/api/contract_appeals/`, with every filter the API accepts declared as a typed option on `ListContractAppealsOptions` (`search`, `board`, `docket`, `appellant`, `judge`, `decision_type`, the `decision_date_after` / `_before` pair, `listed`, `document_id`, `ordering`), the new `ContractAppealRecord` return type, and a registered `ContractAppeal` shape schema so the typed shape API resolves the resource's fields.

These are Contract Disputes Act decisions from the CBCA (civilian) and the ASBCA (defense) — a dispute under an existing contract, not a challenge to an award. Bid protests remain the separate `listProtests()` resource, and the two do not overlap.
Expand All @@ -62,6 +64,10 @@ This project follows [Semantic Versioning](https://semver.org/).
- Wrapping eBuy maps `ebuy/requests` in the conformance gate and removes it from both coverage baselines.
- Baselined 14 reverse-shape-coverage gaps in `contracts/shape_coverage_baseline.json`, matching tango-python. All 14 are the nested sub-resource routes above, which reuse the parent resource's model rather than carrying one of their own; none is SLED, and none is a regression — they became visible only with the re-vendored contract.

### Deprecated

- `Vehicle.awardee_count` is deprecated in favor of `order_winner_count`, which carries the same value. The API keeps serving it as an alias until its next major version.

### Fixed

- **`attachments(name,doc_role,doc_role_alt)` no longer throws `ShapeValidationError`** on `getOpportunity`, `listOpportunities`, `getNotice` and `listNotices`; the API serves both fields on the Pro plan and above when they are named. New `OpportunityAttachment` and `AttachmentDocRole` types cover the six roles and are used on `Opportunity` and `Notice`.
Expand Down
6 changes: 6 additions & 0 deletions docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ Supported parameters:
- `page`, `limit` (max 100)
- `shape`, `flat`, `flatLists`

Company counts (on both the list and detail defaults; neither is orderable or filterable):

- `holder_count`: distinct companies holding one of the vehicle's IDVs.
- `order_winner_count`: distinct companies that have won a task order under the vehicle.
- `awardee_count`: deprecated alias of `order_winner_count` (same value), removed at the API's next major version.

### `getVehicle(uuid, options?)`

```ts
Expand Down
4 changes: 2 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const ShapeConfig = {
VEHICLES_MINIMAL:
"uuid,solicitation_identifier,is_synthetic_solicitation,program_acronym," +
"organization_id,organization,vehicle_type,description," +
"idv_count,awardee_count,order_count,total_obligated," +
"idv_count,holder_count,order_winner_count,awardee_count,order_count,total_obligated," +
"vehicle_obligations,vehicle_contracts_value,latest_award_date," +
"solicitation_title,solicitation_date",

Expand All @@ -72,7 +72,7 @@ export const ShapeConfig = {
"solicitation_title,solicitation_description,solicitation_date,opportunity_id," +
"naics_code,psc_code,set_aside," +
"fiscal_year,award_date,latest_award_date,last_date_to_order," +
"description,idv_count,awardee_count,order_count,total_obligated," +
"description,idv_count,holder_count,order_winner_count,awardee_count,order_count,total_obligated," +
"vehicle_obligations,vehicle_contracts_value," +
"type_of_idc,contract_type,metrics(*)",

Expand Down
5 changes: 5 additions & 0 deletions src/models/Vehicle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ export interface Vehicle {
award_date?: string | null;
last_date_to_order?: string | null;

/** @deprecated Same value as `order_winner_count`; the API removes this alias at its next major version. */
awardee_count?: number | null;
/** Number of distinct companies holding one of the vehicle's IDVs. */
holder_count?: number | null;
/** Number of distinct companies that have won a task order under the vehicle. */
order_winner_count?: number | null;
order_count?: number | null;
vehicle_obligations?: string | null;
vehicle_contracts_value?: string | null;
Expand Down
14 changes: 14 additions & 0 deletions src/shapes/explicitSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2520,6 +2520,20 @@ export const VEHICLE_SCHEMA: FieldSchemaMap = {
isList: false,
nestedModel: null,
},
holder_count: {
name: "holder_count",
type: "int",
isOptional: true,
isList: false,
nestedModel: null,
},
order_winner_count: {
name: "order_winner_count",
type: "int",
isOptional: true,
isList: false,
nestedModel: null,
},
order_count: {
name: "order_count",
type: "int",
Expand Down
2 changes: 0 additions & 2 deletions src/shapes/generatedOverlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1080,8 +1080,6 @@ export const GENERATED_OVERLAY: Record<string, FieldSchemaMap> = {
organization: f("organization", "dict", false, "AwardingOffice"),
},
Vehicle: {
holder_count: f("holder_count", "int"),
name: f("name", "str"),
order_winner_count: f("order_winner_count", "int"),
},
};
2 changes: 1 addition & 1 deletion tests/cassettes/edge-vehicles-bad-ordering.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"request": {
"method": "GET",
"url": "https://tango.makegov.com/api/vehicles/?limit=3&ordering=not_a_real_field&page=1&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date"
"url": "https://tango.makegov.com/api/vehicles/?limit=3&ordering=not_a_real_field&page=1&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cholder_count%2Corder_winner_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date"
},
"response": {
"status": 400,
Expand Down
10 changes: 8 additions & 2 deletions tests/cassettes/vehicles-list.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"request": {
"method": "GET",
"url": "https://tango.makegov.com/api/vehicles/?limit=3&page=1&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date"
"url": "https://tango.makegov.com/api/vehicles/?limit=3&page=1&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cholder_count%2Corder_winner_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date"
},
"response": {
"status": 200,
Expand All @@ -23,10 +23,12 @@
},
"body": {
"count": 20749,
"next": "https://tango.makegov.com/api/vehicles/?limit=3&page=2&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date",
"next": "https://tango.makegov.com/api/vehicles/?limit=3&page=2&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cholder_count%2Corder_winner_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date",
"previous": null,
"results": [
{
"holder_count": 2,
"order_winner_count": 2,
"awardee_count": 2,
"description": [
"CATTLE AND SWINE BACKTAG CEMENT",
Expand Down Expand Up @@ -60,6 +62,8 @@
}
},
{
"holder_count": 2,
"order_winner_count": 1,
"awardee_count": 1,
"description": [
"WIRED TELEPHONY SERVICES"
Expand Down Expand Up @@ -92,6 +96,8 @@
}
},
{
"holder_count": 3,
"order_winner_count": 3,
"awardee_count": 3,
"description": [
"INDEFINITE QUANTITY ARCHITECTURAL ENGINEERING CONTRACT FOR THE MID WEST AREA.",
Expand Down
10 changes: 8 additions & 2 deletions tests/cassettes/vehicles-search.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"request": {
"method": "GET",
"url": "https://tango.makegov.com/api/vehicles/?limit=3&page=1&search=SEWP&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date"
"url": "https://tango.makegov.com/api/vehicles/?limit=3&page=1&search=SEWP&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cholder_count%2Corder_winner_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date"
},
"response": {
"status": 200,
Expand All @@ -23,10 +23,12 @@
},
"body": {
"count": 8,
"next": "https://tango.makegov.com/api/vehicles/?limit=3&page=2&search=SEWP&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date",
"next": "https://tango.makegov.com/api/vehicles/?limit=3&page=2&search=SEWP&shape=uuid%2Csolicitation_identifier%2Cis_synthetic_solicitation%2Cprogram_acronym%2Corganization_id%2Corganization%2Cvehicle_type%2Cdescription%2Cidv_count%2Cholder_count%2Corder_winner_count%2Cawardee_count%2Corder_count%2Ctotal_obligated%2Cvehicle_obligations%2Cvehicle_contracts_value%2Clatest_award_date%2Csolicitation_title%2Csolicitation_date",
"previous": null,
"results": [
{
"holder_count": 1410,
"order_winner_count": 0,
"awardee_count": 0,
"description": [
"NASA ITPO SEWP VI GWAC SOLICITATION. THE PRINCIPAL PURPOSE OF THIS REQUIREMENT IS TO PROVIDE THE FEDERAL GOVERNMENT WITH AN ALL-ENCOMPASSING ONE-STOP ACQUISITION VEHICLE FOR INFORMATION TECHNOLOGY PRODUCT AND SERVICE SOLUTIONS.",
Expand Down Expand Up @@ -60,6 +62,8 @@
}
},
{
"holder_count": 16,
"order_winner_count": 0,
"awardee_count": 0,
"description": [
"IGF::CT::IGF:: REQUEST FOR DESKTOPS TO BE USED WITHIN BY EPIC DEVELOPERS TO ACCESS APPLICATIONS IN DEVELOPMENT ENVIRONMENTS.",
Expand Down Expand Up @@ -107,6 +111,8 @@
}
},
{
"holder_count": 6,
"order_winner_count": 43,
"awardee_count": 43,
"description": [
"LAN NETWORKING SYSTEMS ENGINEERING AND COMPUTER SYSTEMS DESIGN",
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ describe("TangoClient", () => {
solicitation_identifier: "47QSWA20D0001",
solicitation_date: "2024-01-15",
vehicle_obligations: 123.45,
awardee_count: 7,
holder_count: 12,
order_winner_count: 7,
},
],
});
Expand Down Expand Up @@ -368,6 +371,7 @@ describe("TangoClient", () => {
});
expect((vehicles.results[0] as any).solicitation_date).toBeInstanceOf(Date);
expect((vehicles.results[0] as any).vehicle_obligations).toBe("123.45");
expect(vehicles.results[0]).toMatchObject({ holder_count: 12, order_winner_count: 7, awardee_count: 7 });

const vehicle = await client.getVehicle("00000000-0000-0000-0000-000000000001", {
shape: "uuid,opportunity(title)",
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/config.shapes.parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ describe("ShapeConfig parity with Python SDK", () => {
const expected =
"uuid,solicitation_identifier,is_synthetic_solicitation,program_acronym," +
"organization_id,organization,vehicle_type,description," +
"idv_count,awardee_count,order_count,total_obligated," +
"idv_count,holder_count,order_winner_count,awardee_count,order_count,total_obligated," +
"vehicle_obligations,vehicle_contracts_value,latest_award_date," +
"solicitation_title,solicitation_date";
expect(ShapeConfig.VEHICLES_MINIMAL).toBe(expected);
Expand All @@ -193,7 +193,7 @@ describe("ShapeConfig parity with Python SDK", () => {
"solicitation_title,solicitation_description,solicitation_date,opportunity_id," +
"naics_code,psc_code,set_aside," +
"fiscal_year,award_date,latest_award_date,last_date_to_order," +
"description,idv_count,awardee_count,order_count,total_obligated," +
"description,idv_count,holder_count,order_winner_count,awardee_count,order_count,total_obligated," +
"vehicle_obligations,vehicle_contracts_value," +
"type_of_idc,contract_type,metrics(*)";
expect(ShapeConfig.VEHICLES_COMPREHENSIVE).toBe(expected);
Expand Down
Loading