diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index f29d3cb57..b0b63005c 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -112,6 +112,26 @@ jobs: working-directory: ./website run: bun run test + public-api-client: + name: Public API client is current + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install --frozen-lockfile + + # Regenerates the types from the checked-in OpenAPI document and fails + # when src/api/publicApi/schema.d.ts differs from the result. + - name: Check the generated client against its OpenAPI document + working-directory: ./app + run: bun run public-api:check + check-embeds: name: Check embedded site URLs runs-on: ubuntu-latest diff --git a/app/package.json b/app/package.json index b7472eb0d..a561d535e 100644 --- a/app/package.json +++ b/app/package.json @@ -7,6 +7,9 @@ "evaluate-search": "tsx scripts/evaluate-parameter-search.ts", "refresh-search-eval-cases": "tsx scripts/refresh-parameter-search-cases.ts", "build-parameter-dependencies": "python3 scripts/build-parameter-dependency-map.py", + "public-api:generate": "tsx scripts/public-api-client.ts generate", + "public-api:check": "tsx scripts/public-api-client.ts check", + "public-api:refresh": "tsx scripts/public-api-client.ts refresh", "typecheck": "tsc --noEmit", "lint": "bun run eslint && bun run stylelint", "eslint": "eslint . --cache --max-warnings 0", @@ -39,6 +42,7 @@ "fuse.js": "^7.0.0", "html-to-image": "^1.11.13", "lucide-react": "^0.575.0", + "openapi-fetch": "0.17.0", "policyengine-household-wizard": "^0.1.0", "radix-ui": "^1.4.3", "react": "^19.1.0", @@ -78,6 +82,7 @@ "fast-check": "^4.10.2", "identity-obj-proxy": "^3.0.0", "jsdom": "^26.1.0", + "openapi-typescript": "7.13.0", "postcss": "^8.5.5", "postcss-simple-vars": "^7.0.1", "prettier": "^3.5.3", diff --git a/app/scripts/public-api-client.ts b/app/scripts/public-api-client.ts new file mode 100644 index 000000000..270e906bf --- /dev/null +++ b/app/scripts/public-api-client.ts @@ -0,0 +1,144 @@ +/** + * TypeScript types for the Public API's v2 routes (policyengine-api), + * generated from its OpenAPI document (`GET /v2/openapi.json`). + * + * The document is checked in beside the generated types, so a build never + * calls the API, and CI can check that the types match the document: + * + * bun run public-api:generate writes schema.d.ts from openapi.json + * bun run public-api:check fails when schema.d.ts is out of date + * bun run public-api:refresh copies the document from a URL or a + * file, then writes schema.d.ts + * + * is an API origin (https://api.policyengine.org), a full URL of + * the document, or a path to a saved copy of it. + */ +import { readFile, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import openapiTS, { astToString, type OpenAPI3 } from 'openapi-typescript'; +import prettier from 'prettier'; + +const SCRIPT_DIRECTORY = path.dirname(fileURLToPath(import.meta.url)); +const CLIENT_DIRECTORY = path.resolve(SCRIPT_DIRECTORY, '../src/api/publicApi'); + +export const OPENAPI_DOCUMENT_PATH = path.join(CLIENT_DIRECTORY, 'openapi.json'); +export const GENERATED_SCHEMA_PATH = path.join(CLIENT_DIRECTORY, 'schema.d.ts'); + +const GENERATED_HEADER = `/** + * Generated from src/api/publicApi/openapi.json by scripts/public-api-client.ts. + * Do not edit: run \`bun run public-api:generate\` (or \`public-api:refresh\`). + */ +`; + +/** The path the API serves its v2 OpenAPI document at. */ +const OPENAPI_DOCUMENT_ROUTE = '/v2/openapi.json'; + +/** The generated, formatted types for an OpenAPI document. */ +export async function generatePublicApiTypes(openapiDocument: OpenAPI3): Promise { + const syntaxTree = await openapiTS(openapiDocument); + const source = `${GENERATED_HEADER}\n${astToString(syntaxTree)}`; + const prettierConfig = (await prettier.resolveConfig(GENERATED_SCHEMA_PATH)) ?? {}; + return prettier.format(source, { ...prettierConfig, filepath: GENERATED_SCHEMA_PATH }); +} + +export interface SchemaComparison { + upToDate: boolean; + expected: string; +} + +/** Whether checked-in types equal what the document generates. */ +export async function compareGeneratedTypes( + openapiDocument: OpenAPI3, + checkedInTypes: string +): Promise { + const expected = await generatePublicApiTypes(openapiDocument); + return { upToDate: expected === checkedInTypes, expected }; +} + +/** Where to read the document from: an origin gets the v2 route appended. */ +export function openapiDocumentLocation(source: string): string { + if (!/^https?:\/\//.test(source)) { + return source; + } + const url = new URL(source); + if (url.pathname === '/' || url.pathname === '') { + url.pathname = OPENAPI_DOCUMENT_ROUTE; + } + return url.toString(); +} + +/** The document as checked in: two-space JSON with a final newline. */ +export function serializeOpenapiDocument(openapiDocument: OpenAPI3): string { + return `${JSON.stringify(openapiDocument, null, 2)}\n`; +} + +async function readOpenapiDocument(location: string): Promise { + if (/^https?:\/\//.test(location)) { + const response = await fetch(location, { headers: { Accept: 'application/json' } }); + if (!response.ok) { + throw new Error(`GET ${location} returned ${response.status}`); + } + return (await response.json()) as OpenAPI3; + } + return JSON.parse(await readFile(location, 'utf8')) as OpenAPI3; +} + +async function writeGeneratedTypes(): Promise { + const openapiDocument = await readOpenapiDocument(OPENAPI_DOCUMENT_PATH); + await writeFile(GENERATED_SCHEMA_PATH, await generatePublicApiTypes(openapiDocument)); + console.info(`Wrote ${path.relative(process.cwd(), GENERATED_SCHEMA_PATH)}`); +} + +async function checkGeneratedTypes(): Promise { + const openapiDocument = await readOpenapiDocument(OPENAPI_DOCUMENT_PATH); + const checkedInTypes = await readFile(GENERATED_SCHEMA_PATH, 'utf8'); + const { upToDate } = await compareGeneratedTypes(openapiDocument, checkedInTypes); + if (upToDate) { + console.info('The generated Public API types match openapi.json.'); + return true; + } + console.error( + 'src/api/publicApi/schema.d.ts does not match src/api/publicApi/openapi.json.\n' + + 'Run `bun run public-api:generate` in app/ and commit the result.' + ); + return false; +} + +async function refreshOpenapiDocument(source: string | undefined): Promise { + if (!source) { + throw new Error('Name the document to copy: an API origin, a URL or a file path.'); + } + const location = openapiDocumentLocation(source); + const openapiDocument = await readOpenapiDocument(location); + await writeFile(OPENAPI_DOCUMENT_PATH, serializeOpenapiDocument(openapiDocument)); + console.info(`Copied ${location} to ${path.relative(process.cwd(), OPENAPI_DOCUMENT_PATH)}`); + await writeGeneratedTypes(); +} + +async function main(commandLineArguments: string[]): Promise { + const [command, source] = commandLineArguments; + switch (command) { + case 'generate': + await writeGeneratedTypes(); + return 0; + case 'check': + return (await checkGeneratedTypes()) ? 0 : 1; + case 'refresh': + await refreshOpenapiDocument(source); + return 0; + default: + console.error('Usage: public-api-client.ts generate | check | refresh '); + return 2; + } +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).then( + (exitCode) => process.exit(exitCode), + (error: unknown) => { + console.error(error instanceof Error ? error.message : error); + process.exit(1); + } + ); +} diff --git a/app/src/api/publicApi/catalogParameters.ts b/app/src/api/publicApi/catalogParameters.ts new file mode 100644 index 000000000..28c195271 --- /dev/null +++ b/app/src/api/publicApi/catalogParameters.ts @@ -0,0 +1,110 @@ +import { + createPublicApiClient, + publicApiCountryId, + requireResult, + type PublicApiClient, +} from '@/api/publicApi/client'; +import { parameterValueFromApi } from '@/api/publicApi/jsonValues'; +import type { components } from '@/api/publicApi/schema'; +import { v1FallbackLabel } from '@/libs/flagship/v2ParameterSearch'; +import type { ParameterMetadataCollection } from '@/types/metadata/parameterMetadata'; +import type { ValuesList } from '@/types/subIngredients/valueInterval'; + +/** + * Parameters fetched by name from the Public API's + * `GET /v2/model-catalog/parameters`, with their current-law values and the + * labels of every node above them (for breadcrumbs), in the shape v1's + * metadata gave each parameter, so code that reads the metadata store + * reads them unchanged. + */ + +type Schemas = components['schemas']; + +/** How many names one request takes (the route's own bound). */ +export const CATALOG_PARAMETER_BATCH = 100; + +export interface LoadedCatalogParameters { + /** The parameters found, and every node above them. */ + parameters: ParameterMetadataCollection; + /** Each found parameter's catalog ID, which `/v2/policies` takes. */ + parameterIds: Record; + /** Names the catalog version does not have. */ + missingNames: string[]; +} + +/** v1 dated a value in force since the earliest date 0000-01-01; Python's earliest is year 1. */ +const V2_EARLIEST_DATE = '0001-01-01'; +const V1_EARLIEST_DATE = '0000-01-01'; + +function v1Values(parameterName: string, values: Schemas['MetadataDatedValue'][]): ValuesList { + return Object.fromEntries( + values.map((dated) => { + const date = dated.start_date.slice(0, 10); + return [ + date === V2_EARLIEST_DATE ? V1_EARLIEST_DATE : date, + parameterValueFromApi(dated.value, parameterName), + ]; + }) + ); +} + +function addResult( + loaded: LoadedCatalogParameters, + result: Schemas['MetadataCatalogParametersResult'] +): void { + for (const [path, label] of Object.entries(result.node_labels)) { + loaded.parameters[path] = { + type: 'parameterNode', + parameter: path, + label: label ?? v1FallbackLabel(path), + }; + } + for (const parameter of result.parameters) { + loaded.parameters[parameter.name] = { + type: 'parameter', + parameter: parameter.name, + label: parameter.label ?? v1FallbackLabel(parameter.name), + description: parameter.description, + unit: parameter.unit, + values: v1Values(parameter.name, parameter.values), + economy: parameter.economy ?? true, + household: parameter.household ?? true, + }; + loaded.parameterIds[parameter.name] = parameter.id; + } + loaded.missingNames.push(...result.missing_names); +} + +/** The named parameters of a catalog version, in batches the route takes. */ +export async function fetchCatalogParameters( + countryId: string, + policyengineVersion: string, + names: readonly string[], + client: PublicApiClient = createPublicApiClient() +): Promise { + const unique = [...new Set(names)].sort(); + const batches: string[][] = []; + for (let start = 0; start < unique.length; start += CATALOG_PARAMETER_BATCH) { + batches.push(unique.slice(start, start + CATALOG_PARAMETER_BATCH)); + } + const results = await Promise.all( + batches.map((batch) => + requireResult( + client.GET('/v2/model-catalog/parameters', { + params: { + query: { + country_id: publicApiCountryId(countryId), + policyengine_version: policyengineVersion, + name: batch, + }, + }, + }) + ) + ) + ); + const loaded: LoadedCatalogParameters = { parameters: {}, parameterIds: {}, missingNames: [] }; + for (const result of results) { + addResult(loaded, result); + } + return loaded; +} diff --git a/app/src/api/publicApi/client.ts b/app/src/api/publicApi/client.ts new file mode 100644 index 000000000..05c78968c --- /dev/null +++ b/app/src/api/publicApi/client.ts @@ -0,0 +1,111 @@ +import createClient, { type Client } from 'openapi-fetch'; +import { + credentialHeaders, + failureMessage, + V2ApiError, + type V2Credentials, +} from '@/api/v2UserClient'; +import { BASE_URL } from '@/constants'; +import type { paths } from './schema'; + +/** + * A typed client for the Public API's v2 routes (policyengine-api), generated + * from its OpenAPI document (see scripts/public-api-client.ts). Route paths, + * query parameters, bodies and responses are checked against + * src/api/publicApi/schema.d.ts at compile time. + */ +export type PublicApiClient = Client; + +/** A country the v2 routes serve, as the generated client types it. */ +export type PublicApiCountryId = + paths['/v2/model-catalog']['get']['parameters']['query']['country_id']; + +/** Every country the v2 routes serve: the type requires each one, and no other. */ +const PUBLIC_API_COUNTRIES: Record = { us: true, uk: true }; + +function isPublicApiCountryId(countryId: string): countryId is PublicApiCountryId { + return Object.hasOwn(PUBLIC_API_COUNTRIES, countryId); +} + +/** The app's country ID for a v2 request; one the v2 routes do not serve throws. */ +export function publicApiCountryId(countryId: string): PublicApiCountryId { + if (!isPublicApiCountryId(countryId)) { + throw new Error(`The Public API does not serve the country "${countryId}".`); + } + return countryId; +} + +export interface CreatePublicApiClientOptions { + /** Who requests are made as; metadata routes need no one. */ + credentials?: V2Credentials; + baseUrl?: string; + fetchImpl?: typeof fetch; +} + +export function createPublicApiClient({ + credentials = { kind: 'none' }, + baseUrl, + fetchImpl, +}: CreatePublicApiClientOptions = {}): PublicApiClient { + const client = createClient({ + baseUrl: baseUrl ?? BASE_URL, + // Resolved per request, so a stubbed global fetch is honoured. + fetch: (request: Request) => (fetchImpl ?? globalThis.fetch)(request), + }); + client.use({ + async onRequest({ request }) { + for (const [name, value] of Object.entries(await credentialHeaders(credentials))) { + request.headers.set(name, value); + } + return request; + }, + }); + return client; +} + +interface ClientOutcome { + data?: Data; + error?: unknown; + response: Response; +} + +/** + * The body of a successful response, for routes that return a document + * without the v2 envelope (such as the parameter search file). A failed + * response throws V2ApiError with its status and the API's message, and a + * request that never reached the API throws status 0. + */ +export async function requireBody(call: Promise>): Promise { + let outcome: ClientOutcome; + try { + outcome = await call; + } catch (error) { + if (error instanceof V2ApiError) { + throw error; + } + throw new V2ApiError( + 0, + error instanceof Error ? `Network error: ${error.message}` : 'Network error' + ); + } + const { data, error, response } = outcome; + if (!response.ok || data === undefined) { + const apiMessage = + error && typeof error === 'object' && 'message' in error + ? String((error as { message: unknown }).message) + : null; + throw new V2ApiError(response.status, failureMessage(response.status, apiMessage)); + } + return data; +} + +/** + * The `result` of a v2 success envelope (`{status: "ok", message, result}`). + * A failed response throws V2ApiError with its status and the API's message, + * and a request that never reached the API throws status 0. + */ +export async function requireResult( + call: Promise> +): Promise { + return (await requireBody(call)).result; +} diff --git a/app/src/api/publicApi/jsonValues.ts b/app/src/api/publicApi/jsonValues.ts new file mode 100644 index 000000000..48ad3da06 --- /dev/null +++ b/app/src/api/publicApi/jsonValues.ts @@ -0,0 +1,57 @@ +import type { components } from '@/api/publicApi/schema'; +import type { PolicyParameterValue } from '@/types/metadata/policyMetadata'; + +/** A value `/v2/policies` takes: JSON whose numbers are finite. */ +export type FiniteJsonValue = components['schemas']['FiniteJsonValue']; + +/** + * Whether a value is JSON made of the kinds the app keeps parameter values + * as: text, numbers, yes/no values, null, and lists and plain objects of + * them. With `finite`, every number must be finite. + */ +function isJsonValue(value: unknown, finite: boolean): boolean { + if (value === null || typeof value === 'string' || typeof value === 'boolean') { + return true; + } + if (typeof value === 'number') { + return finite ? Number.isFinite(value) : !Number.isNaN(value); + } + if (Array.isArray(value)) { + return value.every((item) => isJsonValue(item, finite)); + } + if (typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) { + return Object.values(value).every((item) => isJsonValue(item, finite)); + } + return false; +} + +function isParameterValue(value: unknown): value is PolicyParameterValue { + return isJsonValue(value, false); +} + +function isFiniteJsonValue(value: unknown): value is FiniteJsonValue { + return isJsonValue(value, true); +} + +/** + * A parameter value the Public API sent (typed only as JSON), checked where + * it arrives so a malformed response fails there, naming the parameter. + */ +export function parameterValueFromApi(value: unknown, parameterName: string): PolicyParameterValue { + if (!isParameterValue(value)) { + throw new Error( + `The Public API sent a value for ${parameterName} that is not a parameter value.` + ); + } + return value; +} + +/** A parameter value as `/v2/policies` takes it, checked before it is sent. */ +export function finiteJsonValueForApi(value: unknown, parameterName: string): FiniteJsonValue { + if (!isFiniteJsonValue(value)) { + throw new Error( + `The value for ${parameterName} cannot be sent to the Public API as finite JSON.` + ); + } + return value; +} diff --git a/app/src/api/publicApi/modelCatalog.ts b/app/src/api/publicApi/modelCatalog.ts new file mode 100644 index 000000000..5f0137795 --- /dev/null +++ b/app/src/api/publicApi/modelCatalog.ts @@ -0,0 +1,116 @@ +import { + createPublicApiClient, + publicApiCountryId, + requireResult, + type PublicApiClient, +} from '@/api/publicApi/client'; +import type { components } from '@/api/publicApi/schema'; +import type { MetadataApiPayload, MetadataRegionEntry } from '@/types/metadata'; + +/** + * The v3 workspace's model metadata at start, from the Public API's + * `GET /v2/model-catalog`: only the small parts of the current catalog + * version (its PolicyEngine.py version, entities, basic inputs, regions, + * years and datasets), in the shape `GET /{country}/metadata` returns, so + * everything that reads the metadata store keeps working. + * + * Parameters and variables are not part of it. v3 loads them on demand + * (the parameters of what is open, the parameter tree one level at a time, + * and a search file in the background), so they start empty here. v1's + * variable modules, modelled policies and SPM settings are not published + * by v2 and stay empty too. + */ + +type Schemas = components['schemas']; + +type RegionOption = Schemas['MetadataRegionOption']; +type V1RegionType = MetadataRegionEntry['type']; + +/** Region types v1 listed per country; v2 also lists places, which v3 never offered. */ +const V1_REGION_TYPES: Record = { + us: ['national', 'state', 'congressional_district'], + uk: ['national', 'country', 'constituency', 'local_authority'], +}; + +/** Whether v1 listed a region of this option's type, for a country offering `offered`. */ +function offeredByV1(offered: readonly V1RegionType[]) { + return (option: RegionOption): option is RegionOption & { type: V1RegionType } => + offered.some((type) => type === option.type); +} + +function regionEntries( + countryId: string, + catalog: Schemas['MetadataModelCatalogResult'] +): MetadataRegionEntry[] { + const offered = V1_REGION_TYPES[countryId] ?? ['national']; + const regionsByCode = new Map(catalog.regions.map((region) => [region.code, region])); + return catalog.economy_options.region + .filter(offeredByV1(offered)) + .map((option): MetadataRegionEntry => { + const region = regionsByCode.get(option.name); + return { + name: option.name, + label: option.label, + type: option.type, + // v1 gave the state only for congressional districts. + ...(option.type === 'congressional_district' && region?.state_code + ? { state_abbreviation: region.state_code, state_name: region.state_name ?? '' } + : {}), + }; + }); +} + +function entityCollection( + entities: Schemas['MetadataEntity'][] +): Record { + return Object.fromEntries( + entities.map((entity) => [ + entity.key, + { key: entity.key, plural: entity.plural, is_person: entity.is_person }, + ]) + ); +} + +/** A country's model metadata at start, from the v2 routes, as a v1 metadata envelope. */ +export async function fetchV2ModelCatalogMetadata( + countryId: string, + client: PublicApiClient = createPublicApiClient() +): Promise { + const catalog = await requireResult( + client.GET('/v2/model-catalog', { + params: { query: { country_id: publicApiCountryId(countryId) } }, + }) + ); + + return { + status: 'ok', + message: null, + result: { + variables: {}, + parameters: {}, + entities: entityCollection(catalog.entities), + variableModules: {}, + economy_options: { + region: regionEntries(countryId, catalog), + time_period: catalog.economy_options.time_period.map((period) => ({ + name: period.name, + label: period.label, + })), + datasets: catalog.economy_options.datasets.map((dataset) => ({ + name: dataset.name, + label: dataset.label, + title: dataset.label, + default: dataset.default, + })), + }, + current_law_id: catalog.model_version.current_law_id, + basicInputs: catalog.basic_inputs, + modelled_policies: { core: {}, filtered: {} }, + version: catalog.policyengine_version, + v2Catalog: { + policyengineVersion: catalog.policyengine_version, + taxBenefitModelId: catalog.model.id, + }, + }, + }; +} diff --git a/app/src/api/publicApi/openapi.json b/app/src/api/publicApi/openapi.json new file mode 100644 index 000000000..505a52475 --- /dev/null +++ b/app/src/api/publicApi/openapi.json @@ -0,0 +1,14228 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "PolicyEngine API", + "version": "3.38.1" + }, + "paths": { + "/v2/me": { + "get": { + "summary": "Read the current user", + "description": "For a bearer access token, returns the PolicyEngine user, creating it on the first call with primary_country set from country_id. For the PolicyEngine-Unauthenticated-User header, returns the browser's unauthenticated user, or 404 before its first saved record; reading never creates one.", + "operationId": "get_me_v2_me_get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MeDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "patch": { + "summary": "Update the signed-in user's names or primary country", + "description": "Unauthenticated users cannot edit account details (403).", + "operationId": "patch_me_v2_me_patch", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MeDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "delete": { + "summary": "Delete the current user and all of their saved records", + "description": "For a signed-in user, obtains Auth0 Management credentials first when they are configured, then deletes the user's legacy user mappings, the user (identities and associations cascade), and the Auth0 user. For an unauthenticated user, deletes the user and its associations and never calls Auth0; 204 even when it does not exist.", + "operationId": "delete_me_v2_me_delete", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "502": { + "description": "Account data was deleted but the Auth0 user could not be removed; retrying is safe.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Auth0 Management credentials could not be obtained, or persistence is unavailable; nothing was deleted.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/me/export": { + "get": { + "summary": "Export the current user and every saved record", + "description": "Covers every country, not only country_id. user is null, and every list empty, for an unauthenticated user with no saved records.", + "operationId": "export_me_v2_me_export_get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MeExportResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/me/upgrade": { + "post": { + "summary": "Move this browser's unauthenticated user into the signed-in account", + "description": "Requires both a bearer access token and the PolicyEngine-Unauthenticated-User header. When the signed-in identity has no account yet, the unauthenticated user becomes it in place, keeping its ID and records (converted). When the identity already has an account, every record moves to that account with its ID unchanged and the unauthenticated user is deleted (merged). When the header names no unauthenticated user, including a signed-in account's ID, nothing moves (none). The response is the signed-in account.", + "operationId": "upgrade_me_v2_me_upgrade_post", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "PolicyEngine-Unauthenticated-User", + "in": "header", + "required": true, + "schema": { + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + "description": "The browser's unauthenticated user ID (a canonical UUID).", + "title": "Policyengine-Unauthenticated-User" + }, + "description": "The browser's unauthenticated user ID (a canonical UUID)." + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MeUpgradeResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-reports": { + "post": { + "summary": "Save a v1 report for the current user", + "description": "Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Stage 12A accepts legacy_report_id only (the requested v1 report output ID), checked in the v1 database. An optional user_policy_id must name the same user's saved policy in the same country.", + "operationId": "create_user_report_v2_user_reports_post", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserReportCreateRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserReportDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "get": { + "summary": "List the current user's saved reports", + "operationId": "get_user_reports_v2_user_reports_get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "legacy_report_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact country-scoped v1 database identifier" + }, + { + "type": "null" + } + ], + "title": "Legacy Report Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserReportPageResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-reports/{association_id}": { + "get": { + "summary": "Read one of the current user's saved reports", + "operationId": "get_user_report_v2_user_reports__association_id__get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserReportDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "patch": { + "summary": "Update or repoint one of the current user's saved reports", + "operationId": "patch_user_report_route_v2_user_reports__association_id__patch", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserReportPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserReportDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "delete": { + "summary": "Delete one of the current user's saved reports", + "operationId": "delete_user_report_route_v2_user_reports__association_id__delete", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/shared-reports/{association_id}": { + "get": { + "summary": "Read a saved report by its shareable link", + "description": "Needs no credential: a saved report's ID is its link, and anyone holding it can read the report. The owner and the owner's saved-reform IDs are left out; the reform the report was compared with is named instead.", + "operationId": "get_shared_report_v2_shared_reports__association_id__get", + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SharedUserReportDetailResponse" + } + } + } + }, + "404": { + "description": "No saved report has this ID in this country.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The ID or the query does not match the schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-simulations": { + "post": { + "summary": "Save a v1 simulation for the current user", + "description": "Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Stage 12A accepts legacy_simulation_id only, checked in the v1 database.", + "operationId": "create_user_simulation_v2_user_simulations_post", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSimulationCreateRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSimulationDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "get": { + "summary": "List the current user's saved simulations", + "operationId": "get_user_simulations_v2_user_simulations_get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "legacy_simulation_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact country-scoped v1 database identifier" + }, + { + "type": "null" + } + ], + "title": "Legacy Simulation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSimulationPageResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-simulations/{association_id}": { + "get": { + "summary": "Read one of the current user's saved simulations", + "operationId": "get_user_simulation_v2_user_simulations__association_id__get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSimulationDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "patch": { + "summary": "Update or repoint one of the current user's saved simulations", + "operationId": "patch_user_simulation_route_v2_user_simulations__association_id__patch", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSimulationPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSimulationDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "delete": { + "summary": "Delete one of the current user's saved simulations", + "operationId": "delete_user_simulation_route_v2_user_simulations__association_id__delete", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-households": { + "post": { + "summary": "Save a household for the current user", + "description": "Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Accepts legacy_household_id (a v1 household, checked in the v1 database) or household_id (a v2 household).", + "operationId": "create_user_household_v2_user_households_post", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserHouseholdCreateRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserHouseholdDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "get": { + "summary": "List the current user's saved households", + "operationId": "get_user_households_v2_user_households_get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "household_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Household Id" + } + }, + { + "name": "legacy_household_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact country-scoped v1 database identifier" + }, + { + "type": "null" + } + ], + "title": "Legacy Household Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserHouseholdPageResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-households/{association_id}": { + "get": { + "summary": "Read one of the current user's saved households", + "operationId": "get_user_household_v2_user_households__association_id__get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserHouseholdDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "patch": { + "summary": "Update or repoint one of the current user's saved households", + "description": "The row keeps its id, owner and creation time. Changing legacy_household_id repoints the row and re-resolves household_id from the Stage 11 copy mapping, or clears it. Changing household_id alone points the row at that v2 household, which must exist in the same country, and clears legacy_household_id. Supplied together, both are stored, and household_id must be the copy of legacy_household_id when one exists.", + "operationId": "patch_user_household_route_v2_user_households__association_id__patch", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserHouseholdPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserHouseholdDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "delete": { + "summary": "Delete one of the current user's saved households", + "operationId": "delete_user_household_route_v2_user_households__association_id__delete", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-policies": { + "post": { + "summary": "Save a policy for the current user", + "description": "Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Accepts legacy_policy_id (a v1 policy, checked in the v1 database) or policy_id (a v2 policy). When only legacy_policy_id is supplied, policy_id is filled from the Stage 10 mirror mapping when one exists.", + "operationId": "create_user_policy_v2_user_policies_post", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPolicyCreateRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPolicyDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "get": { + "summary": "List the current user's saved policies", + "operationId": "get_user_policies_v2_user_policies_get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policy_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Policy Id" + } + }, + { + "name": "legacy_policy_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact country-scoped v1 database identifier" + }, + { + "type": "null" + } + ], + "title": "Legacy Policy Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPolicyPageResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/user-policies/{association_id}": { + "get": { + "summary": "Read one of the current user's saved policies", + "operationId": "get_user_policy_v2_user_policies__association_id__get", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPolicyDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "patch": { + "summary": "Update or repoint one of the current user's saved policies", + "description": "The row keeps its id, owner and creation time. Changing legacy_policy_id repoints the row and re-resolves policy_id from the Stage 10 mirror mapping, or clears it. Changing policy_id alone points the row at that v2 policy, which must exist in the same country, and clears legacy_policy_id. Supplied together, both are stored, and policy_id must be the mirror copy of legacy_policy_id when one exists. Rows maintained by the v1 saved-policy mirror return 409.", + "operationId": "patch_user_policy_route_v2_user_policies__association_id__patch", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPolicyPatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPolicyDetailResponse" + } + } + } + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "delete": { + "summary": "Delete one of the current user's saved policies", + "operationId": "delete_user_policy_route_v2_user_policies__association_id__delete", + "security": [ + { + "UserAccessToken": [] + }, + { + "UnauthenticatedUser": [] + } + ], + "parameters": [ + { + "name": "association_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Association Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "400": { + "description": "The supplied references or countries contradict each other.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "401": { + "description": "A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "403": { + "description": "Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The record does not exist or belongs to another user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The request body exceeds 256 KiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the schema, or a referenced v1 artifact or linked record does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The operation could not be completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "User authentication or persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/households": { + "post": { + "summary": "Create or find an immutable household", + "operationId": "create_household_v2_households_post", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HouseholdCreateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Equivalent immutable content already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HouseholdDetailResponse" + } + } + } + }, + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HouseholdDetailResponse" + } + } + } + }, + "400": { + "description": "The household content or country selection is invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The selected household does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "Immutable household content conflicts with stored state.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The household request body exceeds 1 MiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the household schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "Stored household integrity validation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Supabase household persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "get": { + "summary": "List immutable households", + "operationId": "get_households_v2_households_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "default_year", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer", + "maximum": 2200, + "minimum": 1900, + "description": "Default year for values without explicit period keys" + }, + { + "type": "null" + } + ], + "title": "Default Year" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HouseholdPageResponse" + } + } + } + }, + "400": { + "description": "The household content or country selection is invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The selected household does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "Immutable household content conflicts with stored state.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The household request body exceeds 1 MiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the household schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "Stored household integrity validation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Supabase household persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/households/{household_id}": { + "get": { + "summary": "Read one immutable household", + "operationId": "get_household_v2_households__household_id__get", + "parameters": [ + { + "name": "household_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Household Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HouseholdDetailResponse" + } + } + } + }, + "400": { + "description": "The household content or country selection is invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The selected household does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "Immutable household content conflicts with stored state.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The household request body exceeds 1 MiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the household schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "Stored household integrity validation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Supabase household persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/policies": { + "post": { + "summary": "Create or find an immutable policy", + "operationId": "create_policy_v2_policies_post", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyCreateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Equivalent immutable content already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyDetailResponse" + } + } + } + }, + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyDetailResponse" + } + } + } + }, + "400": { + "description": "The policy content or country selection is invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The selected policy or catalog does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "Immutable policy identity conflicts with stored state.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The policy request body exceeds 1 MiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the policy schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "Stored policy integrity validation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Supabase policy persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + }, + "get": { + "summary": "List immutable policies", + "operationId": "get_policies_v2_policies_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "tax_benefit_model_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Tax Benefit Model Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyPageResponse" + } + } + } + }, + "400": { + "description": "The policy content or country selection is invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The selected policy or catalog does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "Immutable policy identity conflicts with stored state.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The policy request body exceeds 1 MiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the policy schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "Stored policy integrity validation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Supabase policy persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/policies/{policy_id}": { + "get": { + "summary": "Read one immutable policy", + "operationId": "get_policy_v2_policies__policy_id__get", + "parameters": [ + { + "name": "policy_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Policy Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PolicyDetailResponse" + } + } + } + }, + "400": { + "description": "The policy content or country selection is invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The selected policy or catalog does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "409": { + "description": "Immutable policy identity conflicts with stored state.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "413": { + "description": "The policy request body exceeds 1 MiB.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The request does not match the policy schema.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "Stored policy integrity validation failed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "Supabase policy persistence is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/tax-benefit-models": { + "get": { + "summary": "List models for one PolicyEngine.py catalog", + "operationId": "list_models_v2_tax_benefit_models_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataModelPageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/tax-benefit-models/by-country/{country_id}": { + "get": { + "summary": "Get a country model and selected PolicyEngine.py version", + "operationId": "get_model_by_country_v2_tax_benefit_models_by_country__country_id__get", + "parameters": [ + { + "name": "country_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Country Id" + } + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataModelSelectionResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The country in the path is not supported. Query parameters are checked first: an invalid one, including the PolicyEngine.py version, returns 422.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/tax-benefit-models/{model_id}": { + "get": { + "summary": "Get one model from a selected PolicyEngine.py catalog", + "operationId": "get_model_v2_tax_benefit_models__model_id__get", + "parameters": [ + { + "name": "model_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Model Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataModelDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/tax-benefit-model-versions": { + "get": { + "summary": "List selected PolicyEngine.py model versions", + "operationId": "list_model_versions_v2_tax_benefit_model_versions_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataModelVersionPageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/tax-benefit-model-versions/{version_id}": { + "get": { + "summary": "Get one selected PolicyEngine.py model version", + "operationId": "get_model_version_v2_tax_benefit_model_versions__version_id__get", + "parameters": [ + { + "name": "version_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Version Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataModelVersionDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/variables": { + "get": { + "summary": "List variables from a selected PolicyEngine.py catalog", + "operationId": "list_variables_v2_variables_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Case-insensitive substring of the name, label or description", + "title": "Search" + }, + "description": "Case-insensitive substring of the name, label or description" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataVariablePageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/variables/{variable_id}": { + "get": { + "summary": "Get one variable from a selected PolicyEngine.py catalog", + "operationId": "get_variable_v2_variables__variable_id__get", + "parameters": [ + { + "name": "variable_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Variable Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataVariableDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/parameters": { + "get": { + "summary": "List parameters from a selected PolicyEngine.py catalog", + "operationId": "list_parameters_v2_parameters_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Case-insensitive substring of the name, label or description", + "title": "Search" + }, + "description": "Case-insensitive substring of the name, label or description" + }, + { + "name": "name", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 512, + "description": "Exact parameter path" + }, + "maxItems": 100 + }, + { + "type": "null" + } + ], + "description": "Keep only these exact parameter paths; repeat the key", + "title": "Name" + }, + "description": "Keep only these exact parameter paths; repeat the key" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataParameterPageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/parameters/children": { + "get": { + "summary": "List one level of a catalog version's parameter tree", + "description": "Lists the direct children of `parent_path` (the top level when empty). With `reform_changeable=true`, only the children a reform can change, sorted by label, with child counts that count only those. Invalid query parameters return 422, including a malformed policyengine_version, an unsupported country, and an unknown or repeated parameter. A catalog version without a tree (publication writes trees only for the versions it publishes and for the installed version with catalog format 2) returns 503 with a message naming it.", + "operationId": "list_parameter_children_v2_parameters_children_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + }, + { + "name": "parent_path", + "in": "query", + "required": false, + "schema": { + "type": "string", + "maxLength": 512, + "description": "Path of the node whose direct children are listed; empty for the top level. A bracket schedule's brackets are its children.", + "default": "", + "title": "Parent Path" + }, + "description": "Path of the node whose direct children are listed; empty for the top level. A bracket schedule's brackets are its children." + }, + { + "name": "reform_changeable", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "List only the children a reform can change, sorted by label, with child counts that count only those", + "default": false, + "title": "Reform Changeable" + }, + "description": "List only the children a reform can change, sorted by label, with child counts that count only those" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataParameterChildPageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable, or the catalog version has no parameter tree (publication writes trees only for the versions it publishes and for the installed version with catalog format 2); the message then names the version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/parameters/search-file": { + "get": { + "summary": "Get a catalog version's parameter search file", + "operationId": "get_parameter_search_file_v2_parameters_search_file_get", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "The catalog version's parameter search file, gzip-encoded when the request accepts gzip.", + "headers": { + "ETag": { + "description": "Strong tag of this representation: the SHA-256 of the uncompressed JSON, with \"-gzip\" appended for the gzip-encoded one.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + }, + "Content-Encoding": { + "description": "gzip when the request accepts gzip; absent otherwise.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataParameterSearchFileContent" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable, or the catalog version has no parameter search file: only catalog format 2 versions that publication has published, or run with installed, since search files were added have one. The message then names the version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/parameters/{parameter_id}": { + "get": { + "summary": "Get one parameter from a selected PolicyEngine.py catalog", + "operationId": "get_parameter_v2_parameters__parameter_id__get", + "parameters": [ + { + "name": "parameter_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Parameter Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataParameterDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/parameter-values": { + "get": { + "summary": "List canonical values from a selected PolicyEngine.py catalog", + "operationId": "list_parameter_values_v2_parameter_values_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + }, + { + "name": "parameter_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Parameter Id" + } + }, + { + "name": "parameter_name", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 512, + "description": "Exact parameter path" + }, + "maxItems": 100 + }, + { + "type": "null" + } + ], + "description": "Keep only these exact parameter paths' values; repeat the key", + "title": "Parameter Name" + }, + "description": "Keep only these exact parameter paths' values; repeat the key" + }, + { + "name": "current", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Keep only the values in effect today (UTC)", + "default": false, + "title": "Current" + }, + "description": "Keep only the values in effect today (UTC)" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataParameterValuePageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/parameter-values/{value_id}": { + "get": { + "summary": "Get one canonical value from a selected PolicyEngine.py catalog", + "operationId": "get_parameter_value_v2_parameter_values__value_id__get", + "parameters": [ + { + "name": "value_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Value Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataParameterValueDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/datasets": { + "get": { + "summary": "List logical inputs from a selected PolicyEngine.py catalog", + "operationId": "list_datasets_v2_datasets_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataDatasetPageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/datasets/{dataset_id}": { + "get": { + "summary": "Get one logical input from a selected PolicyEngine.py catalog", + "operationId": "get_dataset_v2_datasets__dataset_id__get", + "parameters": [ + { + "name": "dataset_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Dataset Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataDatasetDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/regions": { + "get": { + "summary": "List regions from a selected PolicyEngine.py catalog", + "operationId": "list_regions_v2_regions_get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 0, + "description": "Zero-based result offset", + "default": 0, + "title": "Offset" + }, + "description": "Zero-based result offset" + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 500, + "minimum": 1, + "description": "Maximum number of returned resources", + "default": 100, + "title": "Limit" + }, + "description": "Maximum number of returned resources" + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + }, + { + "name": "region_type", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetadataRegionType" + }, + { + "type": "null" + } + ], + "description": "Keep only regions of this type", + "title": "Region Type" + }, + "description": "Keep only regions of this type" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataRegionPageResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/regions/by-code/{region_code}": { + "get": { + "summary": "Get one region by code from a selected catalog", + "operationId": "get_region_by_code_v2_regions_by_code__region_code__get", + "parameters": [ + { + "name": "region_code", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Region Code" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataRegionDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/regions/{region_id}": { + "get": { + "summary": "Get one region from a selected PolicyEngine.py catalog", + "operationId": "get_region_v2_regions__region_id__get", + "parameters": [ + { + "name": "region_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "title": "Region Id" + } + }, + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataRegionDetailResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/economy-options": { + "get": { + "summary": "Get compact economy-selection options from a selected catalog", + "operationId": "get_economy_options_v2_economy_options_get", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataEconomyOptionsResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/model-catalog": { + "get": { + "summary": "Get the small parts of one PolicyEngine.py catalog version", + "description": "Returns the selected catalog version's model, model version, entities, basic inputs, regions, logical input datasets and economy choices, which are empty lists for a catalog without regions. The result names the selected PolicyEngine.py version, so a client can request the version's other parts by that exact version; a response for a named version may be reused for a day.", + "operationId": "get_model_catalog_v2_model_catalog_get", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataModelCatalogResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/model-catalog/variables": { + "get": { + "summary": "Get every variable of one PolicyEngine.py catalog version", + "description": "Returns all of the selected catalog version's variables in name order, in one response. Where a variable's adds or subtracts list comes from a parameter, part_parameter_values carries that parameter's current-law values by date, so a client can pick the list for any year.", + "operationId": "get_catalog_variables_v2_model_catalog_variables_get", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataCatalogVariablesResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + }, + "/v2/model-catalog/parameters": { + "get": { + "summary": "Get several parameters of one catalog version by name", + "description": "Returns the named parameters (repeat name for each, at most 100) in name order, each with its economy and household flags and its current-law values by date, plus the stored labels of their ancestor nodes for breadcrumbs and the requested names the version has no parameter for. A client showing or editing a reform loads all of its parameters in this one request.", + "operationId": "get_catalog_parameters_v2_model_catalog_parameters_get", + "parameters": [ + { + "name": "country_id", + "in": "query", + "required": true, + "schema": { + "enum": [ + "us", + "uk" + ], + "type": "string", + "description": "Supported PolicyEngine country ID", + "title": "Country Id" + }, + "description": "Supported PolicyEngine country ID" + }, + { + "name": "policyengine_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "description": "Canonical non-placeholder PEP 440 version" + }, + { + "type": "null" + } + ], + "title": "Policyengine Version" + } + }, + { + "name": "name", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string", + "minLength": 1, + "maxLength": 512, + "description": "Exact parameter path" + }, + "minItems": 1, + "maxItems": 100, + "description": "Exact parameter paths; repeat the key for each one", + "title": "Name" + }, + "description": "Exact parameter paths; repeat the key for each one" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MetadataCatalogParametersResponse" + } + } + }, + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "304": { + "description": "Not modified: If-None-Match names the current ETag. No body is sent.", + "headers": { + "ETag": { + "description": "Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag.", + "schema": { + "type": "string" + } + }, + "Cache-Control": { + "description": "public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight.", + "schema": { + "type": "string" + } + }, + "Vary": { + "description": "Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value.", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The requested catalog resource is absent.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "405": { + "description": "The dormant v2 metadata resources support GET only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "422": { + "description": "The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "500": { + "description": "The resource query failed internally.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + }, + "503": { + "description": "The initialized v2 catalog is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/V2ErrorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AccountPatchRequest": { + "properties": { + "first_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "First Name" + }, + "last_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Last Name" + }, + "primary_country": { + "anyOf": [ + { + "type": "string", + "enum": [ + "us", + "uk" + ], + "description": "Supported PolicyEngine country ID" + }, + { + "type": "null" + } + ], + "title": "Primary Country" + } + }, + "additionalProperties": false, + "type": "object", + "title": "AccountPatchRequest", + "description": "Explicitly supplied account presentation fields." + }, + "FiniteJsonValue": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/FiniteJsonValue" + }, + "type": "array" + }, + { + "additionalProperties": { + "$ref": "#/components/schemas/FiniteJsonValue" + }, + "type": "object" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "HealthResponse": { + "properties": { + "status": { + "type": "string", + "const": "healthy", + "title": "Status" + } + }, + "type": "object", + "required": [ + "status" + ], + "title": "HealthResponse", + "description": "Health-check result showing that the API is up and serving requests." + }, + "HouseholdCreateRequest": { + "properties": { + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "default_year": { + "type": "integer", + "maximum": 2200, + "minimum": 1900, + "title": "Default Year", + "description": "Default year for values without explicit period keys" + }, + "household_data": { + "anyOf": [ + { + "$ref": "#/components/schemas/USHouseholdDocument-Input" + }, + { + "$ref": "#/components/schemas/UKHouseholdDocument" + } + ], + "title": "Household Data" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "country_id", + "default_year", + "household_data" + ], + "title": "HouseholdCreateRequest", + "description": "Native immutable household content with no presentation or model fields." + }, + "HouseholdDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/HouseholdDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "HouseholdDetailResponse", + "description": "Success response holding one household." + }, + "HouseholdDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/HouseholdItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "HouseholdDetailResult", + "description": "Result holding one household in ``item``." + }, + "HouseholdEntityRecord": { + "properties": { + "id": { + "type": "string", + "maxLength": 255, + "minLength": 1, + "title": "Id" + }, + "source_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + { + "type": "null" + } + ], + "title": "Source Name" + }, + "values": { + "additionalProperties": { + "$ref": "#/components/schemas/JsonValue" + }, + "type": "object", + "maxProperties": 500, + "title": "Values" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "values" + ], + "title": "HouseholdEntityRecord", + "description": "One group entity in a household, such as a tax unit or a benefit unit.\n\n``id`` is unique within its collection and is what people's ``memberships``\nrefer to. ``values`` maps variable names to values, and ``source_name``\noptionally keeps the entity's original name, such as its key in a v1 household." + }, + "HouseholdItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "default_year": { + "anyOf": [ + { + "type": "integer", + "maximum": 2200, + "minimum": 1900, + "description": "Default year for values without explicit period keys" + }, + { + "type": "null" + } + ], + "title": "Default Year" + }, + "household_data": { + "anyOf": [ + { + "$ref": "#/components/schemas/USHouseholdDocument-Output" + }, + { + "$ref": "#/components/schemas/UKHouseholdDocument" + } + ], + "title": "Household Data" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "default_year", + "household_data", + "created_at", + "updated_at" + ], + "title": "HouseholdItem", + "description": "A stored household.\n\nHouseholds are immutable and deduplicated: creating one with identical\ncontent returns the existing household and its ``id``." + }, + "HouseholdPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/HouseholdPageResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "HouseholdPageResponse", + "description": "Success response holding one page of households." + }, + "HouseholdPageResult": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/HouseholdItem" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "items", + "offset", + "limit", + "has_more" + ], + "title": "HouseholdPageResult", + "description": "One page of households, oldest first.\n\n``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask\nfor the next page with ``offset + limit``." + }, + "HouseholdPersonRecord": { + "properties": { + "id": { + "type": "string", + "maxLength": 255, + "minLength": 1, + "title": "Id" + }, + "source_name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + { + "type": "null" + } + ], + "title": "Source Name" + }, + "values": { + "additionalProperties": { + "$ref": "#/components/schemas/JsonValue" + }, + "type": "object", + "maxProperties": 500, + "title": "Values" + }, + "memberships": { + "additionalProperties": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "type": "object", + "title": "Memberships" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "values", + "memberships" + ], + "title": "HouseholdPersonRecord", + "description": "One person in a household.\n\n``memberships`` maps each of the country's entity collections to the ``id``\nof the group the person belongs to, and must name every collection." + }, + "JsonValue": {}, + "MeDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MeDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MeDetailResponse", + "description": "Success response holding the current user." + }, + "MeDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/MeItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "MeDetailResult", + "description": "Result holding the current user in ``item``." + }, + "MeExportResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MeExportResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MeExportResponse", + "description": "Success response holding the current user's export." + }, + "MeExportResult": { + "properties": { + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/MeItem" + }, + { + "type": "null" + } + ] + }, + "policies": { + "items": { + "$ref": "#/components/schemas/UserPolicyItem" + }, + "type": "array", + "title": "Policies" + }, + "households": { + "items": { + "$ref": "#/components/schemas/UserHouseholdItem" + }, + "type": "array", + "title": "Households" + }, + "simulations": { + "items": { + "$ref": "#/components/schemas/UserSimulationItem" + }, + "type": "array", + "title": "Simulations" + }, + "reports": { + "items": { + "$ref": "#/components/schemas/UserReportItem" + }, + "type": "array", + "title": "Reports" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "user", + "policies", + "households", + "simulations", + "reports" + ], + "title": "MeExportResult", + "description": "The current user and every policy, household, simulation and report it saved.\n\nThe export covers every country, not only the requested one." + }, + "MeItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "user_type": { + "type": "string", + "enum": [ + "unauthenticated", + "standard" + ], + "title": "User Type" + }, + "primary_country": { + "anyOf": [ + { + "type": "string", + "enum": [ + "us", + "uk" + ], + "description": "Supported PolicyEngine country ID" + }, + { + "type": "null" + } + ], + "title": "Primary Country" + }, + "first_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "First Name" + }, + "last_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Name" + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "user_type", + "primary_country", + "first_name", + "last_name", + "email", + "created_at" + ], + "title": "MeItem", + "description": "The current user: a signed-in account or a browser's unauthenticated user.\n\n``user_type`` says which. An unauthenticated user has no primary country, and\nonly a signed-in user can set its names or primary country." + }, + "MeUpgradeResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MeUpgradeResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MeUpgradeResponse", + "description": "Success response holding the signed-in account after an upgrade." + }, + "MeUpgradeResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/MeItem" + }, + "outcome": { + "type": "string", + "enum": [ + "converted", + "merged", + "none" + ], + "title": "Outcome" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item", + "outcome" + ], + "title": "MeUpgradeResult", + "description": "The signed-in account after a browser's unauthenticated user moved into it.\n\n``outcome`` is ``converted`` when the unauthenticated user became the account\nin place, ``merged`` when its records moved to an existing account, and\n``none`` when there was no unauthenticated user to move." + }, + "MetadataCanonicalParameterValue": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "parameter_id": { + "type": "string", + "format": "uuid", + "title": "Parameter Id" + }, + "value": { + "$ref": "#/components/schemas/JsonValue" + }, + "start_date": { + "type": "string", + "format": "date-time", + "title": "Start Date" + }, + "end_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "End Date" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "parameter_id", + "value", + "start_date", + "end_date" + ], + "title": "MetadataCanonicalParameterValue", + "description": "One current-law value of a parameter.\n\nThe value applies from start_date until end_date, or indefinitely when\nend_date is null." + }, + "MetadataCatalogParameter": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Label" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "data_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Data Type" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + }, + "economy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Economy" + }, + "household": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Household" + }, + "values": { + "items": { + "$ref": "#/components/schemas/MetadataDatedValue" + }, + "type": "array", + "title": "Values" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "name", + "label", + "description", + "data_type", + "unit", + "economy", + "household", + "values" + ], + "title": "MetadataCatalogParameter", + "description": "A parameter with its current-law values in start-date order." + }, + "MetadataCatalogParametersResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataCatalogParametersResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataCatalogParametersResponse", + "description": "Success response holding parameters fetched by name, with their values." + }, + "MetadataCatalogParametersResult": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "catalog_format": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Catalog Format" + }, + "parameters": { + "items": { + "$ref": "#/components/schemas/MetadataCatalogParameter" + }, + "type": "array", + "title": "Parameters" + }, + "node_labels": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "type": "object", + "title": "Node Labels" + }, + "missing_names": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Missing Names" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "catalog_format", + "parameters", + "node_labels", + "missing_names" + ], + "title": "MetadataCatalogParametersResult", + "description": "Requested parameters of one catalog version, in name order.\n\nnode_labels maps each ancestor of a requested parameter that the version\nstores as a parameter node to the node's stored label (null when it has\nnone), for breadcrumbs; a bracket index such as ``rates[0]`` counts as a\nchild of its schedule ``rates``. missing_names lists requested names the\nversion has no parameter for." + }, + "MetadataCatalogVariablesResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataCatalogVariablesResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataCatalogVariablesResponse", + "description": "Success response holding every variable of one catalog version." + }, + "MetadataCatalogVariablesResult": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "catalog_format": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Catalog Format" + }, + "variables": { + "items": { + "$ref": "#/components/schemas/MetadataVariable" + }, + "type": "array", + "title": "Variables" + }, + "part_parameter_values": { + "additionalProperties": { + "items": { + "$ref": "#/components/schemas/MetadataDatedValue" + }, + "type": "array" + }, + "type": "object", + "title": "Part Parameter Values" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "catalog_format", + "variables", + "part_parameter_values" + ], + "title": "MetadataCatalogVariablesResult", + "description": "Every variable of one catalog version, in name order.\n\npart_parameter_values has an entry for each parameter path a variable's\nadds_parameter_path or subtracts_parameter_path names: that parameter's\ncurrent-law values in start-date order, or an empty list when the\ncatalog has no such parameter." + }, + "MetadataDataset": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "year": { + "type": "integer", + "title": "Year" + }, + "storage_path": { + "type": "null", + "title": "Storage Path" + }, + "is_output_dataset": { + "type": "boolean", + "const": false, + "title": "Is Output Dataset", + "default": false + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "name", + "description", + "year" + ], + "title": "MetadataDataset", + "description": "A named input dataset available to the selected model version.\n\nThese datasets are logical inputs, so storage_path is always null and\nis_output_dataset is always false." + }, + "MetadataDatasetDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataDataset_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataDatasetDetailResponse", + "description": "Success response holding one dataset." + }, + "MetadataDatasetOption": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "label": { + "type": "string", + "title": "Label" + }, + "default": { + "type": "boolean", + "const": true, + "title": "Default", + "default": true + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name", + "label" + ], + "title": "MetadataDatasetOption", + "description": "The national dataset, offered as the default for an economy calculation." + }, + "MetadataDatasetPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataDataset_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataDatasetPageResponse", + "description": "Success response holding one page of datasets." + }, + "MetadataDatedValue": { + "properties": { + "start_date": { + "type": "string", + "format": "date-time", + "title": "Start Date" + }, + "end_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "End Date" + }, + "value": { + "$ref": "#/components/schemas/JsonValue" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "start_date", + "end_date", + "value" + ], + "title": "MetadataDatedValue", + "description": "A current-law parameter value from start_date until end_date.\n\nA null end_date means the value applies until a later value starts, or\nindefinitely." + }, + "MetadataDetailResult_MetadataCanonicalParameterValue_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataCanonicalParameterValue" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataCanonicalParameterValue]" + }, + "MetadataDetailResult_MetadataDataset_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataDataset" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataDataset]" + }, + "MetadataDetailResult_MetadataModelVersionDetail_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataModelVersionDetail" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataModelVersionDetail]" + }, + "MetadataDetailResult_MetadataModel_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataModel" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataModel]" + }, + "MetadataDetailResult_MetadataParameterSummary_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataParameterSummary" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataParameterSummary]" + }, + "MetadataDetailResult_MetadataRegion_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataRegion" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataRegion]" + }, + "MetadataDetailResult_MetadataVariable_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "item": { + "$ref": "#/components/schemas/MetadataVariable" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "item" + ], + "title": "MetadataDetailResult[MetadataVariable]" + }, + "MetadataEconomyOptions": { + "properties": { + "region": { + "items": { + "$ref": "#/components/schemas/MetadataRegionOption" + }, + "type": "array", + "title": "Region" + }, + "time_period": { + "items": { + "$ref": "#/components/schemas/MetadataTimePeriodOption" + }, + "type": "array", + "title": "Time Period" + }, + "datasets": { + "items": { + "$ref": "#/components/schemas/MetadataDatasetOption" + }, + "type": "array", + "title": "Datasets" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "region", + "time_period", + "datasets" + ], + "title": "MetadataEconomyOptions", + "description": "The region, year and dataset choices for an economy calculation.\n\nEvery list is empty for a catalog without regions, such as a\nhousehold-only country's, which offers no economy calculation." + }, + "MetadataEconomyOptionsResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataEconomyOptionsResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataEconomyOptionsResponse", + "description": "Success response holding the options for configuring an economy comparison." + }, + "MetadataEconomyOptionsResult": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "current_law_id": { + "type": "integer", + "title": "Current Law Id" + }, + "region": { + "items": { + "$ref": "#/components/schemas/MetadataRegionOption" + }, + "type": "array", + "title": "Region" + }, + "time_period": { + "items": { + "$ref": "#/components/schemas/MetadataTimePeriodOption" + }, + "type": "array", + "title": "Time Period" + }, + "datasets": { + "items": { + "$ref": "#/components/schemas/MetadataDatasetOption" + }, + "type": "array", + "title": "Datasets" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "current_law_id", + "region", + "time_period", + "datasets" + ], + "title": "MetadataEconomyOptionsResult", + "description": "The choices for configuring an economy calculation.\n\nIt lists the selectable regions, years and datasets, and the id of the\ncurrent-law policy to use as a baseline." + }, + "MetadataEntity": { + "properties": { + "key": { + "type": "string", + "title": "Key" + }, + "plural": { + "type": "string", + "title": "Plural" + }, + "is_person": { + "type": "boolean", + "title": "Is Person" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "key", + "plural", + "is_person" + ], + "title": "MetadataEntity", + "description": "A kind of member a household holds, such as a person or a tax unit.\n\nplural is the key the household's members of this kind are listed under,\nand is_person marks the entity every household input about a person uses." + }, + "MetadataEnumValue": { + "properties": { + "value": { + "type": "string", + "title": "Value" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Label" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "value", + "label" + ], + "title": "MetadataEnumValue", + "description": "One value of an enum variable: its stored name and its display label.\n\nThe label keeps the type the country package declares it with." + }, + "MetadataModel": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "name", + "description" + ], + "title": "MetadataModel", + "description": "A country's tax-benefit model in the selected PolicyEngine.py catalog." + }, + "MetadataModelCatalogResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataModelCatalogResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataModelCatalogResponse", + "description": "Success response holding the small parts of one catalog version." + }, + "MetadataModelCatalogResult": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "catalog_format": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Catalog Format" + }, + "model": { + "$ref": "#/components/schemas/MetadataModel" + }, + "model_version": { + "$ref": "#/components/schemas/MetadataModelVersionDetail" + }, + "entities": { + "items": { + "$ref": "#/components/schemas/MetadataEntity" + }, + "type": "array", + "title": "Entities" + }, + "basic_inputs": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Basic Inputs" + }, + "regions": { + "items": { + "$ref": "#/components/schemas/MetadataRegion" + }, + "type": "array", + "title": "Regions" + }, + "datasets": { + "items": { + "$ref": "#/components/schemas/MetadataDataset" + }, + "type": "array", + "title": "Datasets" + }, + "economy_options": { + "$ref": "#/components/schemas/MetadataEconomyOptions" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "catalog_format", + "model", + "model_version", + "entities", + "basic_inputs", + "regions", + "datasets", + "economy_options" + ], + "title": "MetadataModelCatalogResult", + "description": "The small parts of one catalog version, read when a client starts.\n\npolicyengine_version names the selected catalog version, so a client can\nrequest its other parts for that exact version. catalog_format is null,\nand entities and basic_inputs are empty, for a version published before\ncatalog format 2. Variables, parameters and their values are served by\ntheir own routes." + }, + "MetadataModelDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataModel_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataModelDetailResponse", + "description": "Success response holding one tax-benefit model." + }, + "MetadataModelPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataModel_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataModelPageResponse", + "description": "Success response holding a page of the selected catalog's tax-benefit models.\n\nA catalog has one model per country, so only the first page has an item." + }, + "MetadataModelSelectionResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataModelSelectionResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataModelSelectionResponse", + "description": "Success response holding a country's model and its selected model version." + }, + "MetadataModelSelectionResult": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "model": { + "$ref": "#/components/schemas/MetadataModel" + }, + "model_version": { + "$ref": "#/components/schemas/MetadataModelVersionDetail" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "model", + "model_version" + ], + "title": "MetadataModelSelectionResult", + "description": "A country's model together with the model version selected for the request." + }, + "MetadataModelVersionDetail": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "model_id": { + "type": "string", + "format": "uuid", + "title": "Model Id" + }, + "version": { + "type": "string", + "title": "Version" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "current_law_id": { + "type": "integer", + "title": "Current Law Id" + }, + "metadata_time_periods": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "Metadata Time Periods" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "model_id", + "version", + "description", + "current_law_id", + "metadata_time_periods" + ], + "title": "MetadataModelVersionDetail", + "description": "A model version, with its current-law policy id and the years offered." + }, + "MetadataModelVersionDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataModelVersionDetail_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataModelVersionDetailResponse", + "description": "Success response holding one model version." + }, + "MetadataModelVersionPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataModelVersionDetail_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataModelVersionPageResponse", + "description": "Success response holding a page of the selected catalog's model versions.\n\nA catalog has one model version, so only the first page has an item." + }, + "MetadataPageResult_MetadataCanonicalParameterValue_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataCanonicalParameterValue" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataCanonicalParameterValue]" + }, + "MetadataPageResult_MetadataDataset_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataDataset" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataDataset]" + }, + "MetadataPageResult_MetadataModelVersionDetail_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataModelVersionDetail" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataModelVersionDetail]" + }, + "MetadataPageResult_MetadataModel_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataModel" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataModel]" + }, + "MetadataPageResult_MetadataParameterChild_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataParameterChild" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataParameterChild]" + }, + "MetadataPageResult_MetadataParameterSummary_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataParameterSummary" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataParameterSummary]" + }, + "MetadataPageResult_MetadataRegion_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataRegion" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataRegion]" + }, + "MetadataPageResult_MetadataVariable_": { + "properties": { + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "items": { + "items": { + "$ref": "#/components/schemas/MetadataVariable" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "policyengine_version", + "items", + "offset", + "limit", + "has_more" + ], + "title": "MetadataPageResult[MetadataVariable]" + }, + "MetadataParameterChild": { + "properties": { + "path": { + "type": "string", + "title": "Path" + }, + "label": { + "type": "string", + "title": "Label" + }, + "type": { + "type": "string", + "enum": [ + "node", + "parameter" + ], + "title": "Type" + }, + "child_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Child Count" + }, + "parameter": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetadataParameterSummary" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "path", + "label", + "type" + ], + "title": "MetadataParameterChild", + "description": "A direct child in the parameter tree.\n\nA node has a child_count; a parameter leaf carries the parameter's summary." + }, + "MetadataParameterChildPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataParameterChild_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataParameterChildPageResponse", + "description": "Success response holding one page of a parameter path's direct children." + }, + "MetadataParameterDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataParameterSummary_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataParameterDetailResponse", + "description": "Success response holding one parameter." + }, + "MetadataParameterPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataParameterSummary_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataParameterPageResponse", + "description": "Success response holding one page of parameters." + }, + "MetadataParameterSearchFileContent": { + "properties": { + "format": { + "type": "integer", + "title": "Format" + }, + "country_id": { + "type": "string", + "title": "Country Id" + }, + "policyengine_version": { + "type": "string", + "title": "Policyengine Version" + }, + "parameters": { + "items": { + "$ref": "#/components/schemas/MetadataParameterSearchFileParameter" + }, + "type": "array", + "title": "Parameters" + }, + "other_parameters": { + "items": { + "$ref": "#/components/schemas/MetadataParameterSearchFileLabel" + }, + "type": "array", + "title": "Other Parameters" + }, + "nodes": { + "items": { + "$ref": "#/components/schemas/MetadataParameterSearchFileNode" + }, + "type": "array", + "title": "Nodes" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "format", + "country_id", + "policyengine_version", + "parameters", + "other_parameters", + "nodes" + ], + "title": "MetadataParameterSearchFileContent", + "description": "One catalog version's parameter search file, ordered by path.\n\nIt holds what v3's parameter search reads, without the catalog's values.\nA reader applies v1's label fallback where a label is null." + }, + "MetadataParameterSearchFileEditabilityChange": { + "properties": { + "start_date": { + "type": "string", + "format": "date", + "title": "Start Date" + }, + "editable": { + "type": "boolean", + "title": "Editable" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "start_date", + "editable" + ], + "title": "MetadataParameterSearchFileEditabilityChange", + "description": "From ``start_date`` (an ISO date), whether a draft can edit the value." + }, + "MetadataParameterSearchFileLabel": { + "properties": { + "path": { + "type": "string", + "title": "Path" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Label" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "path", + "label" + ], + "title": "MetadataParameterSearchFileLabel", + "description": "Another parameter under ``gov``: its path and stored label, or null." + }, + "MetadataParameterSearchFileNode": { + "properties": { + "path": { + "type": "string", + "title": "Path" + }, + "label": { + "type": "string", + "title": "Label" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "path", + "label" + ], + "title": "MetadataParameterSearchFileNode", + "description": "``gov`` or a node under it that has a stored label." + }, + "MetadataParameterSearchFileParameter": { + "properties": { + "path": { + "type": "string", + "title": "Path" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Label" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "editable": { + "type": "boolean", + "title": "Editable" + }, + "editable_changes": { + "items": { + "$ref": "#/components/schemas/MetadataParameterSearchFileEditabilityChange" + }, + "type": "array", + "title": "Editable Changes" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "path", + "label", + "unit", + "description", + "editable" + ], + "title": "MetadataParameterSearchFileParameter", + "description": "A parameter a reform can change, with what v3's search indexes.\n\n``label`` is the stored label, or null when none is stored. ``editable``\nsays whether a draft can edit the earliest current-law value (a number, a\nyes/no value or an infinite threshold); ``editable_changes``, present only\nwhen that changes over time and never null, lists the dates from which it\nchanges." + }, + "MetadataParameterSummary": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Label" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "data_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Data Type" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + }, + "economy": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Economy" + }, + "household": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Household" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "name", + "label", + "description", + "data_type", + "unit", + "economy", + "household" + ], + "title": "MetadataParameterSummary", + "description": "A policy parameter, described without its values.\n\nIts current-law values are listed by GET /v2/parameter-values. economy and\nhousehold say whether a reform of an economy or of a household may change\nit; they are null in versions published before catalog format 2." + }, + "MetadataParameterValueDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataCanonicalParameterValue_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataParameterValueDetailResponse", + "description": "Success response holding one parameter value." + }, + "MetadataParameterValuePageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataCanonicalParameterValue_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataParameterValuePageResponse", + "description": "Success response holding one page of parameter values." + }, + "MetadataRegion": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "code": { + "type": "string", + "title": "Code" + }, + "label": { + "type": "string", + "title": "Label" + }, + "region_type": { + "$ref": "#/components/schemas/MetadataRegionType" + }, + "requires_filter": { + "type": "boolean", + "title": "Requires Filter" + }, + "filter_field": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filter Field" + }, + "filter_value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filter Value" + }, + "filter_strategy": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filter Strategy" + }, + "parent_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Code" + }, + "state_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "State Code" + }, + "state_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "State Name" + }, + "default_dataset_id": { + "type": "string", + "format": "uuid", + "title": "Default Dataset Id" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "code", + "label", + "region_type", + "requires_filter", + "filter_field", + "filter_value", + "filter_strategy", + "parent_code", + "state_code", + "state_name", + "default_dataset_id" + ], + "title": "MetadataRegion", + "description": "A region the model can simulate, such as a country, state or constituency.\n\nWhen requires_filter is true, filter_strategy says how the region selects\nhouseholds of its default dataset. With \"row_filter\", it keeps the\nhouseholds whose filter_field column equals filter_value (or is in it,\nwhen filter_value is a JSON list)." + }, + "MetadataRegionDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataRegion_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataRegionDetailResponse", + "description": "Success response holding one region." + }, + "MetadataRegionOption": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "label": { + "type": "string", + "title": "Label" + }, + "type": { + "$ref": "#/components/schemas/MetadataRegionType" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name", + "label", + "type" + ], + "title": "MetadataRegionOption", + "description": "A region that can be chosen for an economy calculation; name is its code." + }, + "MetadataRegionPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataRegion_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataRegionPageResponse", + "description": "Success response holding one page of regions." + }, + "MetadataRegionType": { + "type": "string", + "enum": [ + "national", + "country", + "state", + "congressional_district", + "constituency", + "local_authority", + "city", + "place" + ], + "title": "MetadataRegionType", + "description": "The geographic level of a region." + }, + "MetadataTimePeriodOption": { + "properties": { + "name": { + "type": "integer", + "title": "Name" + }, + "label": { + "type": "string", + "title": "Label" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name", + "label" + ], + "title": "MetadataTimePeriodOption", + "description": "A year that can be chosen for an economy calculation." + }, + "MetadataVariable": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Label" + }, + "entity": { + "type": "string", + "title": "Entity" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "data_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Data Type" + }, + "possible_values": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Possible Values" + }, + "default_value": { + "$ref": "#/components/schemas/JsonValue" + }, + "adds": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Adds" + }, + "subtracts": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Subtracts" + }, + "is_input_variable": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Is Input Variable" + }, + "hidden_input": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Hidden Input" + }, + "module_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Module Name" + }, + "adds_parameter_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Adds Parameter Path" + }, + "subtracts_parameter_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Subtracts Parameter Path" + }, + "enum_values": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/MetadataEnumValue" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Enum Values" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + }, + "definition_period": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Definition Period" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "name", + "label", + "entity", + "description", + "data_type", + "possible_values", + "default_value", + "adds", + "subtracts", + "is_input_variable", + "hidden_input", + "module_name", + "adds_parameter_path", + "subtracts_parameter_path", + "enum_values", + "unit", + "definition_period" + ], + "title": "MetadataVariable", + "description": "A variable the tax-benefit model computes or accepts as input.\n\nadds and subtracts, when present, list the variables summed into or\nsubtracted from this one. When the model takes that list from a\nparameter, adds_parameter_path or subtracts_parameter_path names the\nparameter, whose dated values give the list for each year (adds and\nsubtracts hold its value on 2025-01-01). unit is the country package's\nunit (for example ``currency-USD``) and definition_period the period a\nvalue covers (``year``, ``month`` or ``eternity``). The catalog format 2\nfields (is_input_variable, hidden_input, module_name, the two parameter\npaths, enum_values, unit and definition_period) are null in versions\npublished before that format." + }, + "MetadataVariableDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataDetailResult_MetadataVariable_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataVariableDetailResponse", + "description": "Success response holding one variable." + }, + "MetadataVariablePageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/MetadataPageResult_MetadataVariable_" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "MetadataVariablePageResponse", + "description": "Success response holding one page of variables." + }, + "PolicyCreateRequest": { + "properties": { + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "tax_benefit_model_id": { + "type": "string", + "format": "uuid", + "title": "Tax Benefit Model Id" + }, + "parameter_values": { + "items": { + "$ref": "#/components/schemas/PolicyParameterValueInput" + }, + "type": "array", + "maxItems": 1000, + "title": "Parameter Values" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "country_id", + "tax_benefit_model_id", + "parameter_values" + ], + "title": "PolicyCreateRequest", + "description": "Native body containing immutable policy content only." + }, + "PolicyDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/PolicyDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "PolicyDetailResponse", + "description": "Success response holding one policy." + }, + "PolicyDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/PolicyItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "PolicyDetailResult", + "description": "Result holding one policy in ``item``." + }, + "PolicyItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "title": "Country Id" + }, + "tax_benefit_model_id": { + "type": "string", + "format": "uuid", + "title": "Tax Benefit Model Id" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + }, + "parameter_values": { + "items": { + "$ref": "#/components/schemas/PolicyParameterValueItem" + }, + "type": "array", + "title": "Parameter Values" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "tax_benefit_model_id", + "created_at", + "updated_at", + "parameter_values" + ], + "title": "PolicyItem", + "description": "A stored policy: parameter values for one tax-benefit model.\n\nPolicies are immutable and deduplicated: creating one with identical\ncontent returns the existing policy and its ``id``." + }, + "PolicyPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/PolicyPageResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "PolicyPageResponse", + "description": "Success response holding one page of policies." + }, + "PolicyPageResult": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/PolicyItem" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "items", + "offset", + "limit", + "has_more" + ], + "title": "PolicyPageResult", + "description": "One page of policies, oldest first.\n\n``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask\nfor the next page with ``offset + limit``." + }, + "PolicyParameterValueInput": { + "properties": { + "parameter_id": { + "type": "string", + "format": "uuid", + "title": "Parameter Id" + }, + "value": { + "$ref": "#/components/schemas/JsonValue" + }, + "start_date": { + "type": "string", + "format": "date-time", + "title": "Start Date" + }, + "end_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "End Date" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "parameter_id", + "value", + "start_date" + ], + "title": "PolicyParameterValueInput", + "description": "One normalized effective value for a catalog parameter UUID." + }, + "PolicyParameterValueItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "parameter_id": { + "type": "string", + "format": "uuid", + "title": "Parameter Id" + }, + "parameter_name": { + "type": "string", + "title": "Parameter Name" + }, + "value": { + "$ref": "#/components/schemas/JsonValue" + }, + "start_date": { + "type": "string", + "format": "date-time", + "title": "Start Date" + }, + "end_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "End Date" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "parameter_id", + "parameter_name", + "value", + "start_date", + "end_date" + ], + "title": "PolicyParameterValueItem", + "description": "One value a policy sets for a parameter from ``start_date``.\n\n``parameter_name`` is the parameter's path in the model, and a null\n``end_date`` means the value has no end." + }, + "PolicyProvenance": { + "properties": { + "source": { + "type": "string", + "enum": [ + "manual", + "chat", + "bill", + "tool" + ], + "title": "Source" + }, + "ref": { + "anyOf": [ + { + "type": "string", + "maxLength": 2048 + }, + { + "type": "null" + } + ], + "title": "Ref" + }, + "confidence": { + "anyOf": [ + { + "type": "string", + "enum": [ + "exact", + "approximated", + "partial" + ] + }, + { + "type": "null" + } + ], + "title": "Confidence" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "source" + ], + "title": "PolicyProvenance", + "description": "Where a v3 reform came from." + }, + "PolicyProvenanceItem": { + "properties": { + "source": { + "type": "string", + "title": "Source" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ref" + }, + "confidence": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Confidence" + } + }, + "type": "object", + "required": [ + "source" + ], + "title": "PolicyProvenanceItem", + "description": "Stored v3 reform provenance as responses publish it.\n\nIt mirrors the contract without the input bounds and ignores unknown keys,\nso any stored row serializes." + }, + "ReportCalibration": { + "properties": { + "release_id": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Release Id" + }, + "geography": { + "type": "string", + "maxLength": 64, + "title": "Geography" + }, + "reached_count": { + "type": "integer", + "title": "Reached Count" + }, + "matches": { + "items": { + "$ref": "#/components/schemas/ReportCalibrationMatch" + }, + "type": "array", + "maxItems": 500, + "title": "Matches" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "geography", + "reached_count", + "matches" + ], + "title": "ReportCalibration", + "description": "Calibration results for one geography, with at most 500 variable matches." + }, + "ReportCalibrationItem": { + "properties": { + "release_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Release Id" + }, + "geography": { + "type": "string", + "title": "Geography" + }, + "reached_count": { + "type": "integer", + "title": "Reached Count" + }, + "matches": { + "items": { + "$ref": "#/components/schemas/ReportCalibrationMatchItem" + }, + "type": "array", + "title": "Matches" + } + }, + "type": "object", + "required": [ + "geography", + "reached_count", + "matches" + ], + "title": "ReportCalibrationItem", + "description": "Calibration results saved with a report, for one geography and data release." + }, + "ReportCalibrationMatch": { + "properties": { + "variable": { + "type": "string", + "maxLength": 255, + "title": "Variable" + }, + "depth": { + "type": "integer", + "title": "Depth" + }, + "ring": { + "type": "string", + "enum": [ + "primary", + "mechanism", + "downstream" + ], + "title": "Ring" + }, + "target_count": { + "type": "integer", + "title": "Target Count" + }, + "mean_abs_relative_error": { + "type": "number", + "title": "Mean Abs Relative Error" + }, + "worst": { + "$ref": "#/components/schemas/ReportCalibrationWorst" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "variable", + "depth", + "ring", + "target_count", + "mean_abs_relative_error", + "worst" + ], + "title": "ReportCalibrationMatch", + "description": "Calibration of one variable: how closely its targets are met.\n\n``ring`` is ``primary``, ``mechanism`` or ``downstream``.\n``mean_abs_relative_error`` is averaged over the variable's ``target_count``\ntargets, and ``worst`` is the target with the largest error." + }, + "ReportCalibrationMatchItem": { + "properties": { + "variable": { + "type": "string", + "title": "Variable" + }, + "depth": { + "type": "integer", + "title": "Depth" + }, + "ring": { + "type": "string", + "title": "Ring" + }, + "target_count": { + "type": "integer", + "title": "Target Count" + }, + "mean_abs_relative_error": { + "type": "number", + "title": "Mean Abs Relative Error" + }, + "worst": { + "$ref": "#/components/schemas/ReportCalibrationWorstItem" + } + }, + "type": "object", + "required": [ + "variable", + "depth", + "ring", + "target_count", + "mean_abs_relative_error", + "worst" + ], + "title": "ReportCalibrationMatchItem", + "description": "One variable's calibration match: its targets, mean error and worst target." + }, + "ReportCalibrationWorst": { + "properties": { + "name": { + "type": "string", + "maxLength": 1024, + "title": "Name" + }, + "source": { + "type": "string", + "maxLength": 1024, + "title": "Source" + }, + "geography": { + "type": "string", + "maxLength": 64, + "title": "Geography" + }, + "relative_error": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Relative Error" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name", + "source", + "geography" + ], + "title": "ReportCalibrationWorst", + "description": "The calibration target a match fits worst, with its source and geography." + }, + "ReportCalibrationWorstItem": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "source": { + "type": "string", + "title": "Source" + }, + "geography": { + "type": "string", + "title": "Geography" + }, + "relative_error": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Relative Error" + } + }, + "type": "object", + "required": [ + "name", + "source", + "geography" + ], + "title": "ReportCalibrationWorstItem", + "description": "The calibration target that one match fits worst, with its relative error." + }, + "ReportProvision": { + "properties": { + "path": { + "type": "string", + "title": "Path" + }, + "breadcrumb": { + "type": "string", + "title": "Breadcrumb" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + }, + "baseline_value": { + "$ref": "#/components/schemas/FiniteJsonValue" + }, + "value": { + "$ref": "#/components/schemas/FiniteJsonValue" + }, + "intervals": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ReportProvisionInterval" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Intervals" + }, + "growth": { + "anyOf": [ + { + "type": "string", + "maxLength": 1024 + }, + { + "type": "null" + } + ], + "title": "Growth" + }, + "growth_start": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Growth Start" + }, + "growth_base": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Growth Base" + }, + "growth_round_to": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Growth Round To" + }, + "growth_round": { + "anyOf": [ + { + "type": "string", + "enum": [ + "nearest", + "down", + "up" + ] + }, + { + "type": "null" + } + ], + "title": "Growth Round" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "path", + "breadcrumb", + "baseline_value", + "value" + ], + "title": "ReportProvision", + "description": "One provision of the report's reform: a parameter's baseline and reform values.\n\n``path`` is the parameter path and ``breadcrumb`` its readable label, and\n``intervals`` optionally gives a dated schedule. ``growth`` says how a value\nset from one year on moves in later years (``fixed``, ``current_law`` or an\nindex's parameter path); the other ``growth_*`` fields say when it moves and\nhow grown values round. They are absent for a hand-set schedule." + }, + "ReportProvisionInterval": { + "properties": { + "parameter": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parameter" + }, + "start_date": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "title": "Start Date" + }, + "end_date": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "title": "End Date" + }, + "value": { + "$ref": "#/components/schemas/FiniteJsonValue" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "start_date", + "end_date", + "value" + ], + "title": "ReportProvisionInterval", + "description": "One dated value in a provision's schedule, from ``start_date`` to ``end_date``.\n\nDates are ``YYYY-MM-DD`` strings and ``value`` is any finite JSON value.\n``parameter`` is optional, and is left out of the stored report when unset." + }, + "ReportProvisionIntervalItem": { + "properties": { + "parameter": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parameter" + }, + "start_date": { + "type": "string", + "title": "Start Date" + }, + "end_date": { + "type": "string", + "title": "End Date" + }, + "value": { + "$ref": "#/components/schemas/JsonValue" + } + }, + "type": "object", + "required": [ + "start_date", + "end_date", + "value" + ], + "title": "ReportProvisionIntervalItem", + "description": "A provision's value for the dates from ``start_date`` to ``end_date``." + }, + "ReportProvisionItem": { + "properties": { + "path": { + "type": "string", + "title": "Path" + }, + "breadcrumb": { + "type": "string", + "title": "Breadcrumb" + }, + "unit": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Unit" + }, + "baseline_value": { + "$ref": "#/components/schemas/JsonValue" + }, + "value": { + "$ref": "#/components/schemas/JsonValue" + }, + "intervals": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ReportProvisionIntervalItem" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Intervals" + }, + "growth": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Growth" + }, + "growth_start": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Growth Start" + }, + "growth_base": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Growth Base" + }, + "growth_round_to": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Growth Round To" + }, + "growth_round": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Growth Round" + } + }, + "type": "object", + "required": [ + "path", + "breadcrumb", + "baseline_value", + "value" + ], + "title": "ReportProvisionItem", + "description": "One parameter the report's reform changes, with its baseline and reform values.\n\n``intervals`` gives the reform's dated values when they vary over time. The\n``growth`` keys, present only when set, say how a value set from one year on\nmoves in later years and how grown values round." + }, + "ReportScorecard": { + "properties": { + "programs": { + "items": { + "type": "string", + "maxLength": 255 + }, + "type": "array", + "maxItems": 200, + "title": "Programs" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "programs" + ], + "title": "ReportScorecard", + "description": "The programs on the report's validation scorecard, at most 200 names." + }, + "ReportScorecardItem": { + "properties": { + "programs": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Programs" + } + }, + "type": "object", + "required": [ + "programs" + ], + "title": "ReportScorecardItem", + "description": "The programs on the scorecard saved with a report's validation." + }, + "ReportValidation": { + "properties": { + "matched_at": { + "type": "string", + "maxLength": 64, + "title": "Matched At" + }, + "map_model_version": { + "type": "string", + "maxLength": 255, + "title": "Map Model Version" + }, + "calibration": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportCalibration" + }, + { + "type": "null" + } + ] + }, + "scorecard": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportScorecard" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "matched_at", + "map_model_version" + ], + "title": "ReportValidation", + "description": "The validation snapshot saved with a report.\n\n``calibration`` and ``scorecard`` are optional, and the stored snapshot keeps\nonly the keys supplied." + }, + "ReportValidationItem": { + "properties": { + "matched_at": { + "type": "string", + "title": "Matched At" + }, + "map_model_version": { + "type": "string", + "title": "Map Model Version" + }, + "calibration": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportCalibrationItem" + }, + { + "type": "null" + } + ] + }, + "scorecard": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportScorecardItem" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "matched_at", + "map_model_version" + ], + "title": "ReportValidationItem", + "description": "The validation snapshot saved with a report.\n\nIt records when the report was matched and with which map model version,\nand optionally the calibration results and scorecard." + }, + "SPMSelection": { + "oneOf": [ + { + "not": { + "properties": { + "geography_id": { + "type": "string" + } + }, + "required": [ + "geography_id" + ] + }, + "properties": { + "geography_kind": { + "enum": [ + "county", + "national" + ] + } + } + }, + { + "properties": { + "geography_kind": { + "enum": [ + "metro" + ] + }, + "geography_id": { + "type": "string", + "minLength": 1, + "pattern": "\\S" + } + }, + "required": [ + "geography_kind", + "geography_id" + ] + } + ], + "properties": { + "forecast_content_sha256": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + { + "type": "null" + } + ], + "title": "Forecast Content Sha256" + }, + "scenario": { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "pattern": "^\\S+$" + }, + { + "type": "null" + } + ], + "title": "Scenario" + }, + "geography_kind": { + "type": "string", + "enum": [ + "county", + "national", + "metro" + ], + "title": "Geography Kind" + }, + "geography_id": { + "anyOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "type": "null" + } + ], + "title": "Geography Id" + }, + "county_vintage": { + "type": "string", + "pattern": "^[0-9]{4}$", + "title": "County Vintage" + }, + "as_of": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$", + "format": "date", + "title": "As Of", + "description": "ISO calendar date (YYYY-MM-DD); must satisfy the selected artifact's information date" + } + }, + "additionalProperties": false, + "type": "object", + "title": "SPMSelection", + "description": "Select a forecast scenario and an explicit household SPM geography." + }, + "SharedBaselineReformItem": { + "properties": { + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "name" + ], + "title": "SharedBaselineReformItem", + "description": "The saved reform a shared report was compared with, named but not linked." + }, + "SharedUserReportDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/SharedUserReportDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "SharedUserReportDetailResponse", + "description": "Success response holding one shared report." + }, + "SharedUserReportDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/SharedUserReportItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "SharedUserReportDetailResult", + "description": "Result holding one shared report in ``item``." + }, + "SharedUserReportItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "legacy_report_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Legacy Report Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "source_note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Note" + }, + "provisions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ReportProvisionItem" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Provisions" + }, + "validation": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportValidationItem" + }, + { + "type": "null" + } + ] + }, + "budget_window_years": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Budget Window Years" + }, + "baseline_reform": { + "anyOf": [ + { + "$ref": "#/components/schemas/SharedBaselineReformItem" + }, + { + "type": "null" + } + ] + }, + "last_run_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Last Run At" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "legacy_report_id", + "name", + "source_note", + "provisions", + "validation", + "budget_window_years", + "baseline_reform", + "last_run_at", + "created_at", + "updated_at" + ], + "title": "SharedUserReportItem", + "description": "A saved report as anyone holding its link reads it." + }, + "UKHouseholdDocument": { + "properties": { + "people": { + "items": { + "$ref": "#/components/schemas/HouseholdPersonRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "People" + }, + "household": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Household" + }, + "benunit": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Benunit" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "people", + "household", + "benunit" + ], + "title": "UKHouseholdDocument", + "description": "UK household content: people, households and benefit units." + }, + "USHouseholdDocument-Input": { + "properties": { + "people": { + "items": { + "$ref": "#/components/schemas/HouseholdPersonRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "People" + }, + "household": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Household" + }, + "family": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Family" + }, + "tax_unit": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Tax Unit" + }, + "spm_unit": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Spm Unit" + }, + "marital_unit": { + "items": { + "$ref": "#/components/schemas/HouseholdEntityRecord" + }, + "type": "array", + "maxItems": 1000, + "title": "Marital Unit" + }, + "spm": { + "$ref": "#/components/schemas/SPMSelection", + "title": "Spm" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "people", + "household", + "family", + "tax_unit", + "spm_unit", + "marital_unit" + ], + "title": "USHouseholdDocument", + "description": "US household content: people and the US group entities they belong to.\n\n``spm`` optionally selects an SPM forecast scenario and geography; omit it\nrather than sending null." + }, + "USHouseholdDocument-Output": { + "additionalProperties": true, + "type": "object" + }, + "UserHouseholdCreateRequest": { + "properties": { + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "household_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Household Id" + }, + "legacy_household_id": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact v1 database identifier (a JSON integer)" + }, + { + "type": "null" + } + ], + "title": "Legacy Household Id" + }, + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "country_id" + ], + "title": "UserHouseholdCreateRequest", + "description": "Association fields; the owner is taken from the request credential." + }, + "UserHouseholdDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserHouseholdDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserHouseholdDetailResponse", + "description": "Success response holding one user-household association." + }, + "UserHouseholdDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/UserHouseholdItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "UserHouseholdDetailResult", + "description": "Result holding one user-household association in ``item``." + }, + "UserHouseholdItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" + }, + "household_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Household Id" + }, + "legacy_household_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Legacy Household Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "user_id", + "household_id", + "legacy_household_id", + "name", + "description", + "created_at", + "updated_at" + ], + "title": "UserHouseholdItem", + "description": "A household saved by a v2 user, with the user's name and description for it.\n\nUnlike households, associations can be renamed, pointed at another\nhousehold of the same country, or deleted." + }, + "UserHouseholdPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserHouseholdPageResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserHouseholdPageResponse", + "description": "Success response holding one page of user-household associations." + }, + "UserHouseholdPageResult": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/UserHouseholdItem" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "items", + "offset", + "limit", + "has_more" + ], + "title": "UserHouseholdPageResult", + "description": "One page of user-household associations, oldest first.\n\n``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask\nfor the next page with ``offset + limit``." + }, + "UserHouseholdPatchRequest": { + "properties": { + "household_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Household Id" + }, + "legacy_household_id": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact v1 database identifier (a JSON integer)" + }, + { + "type": "null" + } + ], + "title": "Legacy Household Id" + }, + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "additionalProperties": false, + "type": "object", + "title": "UserHouseholdPatchRequest", + "description": "Explicitly supplied mutable link or presentation fields." + }, + "UserPolicyCreateRequest": { + "properties": { + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Policy Id" + }, + "legacy_policy_id": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact v1 database identifier (a JSON integer)" + }, + { + "type": "null" + } + ], + "title": "Legacy Policy Id" + }, + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "baseline": { + "anyOf": [ + { + "type": "string", + "enum": [ + "current-law", + "current-policy" + ] + }, + { + "type": "null" + } + ], + "title": "Baseline" + }, + "provenance": { + "anyOf": [ + { + "$ref": "#/components/schemas/PolicyProvenance" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "country_id" + ], + "title": "UserPolicyCreateRequest", + "description": "Association fields; the owner is taken from the request credential." + }, + "UserPolicyDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserPolicyDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserPolicyDetailResponse", + "description": "Success response holding one user-policy association." + }, + "UserPolicyDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/UserPolicyItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "UserPolicyDetailResult", + "description": "Result holding one user-policy association in ``item``." + }, + "UserPolicyItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" + }, + "policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Policy Id" + }, + "legacy_policy_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Legacy Policy Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "baseline": { + "anyOf": [ + { + "type": "string", + "enum": [ + "current-law", + "current-policy" + ] + }, + { + "type": "null" + } + ], + "title": "Baseline" + }, + "provenance": { + "anyOf": [ + { + "$ref": "#/components/schemas/PolicyProvenanceItem" + }, + { + "type": "null" + } + ] + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "user_id", + "policy_id", + "legacy_policy_id", + "name", + "description", + "baseline", + "provenance", + "created_at", + "updated_at" + ], + "title": "UserPolicyItem", + "description": "A policy saved by a v2 user, with the user's name and description for it.\n\nUnlike policies, associations can be renamed or deleted." + }, + "UserPolicyPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserPolicyPageResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserPolicyPageResponse", + "description": "Success response holding one page of user-policy associations." + }, + "UserPolicyPageResult": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/UserPolicyItem" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "items", + "offset", + "limit", + "has_more" + ], + "title": "UserPolicyPageResult", + "description": "One page of user-policy associations, oldest first.\n\n``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask\nfor the next page with ``offset + limit``." + }, + "UserPolicyPatchRequest": { + "properties": { + "policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Policy Id" + }, + "legacy_policy_id": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact v1 database identifier (a JSON integer)" + }, + { + "type": "null" + } + ], + "title": "Legacy Policy Id" + }, + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "baseline": { + "anyOf": [ + { + "type": "string", + "enum": [ + "current-law", + "current-policy" + ] + }, + { + "type": "null" + } + ], + "title": "Baseline" + }, + "provenance": { + "anyOf": [ + { + "$ref": "#/components/schemas/PolicyProvenance" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "type": "object", + "title": "UserPolicyPatchRequest", + "description": "Explicitly supplied mutable link or presentation fields." + }, + "UserReportCreateRequest": { + "properties": { + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "source_note": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Source Note" + }, + "provisions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ReportProvision" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Provisions" + }, + "validation": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportValidation" + }, + { + "type": "null" + } + ] + }, + "budget_window_years": { + "anyOf": [ + { + "type": "integer", + "maximum": 75, + "minimum": 2 + }, + { + "type": "null" + } + ], + "title": "Budget Window Years" + }, + "last_run_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Last Run At" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "legacy_report_id": { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "title": "Legacy Report Id", + "description": "Exact v1 database identifier (a JSON integer)" + }, + "user_policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "User Policy Id" + }, + "baseline_user_policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Baseline User Policy Id" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "country_id", + "legacy_report_id" + ], + "title": "UserReportCreateRequest", + "description": "Association fields; the owner is taken from the request credential." + }, + "UserReportDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserReportDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserReportDetailResponse", + "description": "Success response holding one user-report association." + }, + "UserReportDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/UserReportItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "UserReportDetailResult", + "description": "Result holding one user-report association in ``item``." + }, + "UserReportItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" + }, + "report_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Report Id" + }, + "legacy_report_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Legacy Report Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "user_policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "User Policy Id" + }, + "baseline_user_policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Baseline User Policy Id" + }, + "source_note": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Note" + }, + "provisions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ReportProvisionItem" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Provisions" + }, + "validation": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportValidationItem" + }, + { + "type": "null" + } + ] + }, + "budget_window_years": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Budget Window Years" + }, + "last_run_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Last Run At" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "user_id", + "report_id", + "legacy_report_id", + "name", + "description", + "user_policy_id", + "baseline_user_policy_id", + "source_note", + "provisions", + "validation", + "budget_window_years", + "last_run_at", + "created_at", + "updated_at" + ], + "title": "UserReportItem", + "description": "A report saved by a user, with the user's name, notes and saved presentation.\n\n``legacy_report_id`` is the v1 report output it shows. ``user_policy_id`` is\nthe user's saved reform the report was run from, and\n``baseline_user_policy_id`` the saved reform it was compared with instead of\ncurrent law; each is null when not set or once that reform is deleted.\n``budget_window_years`` is null for a report without a budget window." + }, + "UserReportPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserReportPageResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserReportPageResponse", + "description": "Success response holding one page of user-report associations." + }, + "UserReportPageResult": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/UserReportItem" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "items", + "offset", + "limit", + "has_more" + ], + "title": "UserReportPageResult", + "description": "One page of user-report associations, oldest first.\n\n``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask\nfor the next page with ``offset + limit``." + }, + "UserReportPatchRequest": { + "properties": { + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "source_note": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Source Note" + }, + "provisions": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/ReportProvision" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Provisions" + }, + "validation": { + "anyOf": [ + { + "$ref": "#/components/schemas/ReportValidation" + }, + { + "type": "null" + } + ] + }, + "budget_window_years": { + "anyOf": [ + { + "type": "integer", + "maximum": 75, + "minimum": 2 + }, + { + "type": "null" + } + ], + "title": "Budget Window Years" + }, + "last_run_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Last Run At" + }, + "legacy_report_id": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact v1 database identifier (a JSON integer)" + }, + { + "type": "null" + } + ], + "title": "Legacy Report Id" + }, + "user_policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "User Policy Id" + }, + "baseline_user_policy_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid", + "description": "Exact resource UUID" + }, + { + "type": "null" + } + ], + "title": "Baseline User Policy Id" + } + }, + "additionalProperties": false, + "type": "object", + "title": "UserReportPatchRequest", + "description": "Explicitly supplied mutable link or presentation fields." + }, + "UserSimulationCreateRequest": { + "properties": { + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "legacy_simulation_id": { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "title": "Legacy Simulation Id", + "description": "Exact v1 database identifier (a JSON integer)" + }, + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "country_id", + "legacy_simulation_id" + ], + "title": "UserSimulationCreateRequest", + "description": "Association fields; the owner is taken from the request credential." + }, + "UserSimulationDetailResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserSimulationDetailResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserSimulationDetailResponse", + "description": "Success response holding one user-simulation association." + }, + "UserSimulationDetailResult": { + "properties": { + "item": { + "$ref": "#/components/schemas/UserSimulationItem" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "item" + ], + "title": "UserSimulationDetailResult", + "description": "Result holding one user-simulation association in ``item``." + }, + "UserSimulationItem": { + "properties": { + "id": { + "type": "string", + "format": "uuid", + "title": "Id" + }, + "country_id": { + "type": "string", + "enum": [ + "us", + "uk" + ], + "title": "Country Id", + "description": "Supported PolicyEngine country ID" + }, + "user_id": { + "type": "string", + "format": "uuid", + "title": "User Id" + }, + "simulation_id": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Simulation Id" + }, + "legacy_simulation_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Legacy Simulation Id" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "country_id", + "user_id", + "simulation_id", + "legacy_simulation_id", + "name", + "description", + "created_at", + "updated_at" + ], + "title": "UserSimulationItem", + "description": "A simulation saved by a user, with the user's name and description for it.\n\n``legacy_simulation_id`` is the v1 simulation it saves. Associations can be\nrenamed, pointed at another v1 simulation of the same country, or deleted." + }, + "UserSimulationPageResponse": { + "properties": { + "status": { + "type": "string", + "const": "ok", + "title": "Status", + "default": "ok" + }, + "message": { + "type": "null", + "title": "Message" + }, + "result": { + "$ref": "#/components/schemas/UserSimulationPageResult" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "result" + ], + "title": "UserSimulationPageResponse", + "description": "Success response holding one page of user-simulation associations." + }, + "UserSimulationPageResult": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/UserSimulationItem" + }, + "type": "array", + "title": "Items" + }, + "offset": { + "type": "integer", + "title": "Offset" + }, + "limit": { + "type": "integer", + "title": "Limit" + }, + "has_more": { + "type": "boolean", + "title": "Has More" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "items", + "offset", + "limit", + "has_more" + ], + "title": "UserSimulationPageResult", + "description": "One page of user-simulation associations, oldest first.\n\n``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask\nfor the next page with ``offset + limit``." + }, + "UserSimulationPatchRequest": { + "properties": { + "legacy_simulation_id": { + "anyOf": [ + { + "type": "integer", + "maximum": 9223372036854776000, + "minimum": 1, + "description": "Exact v1 database identifier (a JSON integer)" + }, + { + "type": "null" + } + ], + "title": "Legacy Simulation Id" + }, + "name": { + "anyOf": [ + { + "type": "string", + "maxLength": 255 + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 10000 + }, + { + "type": "null" + } + ], + "title": "Description" + } + }, + "additionalProperties": false, + "type": "object", + "title": "UserSimulationPatchRequest", + "description": "Explicitly supplied mutable link or presentation fields." + }, + "V2ErrorResponse": { + "properties": { + "status": { + "type": "string", + "const": "error", + "title": "Status", + "default": "error" + }, + "message": { + "type": "string", + "minLength": 1, + "title": "Message" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "message" + ], + "title": "V2ErrorResponse", + "description": "Strict error response shared by every API v2 resource." + } + }, + "securitySchemes": { + "UserAccessToken": { + "type": "http", + "description": "Auth0 user access token issued for the Public API audience.", + "scheme": "bearer" + }, + "UnauthenticatedUser": { + "type": "apiKey", + "description": "Canonical UUID that a signed-out browser generates once and keeps. It is a bearer credential for that browser's unauthenticated user; ignored when a bearer access token is present.", + "in": "header", + "name": "PolicyEngine-Unauthenticated-User" + } + } + } +} diff --git a/app/src/api/publicApi/parameterChildren.ts b/app/src/api/publicApi/parameterChildren.ts new file mode 100644 index 000000000..7e8bbdc4b --- /dev/null +++ b/app/src/api/publicApi/parameterChildren.ts @@ -0,0 +1,50 @@ +import { + createPublicApiClient, + publicApiCountryId, + requireResult, + type PublicApiClient, +} from '@/api/publicApi/client'; +import type { components } from '@/api/publicApi/schema'; + +/** + * One level of v3's parameter tree, from `GET /v2/parameters/children`: + * a node's direct children that a reform can change, with their labels, + * how many changeable parameters each holds, and, for a parameter, its + * summary. A bracket schedule is one node whose children are its brackets. + */ +export type ParameterChild = components['schemas']['MetadataParameterChild']; + +/** The largest page the route serves. */ +const CHILDREN_PAGE_SIZE = 500; +/** A generous bound so a misbehaving server cannot loop forever. */ +const MAXIMUM_CHILDREN_PAGES = 50; + +export async function fetchParameterChildren( + countryId: string, + policyengineVersion: string, + parentPath: string, + client: PublicApiClient = createPublicApiClient() +): Promise { + const children: ParameterChild[] = []; + for (let page = 0; page < MAXIMUM_CHILDREN_PAGES; page += 1) { + const result = await requireResult( + client.GET('/v2/parameters/children', { + params: { + query: { + country_id: publicApiCountryId(countryId), + policyengine_version: policyengineVersion, + parent_path: parentPath, + reform_changeable: true, + offset: children.length, + limit: CHILDREN_PAGE_SIZE, + }, + }, + }) + ); + children.push(...result.items); + if (!result.has_more || result.items.length === 0) { + break; + } + } + return children; +} diff --git a/app/src/api/publicApi/schema.d.ts b/app/src/api/publicApi/schema.d.ts new file mode 100644 index 000000000..2cda7dc34 --- /dev/null +++ b/app/src/api/publicApi/schema.d.ts @@ -0,0 +1,8498 @@ +/** + * Generated from src/api/publicApi/openapi.json by scripts/public-api-client.ts. + * Do not edit: run `bun run public-api:generate` (or `public-api:refresh`). + */ + +export interface paths { + '/v2/me': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Read the current user + * @description For a bearer access token, returns the PolicyEngine user, creating it on the first call with primary_country set from country_id. For the PolicyEngine-Unauthenticated-User header, returns the browser's unauthenticated user, or 404 before its first saved record; reading never creates one. + */ + get: operations['get_me_v2_me_get']; + put?: never; + post?: never; + /** + * Delete the current user and all of their saved records + * @description For a signed-in user, obtains Auth0 Management credentials first when they are configured, then deletes the user's legacy user mappings, the user (identities and associations cascade), and the Auth0 user. For an unauthenticated user, deletes the user and its associations and never calls Auth0; 204 even when it does not exist. + */ + delete: operations['delete_me_v2_me_delete']; + options?: never; + head?: never; + /** + * Update the signed-in user's names or primary country + * @description Unauthenticated users cannot edit account details (403). + */ + patch: operations['patch_me_v2_me_patch']; + trace?: never; + }; + '/v2/me/export': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Export the current user and every saved record + * @description Covers every country, not only country_id. user is null, and every list empty, for an unauthenticated user with no saved records. + */ + get: operations['export_me_v2_me_export_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/me/upgrade': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Move this browser's unauthenticated user into the signed-in account + * @description Requires both a bearer access token and the PolicyEngine-Unauthenticated-User header. When the signed-in identity has no account yet, the unauthenticated user becomes it in place, keeping its ID and records (converted). When the identity already has an account, every record moves to that account with its ID unchanged and the unauthenticated user is deleted (merged). When the header names no unauthenticated user, including a signed-in account's ID, nothing moves (none). The response is the signed-in account. + */ + post: operations['upgrade_me_v2_me_upgrade_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/user-reports': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List the current user's saved reports */ + get: operations['get_user_reports_v2_user_reports_get']; + put?: never; + /** + * Save a v1 report for the current user + * @description Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Stage 12A accepts legacy_report_id only (the requested v1 report output ID), checked in the v1 database. An optional user_policy_id must name the same user's saved policy in the same country. + */ + post: operations['create_user_report_v2_user_reports_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/user-reports/{association_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Read one of the current user's saved reports */ + get: operations['get_user_report_v2_user_reports__association_id__get']; + put?: never; + post?: never; + /** Delete one of the current user's saved reports */ + delete: operations['delete_user_report_route_v2_user_reports__association_id__delete']; + options?: never; + head?: never; + /** Update or repoint one of the current user's saved reports */ + patch: operations['patch_user_report_route_v2_user_reports__association_id__patch']; + trace?: never; + }; + '/v2/shared-reports/{association_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Read a saved report by its shareable link + * @description Needs no credential: a saved report's ID is its link, and anyone holding it can read the report. The owner and the owner's saved-reform IDs are left out; the reform the report was compared with is named instead. + */ + get: operations['get_shared_report_v2_shared_reports__association_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/user-simulations': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List the current user's saved simulations */ + get: operations['get_user_simulations_v2_user_simulations_get']; + put?: never; + /** + * Save a v1 simulation for the current user + * @description Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Stage 12A accepts legacy_simulation_id only, checked in the v1 database. + */ + post: operations['create_user_simulation_v2_user_simulations_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/user-simulations/{association_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Read one of the current user's saved simulations */ + get: operations['get_user_simulation_v2_user_simulations__association_id__get']; + put?: never; + post?: never; + /** Delete one of the current user's saved simulations */ + delete: operations['delete_user_simulation_route_v2_user_simulations__association_id__delete']; + options?: never; + head?: never; + /** Update or repoint one of the current user's saved simulations */ + patch: operations['patch_user_simulation_route_v2_user_simulations__association_id__patch']; + trace?: never; + }; + '/v2/user-households': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List the current user's saved households */ + get: operations['get_user_households_v2_user_households_get']; + put?: never; + /** + * Save a household for the current user + * @description Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Accepts legacy_household_id (a v1 household, checked in the v1 database) or household_id (a v2 household). + */ + post: operations['create_user_household_v2_user_households_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/user-households/{association_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Read one of the current user's saved households */ + get: operations['get_user_household_v2_user_households__association_id__get']; + put?: never; + post?: never; + /** Delete one of the current user's saved households */ + delete: operations['delete_user_household_route_v2_user_households__association_id__delete']; + options?: never; + head?: never; + /** + * Update or repoint one of the current user's saved households + * @description The row keeps its id, owner and creation time. Changing legacy_household_id repoints the row and re-resolves household_id from the Stage 11 copy mapping, or clears it. Changing household_id alone points the row at that v2 household, which must exist in the same country, and clears legacy_household_id. Supplied together, both are stored, and household_id must be the copy of legacy_household_id when one exists. + */ + patch: operations['patch_user_household_route_v2_user_households__association_id__patch']; + trace?: never; + }; + '/v2/user-policies': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List the current user's saved policies */ + get: operations['get_user_policies_v2_user_policies_get']; + put?: never; + /** + * Save a policy for the current user + * @description Requires a bearer access token or the PolicyEngine-Unauthenticated-User header; the owner is taken from that credential. Accepts legacy_policy_id (a v1 policy, checked in the v1 database) or policy_id (a v2 policy). When only legacy_policy_id is supplied, policy_id is filled from the Stage 10 mirror mapping when one exists. + */ + post: operations['create_user_policy_v2_user_policies_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/user-policies/{association_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Read one of the current user's saved policies */ + get: operations['get_user_policy_v2_user_policies__association_id__get']; + put?: never; + post?: never; + /** Delete one of the current user's saved policies */ + delete: operations['delete_user_policy_route_v2_user_policies__association_id__delete']; + options?: never; + head?: never; + /** + * Update or repoint one of the current user's saved policies + * @description The row keeps its id, owner and creation time. Changing legacy_policy_id repoints the row and re-resolves policy_id from the Stage 10 mirror mapping, or clears it. Changing policy_id alone points the row at that v2 policy, which must exist in the same country, and clears legacy_policy_id. Supplied together, both are stored, and policy_id must be the mirror copy of legacy_policy_id when one exists. Rows maintained by the v1 saved-policy mirror return 409. + */ + patch: operations['patch_user_policy_route_v2_user_policies__association_id__patch']; + trace?: never; + }; + '/v2/households': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List immutable households */ + get: operations['get_households_v2_households_get']; + put?: never; + /** Create or find an immutable household */ + post: operations['create_household_v2_households_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/households/{household_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Read one immutable household */ + get: operations['get_household_v2_households__household_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/policies': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List immutable policies */ + get: operations['get_policies_v2_policies_get']; + put?: never; + /** Create or find an immutable policy */ + post: operations['create_policy_v2_policies_post']; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/policies/{policy_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Read one immutable policy */ + get: operations['get_policy_v2_policies__policy_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/tax-benefit-models': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List models for one PolicyEngine.py catalog */ + get: operations['list_models_v2_tax_benefit_models_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/tax-benefit-models/by-country/{country_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a country model and selected PolicyEngine.py version */ + get: operations['get_model_by_country_v2_tax_benefit_models_by_country__country_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/tax-benefit-models/{model_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one model from a selected PolicyEngine.py catalog */ + get: operations['get_model_v2_tax_benefit_models__model_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/tax-benefit-model-versions': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List selected PolicyEngine.py model versions */ + get: operations['list_model_versions_v2_tax_benefit_model_versions_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/tax-benefit-model-versions/{version_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one selected PolicyEngine.py model version */ + get: operations['get_model_version_v2_tax_benefit_model_versions__version_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/variables': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List variables from a selected PolicyEngine.py catalog */ + get: operations['list_variables_v2_variables_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/variables/{variable_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one variable from a selected PolicyEngine.py catalog */ + get: operations['get_variable_v2_variables__variable_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/parameters': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List parameters from a selected PolicyEngine.py catalog */ + get: operations['list_parameters_v2_parameters_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/parameters/children': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List one level of a catalog version's parameter tree + * @description Lists the direct children of `parent_path` (the top level when empty). With `reform_changeable=true`, only the children a reform can change, sorted by label, with child counts that count only those. Invalid query parameters return 422, including a malformed policyengine_version, an unsupported country, and an unknown or repeated parameter. A catalog version without a tree (publication writes trees only for the versions it publishes and for the installed version with catalog format 2) returns 503 with a message naming it. + */ + get: operations['list_parameter_children_v2_parameters_children_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/parameters/search-file': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get a catalog version's parameter search file */ + get: operations['get_parameter_search_file_v2_parameters_search_file_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/parameters/{parameter_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one parameter from a selected PolicyEngine.py catalog */ + get: operations['get_parameter_v2_parameters__parameter_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/parameter-values': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List canonical values from a selected PolicyEngine.py catalog */ + get: operations['list_parameter_values_v2_parameter_values_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/parameter-values/{value_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one canonical value from a selected PolicyEngine.py catalog */ + get: operations['get_parameter_value_v2_parameter_values__value_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/datasets': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List logical inputs from a selected PolicyEngine.py catalog */ + get: operations['list_datasets_v2_datasets_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/datasets/{dataset_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one logical input from a selected PolicyEngine.py catalog */ + get: operations['get_dataset_v2_datasets__dataset_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/regions': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List regions from a selected PolicyEngine.py catalog */ + get: operations['list_regions_v2_regions_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/regions/by-code/{region_code}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one region by code from a selected catalog */ + get: operations['get_region_by_code_v2_regions_by_code__region_code__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/regions/{region_id}': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get one region from a selected PolicyEngine.py catalog */ + get: operations['get_region_v2_regions__region_id__get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/economy-options': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get compact economy-selection options from a selected catalog */ + get: operations['get_economy_options_v2_economy_options_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/model-catalog': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get the small parts of one PolicyEngine.py catalog version + * @description Returns the selected catalog version's model, model version, entities, basic inputs, regions, logical input datasets and economy choices, which are empty lists for a catalog without regions. The result names the selected PolicyEngine.py version, so a client can request the version's other parts by that exact version; a response for a named version may be reused for a day. + */ + get: operations['get_model_catalog_v2_model_catalog_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/model-catalog/variables': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get every variable of one PolicyEngine.py catalog version + * @description Returns all of the selected catalog version's variables in name order, in one response. Where a variable's adds or subtracts list comes from a parameter, part_parameter_values carries that parameter's current-law values by date, so a client can pick the list for any year. + */ + get: operations['get_catalog_variables_v2_model_catalog_variables_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + '/v2/model-catalog/parameters': { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get several parameters of one catalog version by name + * @description Returns the named parameters (repeat name for each, at most 100) in name order, each with its economy and household flags and its current-law values by date, plus the stored labels of their ancestor nodes for breadcrumbs and the requested names the version has no parameter for. A client showing or editing a reform loads all of its parameters in this one request. + */ + get: operations['get_catalog_parameters_v2_model_catalog_parameters_get']; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** + * AccountPatchRequest + * @description Explicitly supplied account presentation fields. + */ + AccountPatchRequest: { + /** First Name */ + first_name?: string | null; + /** Last Name */ + last_name?: string | null; + /** Primary Country */ + primary_country?: ('us' | 'uk') | null; + }; + FiniteJsonValue: + | components['schemas']['FiniteJsonValue'][] + | { + [key: string]: components['schemas']['FiniteJsonValue']; + } + | string + | boolean + | number + | null; + /** + * HealthResponse + * @description Health-check result showing that the API is up and serving requests. + */ + HealthResponse: { + /** + * Status + * @constant + */ + status: 'healthy'; + }; + /** + * HouseholdCreateRequest + * @description Native immutable household content with no presentation or model fields. + */ + HouseholdCreateRequest: { + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * Default Year + * @description Default year for values without explicit period keys + */ + default_year: number; + /** Household Data */ + household_data: + | components['schemas']['USHouseholdDocument-Input'] + | components['schemas']['UKHouseholdDocument']; + }; + /** + * HouseholdDetailResponse + * @description Success response holding one household. + */ + HouseholdDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['HouseholdDetailResult']; + }; + /** + * HouseholdDetailResult + * @description Result holding one household in ``item``. + */ + HouseholdDetailResult: { + item: components['schemas']['HouseholdItem']; + }; + /** + * HouseholdEntityRecord + * @description One group entity in a household, such as a tax unit or a benefit unit. + * + * ``id`` is unique within its collection and is what people's ``memberships`` + * refer to. ``values`` maps variable names to values, and ``source_name`` + * optionally keeps the entity's original name, such as its key in a v1 household. + */ + HouseholdEntityRecord: { + /** Id */ + id: string; + /** Source Name */ + source_name?: string | null; + /** Values */ + values: { + [key: string]: components['schemas']['JsonValue']; + }; + }; + /** + * HouseholdItem + * @description A stored household. + * + * Households are immutable and deduplicated: creating one with identical + * content returns the existing household and its ``id``. + */ + HouseholdItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** Default Year */ + default_year: number | null; + /** Household Data */ + household_data: + | components['schemas']['USHouseholdDocument-Output'] + | components['schemas']['UKHouseholdDocument']; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * HouseholdPageResponse + * @description Success response holding one page of households. + */ + HouseholdPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['HouseholdPageResult']; + }; + /** + * HouseholdPageResult + * @description One page of households, oldest first. + * + * ``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask + * for the next page with ``offset + limit``. + */ + HouseholdPageResult: { + /** Items */ + items: components['schemas']['HouseholdItem'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * HouseholdPersonRecord + * @description One person in a household. + * + * ``memberships`` maps each of the country's entity collections to the ``id`` + * of the group the person belongs to, and must name every collection. + */ + HouseholdPersonRecord: { + /** Id */ + id: string; + /** Source Name */ + source_name?: string | null; + /** Values */ + values: { + [key: string]: components['schemas']['JsonValue']; + }; + /** Memberships */ + memberships: { + [key: string]: string; + }; + }; + JsonValue: unknown; + /** + * MeDetailResponse + * @description Success response holding the current user. + */ + MeDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MeDetailResult']; + }; + /** + * MeDetailResult + * @description Result holding the current user in ``item``. + */ + MeDetailResult: { + item: components['schemas']['MeItem']; + }; + /** + * MeExportResponse + * @description Success response holding the current user's export. + */ + MeExportResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MeExportResult']; + }; + /** + * MeExportResult + * @description The current user and every policy, household, simulation and report it saved. + * + * The export covers every country, not only the requested one. + */ + MeExportResult: { + user: components['schemas']['MeItem'] | null; + /** Policies */ + policies: components['schemas']['UserPolicyItem'][]; + /** Households */ + households: components['schemas']['UserHouseholdItem'][]; + /** Simulations */ + simulations: components['schemas']['UserSimulationItem'][]; + /** Reports */ + reports: components['schemas']['UserReportItem'][]; + }; + /** + * MeItem + * @description The current user: a signed-in account or a browser's unauthenticated user. + * + * ``user_type`` says which. An unauthenticated user has no primary country, and + * only a signed-in user can set its names or primary country. + */ + MeItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * User Type + * @enum {string} + */ + user_type: 'unauthenticated' | 'standard'; + /** Primary Country */ + primary_country: ('us' | 'uk') | null; + /** First Name */ + first_name: string | null; + /** Last Name */ + last_name: string | null; + /** Email */ + email: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + }; + /** + * MeUpgradeResponse + * @description Success response holding the signed-in account after an upgrade. + */ + MeUpgradeResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MeUpgradeResult']; + }; + /** + * MeUpgradeResult + * @description The signed-in account after a browser's unauthenticated user moved into it. + * + * ``outcome`` is ``converted`` when the unauthenticated user became the account + * in place, ``merged`` when its records moved to an existing account, and + * ``none`` when there was no unauthenticated user to move. + */ + MeUpgradeResult: { + item: components['schemas']['MeItem']; + /** + * Outcome + * @enum {string} + */ + outcome: 'converted' | 'merged' | 'none'; + }; + /** + * MetadataCanonicalParameterValue + * @description One current-law value of a parameter. + * + * The value applies from start_date until end_date, or indefinitely when + * end_date is null. + */ + MetadataCanonicalParameterValue: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Parameter Id + * Format: uuid + */ + parameter_id: string; + value: components['schemas']['JsonValue']; + /** + * Start Date + * Format: date-time + */ + start_date: string; + /** End Date */ + end_date: string | null; + }; + /** + * MetadataCatalogParameter + * @description A parameter with its current-law values in start-date order. + */ + MetadataCatalogParameter: { + /** + * Id + * Format: uuid + */ + id: string; + /** Name */ + name: string; + /** Label */ + label: string | null; + /** Description */ + description: string | null; + /** Data Type */ + data_type: string | null; + /** Unit */ + unit: string | null; + /** Economy */ + economy: boolean | null; + /** Household */ + household: boolean | null; + /** Values */ + values: components['schemas']['MetadataDatedValue'][]; + }; + /** + * MetadataCatalogParametersResponse + * @description Success response holding parameters fetched by name, with their values. + */ + MetadataCatalogParametersResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataCatalogParametersResult']; + }; + /** + * MetadataCatalogParametersResult + * @description Requested parameters of one catalog version, in name order. + * + * node_labels maps each ancestor of a requested parameter that the version + * stores as a parameter node to the node's stored label (null when it has + * none), for breadcrumbs; a bracket index such as ``rates[0]`` counts as a + * child of its schedule ``rates``. missing_names lists requested names the + * version has no parameter for. + */ + MetadataCatalogParametersResult: { + /** Policyengine Version */ + policyengine_version: string; + /** Catalog Format */ + catalog_format: number | null; + /** Parameters */ + parameters: components['schemas']['MetadataCatalogParameter'][]; + /** Node Labels */ + node_labels: { + [key: string]: string | null; + }; + /** Missing Names */ + missing_names: string[]; + }; + /** + * MetadataCatalogVariablesResponse + * @description Success response holding every variable of one catalog version. + */ + MetadataCatalogVariablesResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataCatalogVariablesResult']; + }; + /** + * MetadataCatalogVariablesResult + * @description Every variable of one catalog version, in name order. + * + * part_parameter_values has an entry for each parameter path a variable's + * adds_parameter_path or subtracts_parameter_path names: that parameter's + * current-law values in start-date order, or an empty list when the + * catalog has no such parameter. + */ + MetadataCatalogVariablesResult: { + /** Policyengine Version */ + policyengine_version: string; + /** Catalog Format */ + catalog_format: number | null; + /** Variables */ + variables: components['schemas']['MetadataVariable'][]; + /** Part Parameter Values */ + part_parameter_values: { + [key: string]: components['schemas']['MetadataDatedValue'][]; + }; + }; + /** + * MetadataDataset + * @description A named input dataset available to the selected model version. + * + * These datasets are logical inputs, so storage_path is always null and + * is_output_dataset is always false. + */ + MetadataDataset: { + /** + * Id + * Format: uuid + */ + id: string; + /** Name */ + name: string; + /** Description */ + description: string | null; + /** Year */ + year: number; + /** Storage Path */ + storage_path?: null; + /** + * Is Output Dataset + * @default false + * @constant + */ + is_output_dataset: false; + }; + /** + * MetadataDatasetDetailResponse + * @description Success response holding one dataset. + */ + MetadataDatasetDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataDataset_']; + }; + /** + * MetadataDatasetOption + * @description The national dataset, offered as the default for an economy calculation. + */ + MetadataDatasetOption: { + /** Name */ + name: string; + /** Label */ + label: string; + /** + * Default + * @default true + * @constant + */ + default: true; + }; + /** + * MetadataDatasetPageResponse + * @description Success response holding one page of datasets. + */ + MetadataDatasetPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataDataset_']; + }; + /** + * MetadataDatedValue + * @description A current-law parameter value from start_date until end_date. + * + * A null end_date means the value applies until a later value starts, or + * indefinitely. + */ + MetadataDatedValue: { + /** + * Start Date + * Format: date-time + */ + start_date: string; + /** End Date */ + end_date: string | null; + value: components['schemas']['JsonValue']; + }; + /** MetadataDetailResult[MetadataCanonicalParameterValue] */ + MetadataDetailResult_MetadataCanonicalParameterValue_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataCanonicalParameterValue']; + }; + /** MetadataDetailResult[MetadataDataset] */ + MetadataDetailResult_MetadataDataset_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataDataset']; + }; + /** MetadataDetailResult[MetadataModelVersionDetail] */ + MetadataDetailResult_MetadataModelVersionDetail_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataModelVersionDetail']; + }; + /** MetadataDetailResult[MetadataModel] */ + MetadataDetailResult_MetadataModel_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataModel']; + }; + /** MetadataDetailResult[MetadataParameterSummary] */ + MetadataDetailResult_MetadataParameterSummary_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataParameterSummary']; + }; + /** MetadataDetailResult[MetadataRegion] */ + MetadataDetailResult_MetadataRegion_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataRegion']; + }; + /** MetadataDetailResult[MetadataVariable] */ + MetadataDetailResult_MetadataVariable_: { + /** Policyengine Version */ + policyengine_version: string; + item: components['schemas']['MetadataVariable']; + }; + /** + * MetadataEconomyOptions + * @description The region, year and dataset choices for an economy calculation. + * + * Every list is empty for a catalog without regions, such as a + * household-only country's, which offers no economy calculation. + */ + MetadataEconomyOptions: { + /** Region */ + region: components['schemas']['MetadataRegionOption'][]; + /** Time Period */ + time_period: components['schemas']['MetadataTimePeriodOption'][]; + /** Datasets */ + datasets: components['schemas']['MetadataDatasetOption'][]; + }; + /** + * MetadataEconomyOptionsResponse + * @description Success response holding the options for configuring an economy comparison. + */ + MetadataEconomyOptionsResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataEconomyOptionsResult']; + }; + /** + * MetadataEconomyOptionsResult + * @description The choices for configuring an economy calculation. + * + * It lists the selectable regions, years and datasets, and the id of the + * current-law policy to use as a baseline. + */ + MetadataEconomyOptionsResult: { + /** Policyengine Version */ + policyengine_version: string; + /** Current Law Id */ + current_law_id: number; + /** Region */ + region: components['schemas']['MetadataRegionOption'][]; + /** Time Period */ + time_period: components['schemas']['MetadataTimePeriodOption'][]; + /** Datasets */ + datasets: components['schemas']['MetadataDatasetOption'][]; + }; + /** + * MetadataEntity + * @description A kind of member a household holds, such as a person or a tax unit. + * + * plural is the key the household's members of this kind are listed under, + * and is_person marks the entity every household input about a person uses. + */ + MetadataEntity: { + /** Key */ + key: string; + /** Plural */ + plural: string; + /** Is Person */ + is_person: boolean; + }; + /** + * MetadataEnumValue + * @description One value of an enum variable: its stored name and its display label. + * + * The label keeps the type the country package declares it with. + */ + MetadataEnumValue: { + /** Value */ + value: string; + /** Label */ + label: string | number | boolean | null; + }; + /** + * MetadataModel + * @description A country's tax-benefit model in the selected PolicyEngine.py catalog. + */ + MetadataModel: { + /** + * Id + * Format: uuid + */ + id: string; + /** Name */ + name: string; + /** Description */ + description: string | null; + }; + /** + * MetadataModelCatalogResponse + * @description Success response holding the small parts of one catalog version. + */ + MetadataModelCatalogResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataModelCatalogResult']; + }; + /** + * MetadataModelCatalogResult + * @description The small parts of one catalog version, read when a client starts. + * + * policyengine_version names the selected catalog version, so a client can + * request its other parts for that exact version. catalog_format is null, + * and entities and basic_inputs are empty, for a version published before + * catalog format 2. Variables, parameters and their values are served by + * their own routes. + */ + MetadataModelCatalogResult: { + /** Policyengine Version */ + policyengine_version: string; + /** Catalog Format */ + catalog_format: number | null; + model: components['schemas']['MetadataModel']; + model_version: components['schemas']['MetadataModelVersionDetail']; + /** Entities */ + entities: components['schemas']['MetadataEntity'][]; + /** Basic Inputs */ + basic_inputs: string[]; + /** Regions */ + regions: components['schemas']['MetadataRegion'][]; + /** Datasets */ + datasets: components['schemas']['MetadataDataset'][]; + economy_options: components['schemas']['MetadataEconomyOptions']; + }; + /** + * MetadataModelDetailResponse + * @description Success response holding one tax-benefit model. + */ + MetadataModelDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataModel_']; + }; + /** + * MetadataModelPageResponse + * @description Success response holding a page of the selected catalog's tax-benefit models. + * + * A catalog has one model per country, so only the first page has an item. + */ + MetadataModelPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataModel_']; + }; + /** + * MetadataModelSelectionResponse + * @description Success response holding a country's model and its selected model version. + */ + MetadataModelSelectionResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataModelSelectionResult']; + }; + /** + * MetadataModelSelectionResult + * @description A country's model together with the model version selected for the request. + */ + MetadataModelSelectionResult: { + /** Policyengine Version */ + policyengine_version: string; + model: components['schemas']['MetadataModel']; + model_version: components['schemas']['MetadataModelVersionDetail']; + }; + /** + * MetadataModelVersionDetail + * @description A model version, with its current-law policy id and the years offered. + */ + MetadataModelVersionDetail: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Model Id + * Format: uuid + */ + model_id: string; + /** Version */ + version: string; + /** Description */ + description: string | null; + /** Current Law Id */ + current_law_id: number; + /** Metadata Time Periods */ + metadata_time_periods: number[]; + }; + /** + * MetadataModelVersionDetailResponse + * @description Success response holding one model version. + */ + MetadataModelVersionDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataModelVersionDetail_']; + }; + /** + * MetadataModelVersionPageResponse + * @description Success response holding a page of the selected catalog's model versions. + * + * A catalog has one model version, so only the first page has an item. + */ + MetadataModelVersionPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataModelVersionDetail_']; + }; + /** MetadataPageResult[MetadataCanonicalParameterValue] */ + MetadataPageResult_MetadataCanonicalParameterValue_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataCanonicalParameterValue'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataDataset] */ + MetadataPageResult_MetadataDataset_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataDataset'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataModelVersionDetail] */ + MetadataPageResult_MetadataModelVersionDetail_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataModelVersionDetail'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataModel] */ + MetadataPageResult_MetadataModel_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataModel'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataParameterChild] */ + MetadataPageResult_MetadataParameterChild_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataParameterChild'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataParameterSummary] */ + MetadataPageResult_MetadataParameterSummary_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataParameterSummary'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataRegion] */ + MetadataPageResult_MetadataRegion_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataRegion'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** MetadataPageResult[MetadataVariable] */ + MetadataPageResult_MetadataVariable_: { + /** Policyengine Version */ + policyengine_version: string; + /** Items */ + items: components['schemas']['MetadataVariable'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * MetadataParameterChild + * @description A direct child in the parameter tree. + * + * A node has a child_count; a parameter leaf carries the parameter's summary. + */ + MetadataParameterChild: { + /** Path */ + path: string; + /** Label */ + label: string; + /** + * Type + * @enum {string} + */ + type: 'node' | 'parameter'; + /** Child Count */ + child_count?: number | null; + parameter?: components['schemas']['MetadataParameterSummary'] | null; + }; + /** + * MetadataParameterChildPageResponse + * @description Success response holding one page of a parameter path's direct children. + */ + MetadataParameterChildPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataParameterChild_']; + }; + /** + * MetadataParameterDetailResponse + * @description Success response holding one parameter. + */ + MetadataParameterDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataParameterSummary_']; + }; + /** + * MetadataParameterPageResponse + * @description Success response holding one page of parameters. + */ + MetadataParameterPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataParameterSummary_']; + }; + /** + * MetadataParameterSearchFileContent + * @description One catalog version's parameter search file, ordered by path. + * + * It holds what v3's parameter search reads, without the catalog's values. + * A reader applies v1's label fallback where a label is null. + */ + MetadataParameterSearchFileContent: { + /** Format */ + format: number; + /** Country Id */ + country_id: string; + /** Policyengine Version */ + policyengine_version: string; + /** Parameters */ + parameters: components['schemas']['MetadataParameterSearchFileParameter'][]; + /** Other Parameters */ + other_parameters: components['schemas']['MetadataParameterSearchFileLabel'][]; + /** Nodes */ + nodes: components['schemas']['MetadataParameterSearchFileNode'][]; + }; + /** + * MetadataParameterSearchFileEditabilityChange + * @description From ``start_date`` (an ISO date), whether a draft can edit the value. + */ + MetadataParameterSearchFileEditabilityChange: { + /** + * Start Date + * Format: date + */ + start_date: string; + /** Editable */ + editable: boolean; + }; + /** + * MetadataParameterSearchFileLabel + * @description Another parameter under ``gov``: its path and stored label, or null. + */ + MetadataParameterSearchFileLabel: { + /** Path */ + path: string; + /** Label */ + label: string | null; + }; + /** + * MetadataParameterSearchFileNode + * @description ``gov`` or a node under it that has a stored label. + */ + MetadataParameterSearchFileNode: { + /** Path */ + path: string; + /** Label */ + label: string; + }; + /** + * MetadataParameterSearchFileParameter + * @description A parameter a reform can change, with what v3's search indexes. + * + * ``label`` is the stored label, or null when none is stored. ``editable`` + * says whether a draft can edit the earliest current-law value (a number, a + * yes/no value or an infinite threshold); ``editable_changes``, present only + * when that changes over time and never null, lists the dates from which it + * changes. + */ + MetadataParameterSearchFileParameter: { + /** Path */ + path: string; + /** Label */ + label: string | null; + /** Unit */ + unit: string | null; + /** Description */ + description: string | null; + /** Editable */ + editable: boolean; + /** Editable Changes */ + editable_changes?: components['schemas']['MetadataParameterSearchFileEditabilityChange'][]; + }; + /** + * MetadataParameterSummary + * @description A policy parameter, described without its values. + * + * Its current-law values are listed by GET /v2/parameter-values. economy and + * household say whether a reform of an economy or of a household may change + * it; they are null in versions published before catalog format 2. + */ + MetadataParameterSummary: { + /** + * Id + * Format: uuid + */ + id: string; + /** Name */ + name: string; + /** Label */ + label: string | null; + /** Description */ + description: string | null; + /** Data Type */ + data_type: string | null; + /** Unit */ + unit: string | null; + /** Economy */ + economy: boolean | null; + /** Household */ + household: boolean | null; + }; + /** + * MetadataParameterValueDetailResponse + * @description Success response holding one parameter value. + */ + MetadataParameterValueDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataCanonicalParameterValue_']; + }; + /** + * MetadataParameterValuePageResponse + * @description Success response holding one page of parameter values. + */ + MetadataParameterValuePageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataCanonicalParameterValue_']; + }; + /** + * MetadataRegion + * @description A region the model can simulate, such as a country, state or constituency. + * + * When requires_filter is true, filter_strategy says how the region selects + * households of its default dataset. With "row_filter", it keeps the + * households whose filter_field column equals filter_value (or is in it, + * when filter_value is a JSON list). + */ + MetadataRegion: { + /** + * Id + * Format: uuid + */ + id: string; + /** Code */ + code: string; + /** Label */ + label: string; + region_type: components['schemas']['MetadataRegionType']; + /** Requires Filter */ + requires_filter: boolean; + /** Filter Field */ + filter_field: string | null; + /** Filter Value */ + filter_value: string | null; + /** Filter Strategy */ + filter_strategy: string | null; + /** Parent Code */ + parent_code: string | null; + /** State Code */ + state_code: string | null; + /** State Name */ + state_name: string | null; + /** + * Default Dataset Id + * Format: uuid + */ + default_dataset_id: string; + }; + /** + * MetadataRegionDetailResponse + * @description Success response holding one region. + */ + MetadataRegionDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataRegion_']; + }; + /** + * MetadataRegionOption + * @description A region that can be chosen for an economy calculation; name is its code. + */ + MetadataRegionOption: { + /** Name */ + name: string; + /** Label */ + label: string; + type: components['schemas']['MetadataRegionType']; + }; + /** + * MetadataRegionPageResponse + * @description Success response holding one page of regions. + */ + MetadataRegionPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataRegion_']; + }; + /** + * MetadataRegionType + * @description The geographic level of a region. + * @enum {string} + */ + MetadataRegionType: + | 'national' + | 'country' + | 'state' + | 'congressional_district' + | 'constituency' + | 'local_authority' + | 'city' + | 'place'; + /** + * MetadataTimePeriodOption + * @description A year that can be chosen for an economy calculation. + */ + MetadataTimePeriodOption: { + /** Name */ + name: number; + /** Label */ + label: string; + }; + /** + * MetadataVariable + * @description A variable the tax-benefit model computes or accepts as input. + * + * adds and subtracts, when present, list the variables summed into or + * subtracted from this one. When the model takes that list from a + * parameter, adds_parameter_path or subtracts_parameter_path names the + * parameter, whose dated values give the list for each year (adds and + * subtracts hold its value on 2025-01-01). unit is the country package's + * unit (for example ``currency-USD``) and definition_period the period a + * value covers (``year``, ``month`` or ``eternity``). The catalog format 2 + * fields (is_input_variable, hidden_input, module_name, the two parameter + * paths, enum_values, unit and definition_period) are null in versions + * published before that format. + */ + MetadataVariable: { + /** + * Id + * Format: uuid + */ + id: string; + /** Name */ + name: string; + /** Label */ + label: string | null; + /** Entity */ + entity: string; + /** Description */ + description: string | null; + /** Data Type */ + data_type: string | null; + /** Possible Values */ + possible_values: string[] | null; + default_value: components['schemas']['JsonValue']; + /** Adds */ + adds: string[] | null; + /** Subtracts */ + subtracts: string[] | null; + /** Is Input Variable */ + is_input_variable: boolean | null; + /** Hidden Input */ + hidden_input: boolean | null; + /** Module Name */ + module_name: string | null; + /** Adds Parameter Path */ + adds_parameter_path: string | null; + /** Subtracts Parameter Path */ + subtracts_parameter_path: string | null; + /** Enum Values */ + enum_values: components['schemas']['MetadataEnumValue'][] | null; + /** Unit */ + unit: string | null; + /** Definition Period */ + definition_period: string | null; + }; + /** + * MetadataVariableDetailResponse + * @description Success response holding one variable. + */ + MetadataVariableDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataDetailResult_MetadataVariable_']; + }; + /** + * MetadataVariablePageResponse + * @description Success response holding one page of variables. + */ + MetadataVariablePageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['MetadataPageResult_MetadataVariable_']; + }; + /** + * PolicyCreateRequest + * @description Native body containing immutable policy content only. + */ + PolicyCreateRequest: { + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * Tax Benefit Model Id + * Format: uuid + */ + tax_benefit_model_id: string; + /** Parameter Values */ + parameter_values: components['schemas']['PolicyParameterValueInput'][]; + }; + /** + * PolicyDetailResponse + * @description Success response holding one policy. + */ + PolicyDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['PolicyDetailResult']; + }; + /** + * PolicyDetailResult + * @description Result holding one policy in ``item``. + */ + PolicyDetailResult: { + item: components['schemas']['PolicyItem']; + }; + /** + * PolicyItem + * @description A stored policy: parameter values for one tax-benefit model. + * + * Policies are immutable and deduplicated: creating one with identical + * content returns the existing policy and its ``id``. + */ + PolicyItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** Country Id */ + country_id: string; + /** + * Tax Benefit Model Id + * Format: uuid + */ + tax_benefit_model_id: string; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + /** Parameter Values */ + parameter_values: components['schemas']['PolicyParameterValueItem'][]; + }; + /** + * PolicyPageResponse + * @description Success response holding one page of policies. + */ + PolicyPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['PolicyPageResult']; + }; + /** + * PolicyPageResult + * @description One page of policies, oldest first. + * + * ``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask + * for the next page with ``offset + limit``. + */ + PolicyPageResult: { + /** Items */ + items: components['schemas']['PolicyItem'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * PolicyParameterValueInput + * @description One normalized effective value for a catalog parameter UUID. + */ + PolicyParameterValueInput: { + /** + * Parameter Id + * Format: uuid + */ + parameter_id: string; + value: components['schemas']['JsonValue']; + /** + * Start Date + * Format: date-time + */ + start_date: string; + /** End Date */ + end_date?: string | null; + }; + /** + * PolicyParameterValueItem + * @description One value a policy sets for a parameter from ``start_date``. + * + * ``parameter_name`` is the parameter's path in the model, and a null + * ``end_date`` means the value has no end. + */ + PolicyParameterValueItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Parameter Id + * Format: uuid + */ + parameter_id: string; + /** Parameter Name */ + parameter_name: string; + value: components['schemas']['JsonValue']; + /** + * Start Date + * Format: date-time + */ + start_date: string; + /** End Date */ + end_date: string | null; + }; + /** + * PolicyProvenance + * @description Where a v3 reform came from. + */ + PolicyProvenance: { + /** + * Source + * @enum {string} + */ + source: 'manual' | 'chat' | 'bill' | 'tool'; + /** Ref */ + ref?: string | null; + /** Confidence */ + confidence?: ('exact' | 'approximated' | 'partial') | null; + }; + /** + * PolicyProvenanceItem + * @description Stored v3 reform provenance as responses publish it. + * + * It mirrors the contract without the input bounds and ignores unknown keys, + * so any stored row serializes. + */ + PolicyProvenanceItem: { + /** Source */ + source: string; + /** Ref */ + ref?: string | null; + /** Confidence */ + confidence?: string | null; + }; + /** + * ReportCalibration + * @description Calibration results for one geography, with at most 500 variable matches. + */ + ReportCalibration: { + /** Release Id */ + release_id?: string | null; + /** Geography */ + geography: string; + /** Reached Count */ + reached_count: number; + /** Matches */ + matches: components['schemas']['ReportCalibrationMatch'][]; + }; + /** + * ReportCalibrationItem + * @description Calibration results saved with a report, for one geography and data release. + */ + ReportCalibrationItem: { + /** Release Id */ + release_id?: string | null; + /** Geography */ + geography: string; + /** Reached Count */ + reached_count: number; + /** Matches */ + matches: components['schemas']['ReportCalibrationMatchItem'][]; + }; + /** + * ReportCalibrationMatch + * @description Calibration of one variable: how closely its targets are met. + * + * ``ring`` is ``primary``, ``mechanism`` or ``downstream``. + * ``mean_abs_relative_error`` is averaged over the variable's ``target_count`` + * targets, and ``worst`` is the target with the largest error. + */ + ReportCalibrationMatch: { + /** Variable */ + variable: string; + /** Depth */ + depth: number; + /** + * Ring + * @enum {string} + */ + ring: 'primary' | 'mechanism' | 'downstream'; + /** Target Count */ + target_count: number; + /** Mean Abs Relative Error */ + mean_abs_relative_error: number; + worst: components['schemas']['ReportCalibrationWorst']; + }; + /** + * ReportCalibrationMatchItem + * @description One variable's calibration match: its targets, mean error and worst target. + */ + ReportCalibrationMatchItem: { + /** Variable */ + variable: string; + /** Depth */ + depth: number; + /** Ring */ + ring: string; + /** Target Count */ + target_count: number; + /** Mean Abs Relative Error */ + mean_abs_relative_error: number; + worst: components['schemas']['ReportCalibrationWorstItem']; + }; + /** + * ReportCalibrationWorst + * @description The calibration target a match fits worst, with its source and geography. + */ + ReportCalibrationWorst: { + /** Name */ + name: string; + /** Source */ + source: string; + /** Geography */ + geography: string; + /** Relative Error */ + relative_error?: number | null; + }; + /** + * ReportCalibrationWorstItem + * @description The calibration target that one match fits worst, with its relative error. + */ + ReportCalibrationWorstItem: { + /** Name */ + name: string; + /** Source */ + source: string; + /** Geography */ + geography: string; + /** Relative Error */ + relative_error?: number | null; + }; + /** + * ReportProvision + * @description One provision of the report's reform: a parameter's baseline and reform values. + * + * ``path`` is the parameter path and ``breadcrumb`` its readable label, and + * ``intervals`` optionally gives a dated schedule. ``growth`` says how a value + * set from one year on moves in later years (``fixed``, ``current_law`` or an + * index's parameter path); the other ``growth_*`` fields say when it moves and + * how grown values round. They are absent for a hand-set schedule. + */ + ReportProvision: { + /** Path */ + path: string; + /** Breadcrumb */ + breadcrumb: string; + /** Unit */ + unit?: string | null; + baseline_value: components['schemas']['FiniteJsonValue']; + value: components['schemas']['FiniteJsonValue']; + /** Intervals */ + intervals?: components['schemas']['ReportProvisionInterval'][] | null; + /** Growth */ + growth?: string | null; + /** Growth Start */ + growth_start?: number | null; + /** Growth Base */ + growth_base?: number | null; + /** Growth Round To */ + growth_round_to?: number | null; + /** Growth Round */ + growth_round?: ('nearest' | 'down' | 'up') | null; + }; + /** + * ReportProvisionInterval + * @description One dated value in a provision's schedule, from ``start_date`` to ``end_date``. + * + * Dates are ``YYYY-MM-DD`` strings and ``value`` is any finite JSON value. + * ``parameter`` is optional, and is left out of the stored report when unset. + */ + ReportProvisionInterval: { + /** Parameter */ + parameter?: string | null; + /** Start Date */ + start_date: string; + /** End Date */ + end_date: string; + value: components['schemas']['FiniteJsonValue']; + }; + /** + * ReportProvisionIntervalItem + * @description A provision's value for the dates from ``start_date`` to ``end_date``. + */ + ReportProvisionIntervalItem: { + /** Parameter */ + parameter?: string | null; + /** Start Date */ + start_date: string; + /** End Date */ + end_date: string; + value: components['schemas']['JsonValue']; + }; + /** + * ReportProvisionItem + * @description One parameter the report's reform changes, with its baseline and reform values. + * + * ``intervals`` gives the reform's dated values when they vary over time. The + * ``growth`` keys, present only when set, say how a value set from one year on + * moves in later years and how grown values round. + */ + ReportProvisionItem: { + /** Path */ + path: string; + /** Breadcrumb */ + breadcrumb: string; + /** Unit */ + unit?: string | null; + baseline_value: components['schemas']['JsonValue']; + value: components['schemas']['JsonValue']; + /** Intervals */ + intervals?: components['schemas']['ReportProvisionIntervalItem'][] | null; + /** Growth */ + growth?: string | null; + /** Growth Start */ + growth_start?: number | null; + /** Growth Base */ + growth_base?: number | null; + /** Growth Round To */ + growth_round_to?: number | null; + /** Growth Round */ + growth_round?: string | null; + }; + /** + * ReportScorecard + * @description The programs on the report's validation scorecard, at most 200 names. + */ + ReportScorecard: { + /** Programs */ + programs: string[]; + }; + /** + * ReportScorecardItem + * @description The programs on the scorecard saved with a report's validation. + */ + ReportScorecardItem: { + /** Programs */ + programs: string[]; + }; + /** + * ReportValidation + * @description The validation snapshot saved with a report. + * + * ``calibration`` and ``scorecard`` are optional, and the stored snapshot keeps + * only the keys supplied. + */ + ReportValidation: { + /** Matched At */ + matched_at: string; + /** Map Model Version */ + map_model_version: string; + calibration?: components['schemas']['ReportCalibration'] | null; + scorecard?: components['schemas']['ReportScorecard'] | null; + }; + /** + * ReportValidationItem + * @description The validation snapshot saved with a report. + * + * It records when the report was matched and with which map model version, + * and optionally the calibration results and scorecard. + */ + ReportValidationItem: { + /** Matched At */ + matched_at: string; + /** Map Model Version */ + map_model_version: string; + calibration?: components['schemas']['ReportCalibrationItem'] | null; + scorecard?: components['schemas']['ReportScorecardItem'] | null; + }; + /** + * SPMSelection + * @description Select a forecast scenario and an explicit household SPM geography. + */ + SPMSelection: { + /** Forecast Content Sha256 */ + forecast_content_sha256?: string | null; + /** Scenario */ + scenario?: string | null; + /** + * Geography Kind + * @enum {string} + */ + geography_kind?: 'county' | 'national' | 'metro'; + /** Geography Id */ + geography_id?: string | null; + /** County Vintage */ + county_vintage?: string; + /** + * As Of + * Format: date + * @description ISO calendar date (YYYY-MM-DD); must satisfy the selected artifact's information date + */ + as_of?: string | null; + } & ( + | { + /** @enum {unknown} */ + geography_kind?: 'county' | 'national'; + } + | { + /** @enum {unknown} */ + geography_kind: 'metro'; + geography_id: string; + } + ); + /** + * SharedBaselineReformItem + * @description The saved reform a shared report was compared with, named but not linked. + */ + SharedBaselineReformItem: { + /** Name */ + name: string | null; + }; + /** + * SharedUserReportDetailResponse + * @description Success response holding one shared report. + */ + SharedUserReportDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['SharedUserReportDetailResult']; + }; + /** + * SharedUserReportDetailResult + * @description Result holding one shared report in ``item``. + */ + SharedUserReportDetailResult: { + item: components['schemas']['SharedUserReportItem']; + }; + /** + * SharedUserReportItem + * @description A saved report as anyone holding its link reads it. + */ + SharedUserReportItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** Legacy Report Id */ + legacy_report_id: number | null; + /** Name */ + name: string | null; + /** Source Note */ + source_note: string | null; + /** Provisions */ + provisions: components['schemas']['ReportProvisionItem'][] | null; + validation: components['schemas']['ReportValidationItem'] | null; + /** Budget Window Years */ + budget_window_years: number | null; + baseline_reform: components['schemas']['SharedBaselineReformItem'] | null; + /** Last Run At */ + last_run_at: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * UKHouseholdDocument + * @description UK household content: people, households and benefit units. + */ + UKHouseholdDocument: { + /** People */ + people: components['schemas']['HouseholdPersonRecord'][]; + /** Household */ + household: components['schemas']['HouseholdEntityRecord'][]; + /** Benunit */ + benunit: components['schemas']['HouseholdEntityRecord'][]; + }; + /** + * USHouseholdDocument + * @description US household content: people and the US group entities they belong to. + * + * ``spm`` optionally selects an SPM forecast scenario and geography; omit it + * rather than sending null. + */ + 'USHouseholdDocument-Input': { + /** People */ + people: components['schemas']['HouseholdPersonRecord'][]; + /** Household */ + household: components['schemas']['HouseholdEntityRecord'][]; + /** Family */ + family: components['schemas']['HouseholdEntityRecord'][]; + /** Tax Unit */ + tax_unit: components['schemas']['HouseholdEntityRecord'][]; + /** Spm Unit */ + spm_unit: components['schemas']['HouseholdEntityRecord'][]; + /** Marital Unit */ + marital_unit: components['schemas']['HouseholdEntityRecord'][]; + /** Spm */ + spm?: components['schemas']['SPMSelection']; + }; + 'USHouseholdDocument-Output': { + [key: string]: unknown; + }; + /** + * UserHouseholdCreateRequest + * @description Association fields; the owner is taken from the request credential. + */ + UserHouseholdCreateRequest: { + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** Household Id */ + household_id?: string | null; + /** Legacy Household Id */ + legacy_household_id?: number | null; + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + }; + /** + * UserHouseholdDetailResponse + * @description Success response holding one user-household association. + */ + UserHouseholdDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserHouseholdDetailResult']; + }; + /** + * UserHouseholdDetailResult + * @description Result holding one user-household association in ``item``. + */ + UserHouseholdDetailResult: { + item: components['schemas']['UserHouseholdItem']; + }; + /** + * UserHouseholdItem + * @description A household saved by a v2 user, with the user's name and description for it. + * + * Unlike households, associations can be renamed, pointed at another + * household of the same country, or deleted. + */ + UserHouseholdItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * User Id + * Format: uuid + */ + user_id: string; + /** Household Id */ + household_id: string | null; + /** Legacy Household Id */ + legacy_household_id: number | null; + /** Name */ + name: string | null; + /** Description */ + description: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * UserHouseholdPageResponse + * @description Success response holding one page of user-household associations. + */ + UserHouseholdPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserHouseholdPageResult']; + }; + /** + * UserHouseholdPageResult + * @description One page of user-household associations, oldest first. + * + * ``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask + * for the next page with ``offset + limit``. + */ + UserHouseholdPageResult: { + /** Items */ + items: components['schemas']['UserHouseholdItem'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * UserHouseholdPatchRequest + * @description Explicitly supplied mutable link or presentation fields. + */ + UserHouseholdPatchRequest: { + /** Household Id */ + household_id?: string | null; + /** Legacy Household Id */ + legacy_household_id?: number | null; + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + }; + /** + * UserPolicyCreateRequest + * @description Association fields; the owner is taken from the request credential. + */ + UserPolicyCreateRequest: { + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** Policy Id */ + policy_id?: string | null; + /** Legacy Policy Id */ + legacy_policy_id?: number | null; + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + /** Baseline */ + baseline?: ('current-law' | 'current-policy') | null; + provenance?: components['schemas']['PolicyProvenance'] | null; + }; + /** + * UserPolicyDetailResponse + * @description Success response holding one user-policy association. + */ + UserPolicyDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserPolicyDetailResult']; + }; + /** + * UserPolicyDetailResult + * @description Result holding one user-policy association in ``item``. + */ + UserPolicyDetailResult: { + item: components['schemas']['UserPolicyItem']; + }; + /** + * UserPolicyItem + * @description A policy saved by a v2 user, with the user's name and description for it. + * + * Unlike policies, associations can be renamed or deleted. + */ + UserPolicyItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * User Id + * Format: uuid + */ + user_id: string; + /** Policy Id */ + policy_id: string | null; + /** Legacy Policy Id */ + legacy_policy_id: number | null; + /** Name */ + name: string | null; + /** Description */ + description: string | null; + /** Baseline */ + baseline: ('current-law' | 'current-policy') | null; + provenance: components['schemas']['PolicyProvenanceItem'] | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * UserPolicyPageResponse + * @description Success response holding one page of user-policy associations. + */ + UserPolicyPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserPolicyPageResult']; + }; + /** + * UserPolicyPageResult + * @description One page of user-policy associations, oldest first. + * + * ``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask + * for the next page with ``offset + limit``. + */ + UserPolicyPageResult: { + /** Items */ + items: components['schemas']['UserPolicyItem'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * UserPolicyPatchRequest + * @description Explicitly supplied mutable link or presentation fields. + */ + UserPolicyPatchRequest: { + /** Policy Id */ + policy_id?: string | null; + /** Legacy Policy Id */ + legacy_policy_id?: number | null; + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + /** Baseline */ + baseline?: ('current-law' | 'current-policy') | null; + provenance?: components['schemas']['PolicyProvenance'] | null; + }; + /** + * UserReportCreateRequest + * @description Association fields; the owner is taken from the request credential. + */ + UserReportCreateRequest: { + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + /** Source Note */ + source_note?: string | null; + /** Provisions */ + provisions?: components['schemas']['ReportProvision'][] | null; + validation?: components['schemas']['ReportValidation'] | null; + /** Budget Window Years */ + budget_window_years?: number | null; + /** Last Run At */ + last_run_at?: string | null; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * Legacy Report Id + * @description Exact v1 database identifier (a JSON integer) + */ + legacy_report_id: number; + /** User Policy Id */ + user_policy_id?: string | null; + /** Baseline User Policy Id */ + baseline_user_policy_id?: string | null; + }; + /** + * UserReportDetailResponse + * @description Success response holding one user-report association. + */ + UserReportDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserReportDetailResult']; + }; + /** + * UserReportDetailResult + * @description Result holding one user-report association in ``item``. + */ + UserReportDetailResult: { + item: components['schemas']['UserReportItem']; + }; + /** + * UserReportItem + * @description A report saved by a user, with the user's name, notes and saved presentation. + * + * ``legacy_report_id`` is the v1 report output it shows. ``user_policy_id`` is + * the user's saved reform the report was run from, and + * ``baseline_user_policy_id`` the saved reform it was compared with instead of + * current law; each is null when not set or once that reform is deleted. + * ``budget_window_years`` is null for a report without a budget window. + */ + UserReportItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * User Id + * Format: uuid + */ + user_id: string; + /** Report Id */ + report_id: string | null; + /** Legacy Report Id */ + legacy_report_id: number | null; + /** Name */ + name: string | null; + /** Description */ + description: string | null; + /** User Policy Id */ + user_policy_id: string | null; + /** Baseline User Policy Id */ + baseline_user_policy_id: string | null; + /** Source Note */ + source_note: string | null; + /** Provisions */ + provisions: components['schemas']['ReportProvisionItem'][] | null; + validation: components['schemas']['ReportValidationItem'] | null; + /** Budget Window Years */ + budget_window_years: number | null; + /** Last Run At */ + last_run_at: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * UserReportPageResponse + * @description Success response holding one page of user-report associations. + */ + UserReportPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserReportPageResult']; + }; + /** + * UserReportPageResult + * @description One page of user-report associations, oldest first. + * + * ``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask + * for the next page with ``offset + limit``. + */ + UserReportPageResult: { + /** Items */ + items: components['schemas']['UserReportItem'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * UserReportPatchRequest + * @description Explicitly supplied mutable link or presentation fields. + */ + UserReportPatchRequest: { + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + /** Source Note */ + source_note?: string | null; + /** Provisions */ + provisions?: components['schemas']['ReportProvision'][] | null; + validation?: components['schemas']['ReportValidation'] | null; + /** Budget Window Years */ + budget_window_years?: number | null; + /** Last Run At */ + last_run_at?: string | null; + /** Legacy Report Id */ + legacy_report_id?: number | null; + /** User Policy Id */ + user_policy_id?: string | null; + /** Baseline User Policy Id */ + baseline_user_policy_id?: string | null; + }; + /** + * UserSimulationCreateRequest + * @description Association fields; the owner is taken from the request credential. + */ + UserSimulationCreateRequest: { + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * Legacy Simulation Id + * @description Exact v1 database identifier (a JSON integer) + */ + legacy_simulation_id: number; + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + }; + /** + * UserSimulationDetailResponse + * @description Success response holding one user-simulation association. + */ + UserSimulationDetailResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserSimulationDetailResult']; + }; + /** + * UserSimulationDetailResult + * @description Result holding one user-simulation association in ``item``. + */ + UserSimulationDetailResult: { + item: components['schemas']['UserSimulationItem']; + }; + /** + * UserSimulationItem + * @description A simulation saved by a user, with the user's name and description for it. + * + * ``legacy_simulation_id`` is the v1 simulation it saves. Associations can be + * renamed, pointed at another v1 simulation of the same country, or deleted. + */ + UserSimulationItem: { + /** + * Id + * Format: uuid + */ + id: string; + /** + * Country Id + * @description Supported PolicyEngine country ID + * @enum {string} + */ + country_id: 'us' | 'uk'; + /** + * User Id + * Format: uuid + */ + user_id: string; + /** Simulation Id */ + simulation_id: string | null; + /** Legacy Simulation Id */ + legacy_simulation_id: number | null; + /** Name */ + name: string | null; + /** Description */ + description: string | null; + /** + * Created At + * Format: date-time + */ + created_at: string; + /** + * Updated At + * Format: date-time + */ + updated_at: string; + }; + /** + * UserSimulationPageResponse + * @description Success response holding one page of user-simulation associations. + */ + UserSimulationPageResponse: { + /** + * Status + * @default ok + * @constant + */ + status: 'ok'; + /** Message */ + message?: null; + result: components['schemas']['UserSimulationPageResult']; + }; + /** + * UserSimulationPageResult + * @description One page of user-simulation associations, oldest first. + * + * ``offset`` and ``limit`` echo the request; when ``has_more`` is true, ask + * for the next page with ``offset + limit``. + */ + UserSimulationPageResult: { + /** Items */ + items: components['schemas']['UserSimulationItem'][]; + /** Offset */ + offset: number; + /** Limit */ + limit: number; + /** Has More */ + has_more: boolean; + }; + /** + * UserSimulationPatchRequest + * @description Explicitly supplied mutable link or presentation fields. + */ + UserSimulationPatchRequest: { + /** Legacy Simulation Id */ + legacy_simulation_id?: number | null; + /** Name */ + name?: string | null; + /** Description */ + description?: string | null; + }; + /** + * V2ErrorResponse + * @description Strict error response shared by every API v2 resource. + */ + V2ErrorResponse: { + /** + * Status + * @default error + * @constant + */ + status: 'error'; + /** Message */ + message: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + get_me_v2_me_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MeDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + delete_me_v2_me_delete: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Account data was deleted but the Auth0 user could not be removed; retrying is safe. */ + 502: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Auth0 Management credentials could not be obtained, or persistence is unavailable; nothing was deleted. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + patch_me_v2_me_patch: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['AccountPatchRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MeDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + export_me_v2_me_export_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MeExportResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + upgrade_me_v2_me_upgrade_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header: { + /** @description The browser's unauthenticated user ID (a canonical UUID). */ + 'PolicyEngine-Unauthenticated-User': string; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MeUpgradeResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_reports_v2_user_reports_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + legacy_report_id?: number | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserReportPageResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + create_user_report_v2_user_reports_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserReportCreateRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserReportDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_report_v2_user_reports__association_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserReportDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + delete_user_report_route_v2_user_reports__association_id__delete: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + patch_user_report_route_v2_user_reports__association_id__patch: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserReportPatchRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserReportDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_shared_report_v2_shared_reports__association_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['SharedUserReportDetailResponse']; + }; + }; + /** @description No saved report has this ID in this country. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The ID or the query does not match the schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_simulations_v2_user_simulations_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + legacy_simulation_id?: number | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserSimulationPageResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + create_user_simulation_v2_user_simulations_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserSimulationCreateRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserSimulationDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_simulation_v2_user_simulations__association_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserSimulationDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + delete_user_simulation_route_v2_user_simulations__association_id__delete: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + patch_user_simulation_route_v2_user_simulations__association_id__patch: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserSimulationPatchRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserSimulationDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_households_v2_user_households_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + household_id?: string | null; + legacy_household_id?: number | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserHouseholdPageResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + create_user_household_v2_user_households_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserHouseholdCreateRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserHouseholdDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_household_v2_user_households__association_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserHouseholdDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + delete_user_household_route_v2_user_households__association_id__delete: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + patch_user_household_route_v2_user_households__association_id__patch: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserHouseholdPatchRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserHouseholdDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_policies_v2_user_policies_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policy_id?: string | null; + legacy_policy_id?: number | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserPolicyPageResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + create_user_policy_v2_user_policies_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserPolicyCreateRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserPolicyDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_user_policy_v2_user_policies__association_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserPolicyDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + delete_user_policy_route_v2_user_policies__association_id__delete: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + patch_user_policy_route_v2_user_policies__association_id__patch: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + association_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['UserPolicyPatchRequest']; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['UserPolicyDetailResponse']; + }; + }; + /** @description The supplied references or countries contradict each other. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description A valid bearer access token, or a canonical UUID in the PolicyEngine-Unauthenticated-User header, is required; that UUID may not name a signed-in account. The response carries WWW-Authenticate: Bearer. */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Machine (client-credentials) principals are not permitted, and unauthenticated users cannot edit account details. */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The record does not exist or belongs to another user. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The change conflicts with stored state, such as a record maintained by the v1 saved-policy mirror. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request body exceeds 256 KiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the schema, or a referenced v1 artifact or linked record does not exist. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The operation could not be completed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description User authentication or persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_households_v2_households_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + default_year?: number | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HouseholdPageResponse']; + }; + }; + /** @description The household content or country selection is invalid. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The selected household does not exist. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Immutable household content conflicts with stored state. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The household request body exceeds 1 MiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the household schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Stored household integrity validation failed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Supabase household persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + create_household_v2_households_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['HouseholdCreateRequest']; + }; + }; + responses: { + /** @description Equivalent immutable content already exists. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HouseholdDetailResponse']; + }; + }; + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HouseholdDetailResponse']; + }; + }; + /** @description The household content or country selection is invalid. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The selected household does not exist. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Immutable household content conflicts with stored state. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The household request body exceeds 1 MiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the household schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Stored household integrity validation failed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Supabase household persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_household_v2_households__household_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + household_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['HouseholdDetailResponse']; + }; + }; + /** @description The household content or country selection is invalid. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The selected household does not exist. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Immutable household content conflicts with stored state. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The household request body exceeds 1 MiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the household schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Stored household integrity validation failed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Supabase household persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_policies_v2_policies_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + tax_benefit_model_id?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['PolicyPageResponse']; + }; + }; + /** @description The policy content or country selection is invalid. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The selected policy or catalog does not exist. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Immutable policy identity conflicts with stored state. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The policy request body exceeds 1 MiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the policy schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Stored policy integrity validation failed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Supabase policy persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + create_policy_v2_policies_post: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + 'application/json': components['schemas']['PolicyCreateRequest']; + }; + }; + responses: { + /** @description Equivalent immutable content already exists. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['PolicyDetailResponse']; + }; + }; + /** @description Successful Response */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['PolicyDetailResponse']; + }; + }; + /** @description The policy content or country selection is invalid. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The selected policy or catalog does not exist. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Immutable policy identity conflicts with stored state. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The policy request body exceeds 1 MiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the policy schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Stored policy integrity validation failed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Supabase policy persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_policy_v2_policies__policy_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + }; + header?: never; + path: { + policy_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['PolicyDetailResponse']; + }; + }; + /** @description The policy content or country selection is invalid. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The selected policy or catalog does not exist. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Immutable policy identity conflicts with stored state. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The policy request body exceeds 1 MiB. */ + 413: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The request does not match the policy schema. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Stored policy integrity validation failed. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description Supabase policy persistence is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_models_v2_tax_benefit_models_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataModelPageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_model_by_country_v2_tax_benefit_models_by_country__country_id__get: { + parameters: { + query?: { + policyengine_version?: string | null; + }; + header?: never; + path: { + country_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataModelSelectionResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The country in the path is not supported. Query parameters are checked first: an invalid one, including the PolicyEngine.py version, returns 422. */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_model_v2_tax_benefit_models__model_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + model_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataModelDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_model_versions_v2_tax_benefit_model_versions_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataModelVersionPageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_model_version_v2_tax_benefit_model_versions__version_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + version_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataModelVersionDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_variables_v2_variables_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + /** @description Case-insensitive substring of the name, label or description */ + search?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataVariablePageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_variable_v2_variables__variable_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + variable_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataVariableDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_parameters_v2_parameters_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + /** @description Case-insensitive substring of the name, label or description */ + search?: string | null; + /** @description Keep only these exact parameter paths; repeat the key */ + name?: string[] | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataParameterPageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_parameter_children_v2_parameters_children_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + /** @description Path of the node whose direct children are listed; empty for the top level. A bracket schedule's brackets are its children. */ + parent_path?: string; + /** @description List only the children a reform can change, sorted by label, with child counts that count only those */ + reform_changeable?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataParameterChildPageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable, or the catalog version has no parameter tree (publication writes trees only for the versions it publishes and for the installed version with catalog format 2); the message then names the version. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_parameter_search_file_v2_parameters_search_file_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The catalog version's parameter search file, gzip-encoded when the request accepts gzip. */ + 200: { + headers: { + /** @description Strong tag of this representation: the SHA-256 of the uncompressed JSON, with "-gzip" appended for the gzip-encoded one. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + /** @description gzip when the request accepts gzip; absent otherwise. */ + 'Content-Encoding'?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataParameterSearchFileContent']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable, or the catalog version has no parameter search file: only catalog format 2 versions that publication has published, or run with installed, since search files were added have one. The message then names the version. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_parameter_v2_parameters__parameter_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + parameter_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataParameterDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_parameter_values_v2_parameter_values_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + parameter_id?: string | null; + /** @description Keep only these exact parameter paths' values; repeat the key */ + parameter_name?: string[] | null; + /** @description Keep only the values in effect today (UTC) */ + current?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataParameterValuePageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_parameter_value_v2_parameter_values__value_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + value_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataParameterValueDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_datasets_v2_datasets_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataDatasetPageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_dataset_v2_datasets__dataset_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + dataset_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataDatasetDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + list_regions_v2_regions_get: { + parameters: { + query: { + /** @description Zero-based result offset */ + offset?: number; + /** @description Maximum number of returned resources */ + limit?: number; + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + /** @description Keep only regions of this type */ + region_type?: components['schemas']['MetadataRegionType'] | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataRegionPageResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_region_by_code_v2_regions_by_code__region_code__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + region_code: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataRegionDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_region_v2_regions__region_id__get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path: { + region_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataRegionDetailResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_economy_options_v2_economy_options_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataEconomyOptionsResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_model_catalog_v2_model_catalog_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataModelCatalogResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_catalog_variables_v2_model_catalog_variables_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataCatalogVariablesResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; + get_catalog_parameters_v2_model_catalog_parameters_get: { + parameters: { + query: { + /** @description Supported PolicyEngine country ID */ + country_id: 'us' | 'uk'; + policyengine_version?: string | null; + /** @description Exact parameter paths; repeat the key for each one */ + name: string[]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['MetadataCatalogParametersResponse']; + }; + }; + /** @description Not modified: If-None-Match names the current ETag. No body is sent. */ + 304: { + headers: { + /** @description Strong validator: a hash of the uncompressed response body, so a gzip-encoded response carries the same tag. */ + ETag?: string; + /** @description public, with max-age 86400 when the request names policyengine_version and 300 otherwise; current parameter values expire at the next UTC midnight. */ + 'Cache-Control'?: string; + /** @description Origin, Accept-Encoding: caches keep responses apart by origin and by compression; a 304 carries the same value. */ + Vary?: string; + [name: string]: unknown; + }; + content?: never; + }; + /** @description The requested catalog resource is absent. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The dormant v2 metadata resources support GET only. */ + 405: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The query parameters do not match the resource schema, for example an unknown parameter or a malformed PolicyEngine.py version. */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The resource query failed internally. */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + /** @description The initialized v2 catalog is unavailable. */ + 503: { + headers: { + [name: string]: unknown; + }; + content: { + 'application/json': components['schemas']['V2ErrorResponse']; + }; + }; + }; + }; +} diff --git a/app/src/api/publicApi/searchFile.ts b/app/src/api/publicApi/searchFile.ts new file mode 100644 index 000000000..04db35901 --- /dev/null +++ b/app/src/api/publicApi/searchFile.ts @@ -0,0 +1,49 @@ +import { + createPublicApiClient, + publicApiCountryId, + requireBody, + type PublicApiClient, +} from '@/api/publicApi/client'; +import type { components } from '@/api/publicApi/schema'; + +/** + * One catalog version's parameter search file (`GET /v2/parameters/search-file`): + * every parameter a reform can change, with its label, unit, description + * and whether a draft can edit its value by date; the labels of parameters + * a reform cannot change; and every labelled node. It carries no values. + * v3 searches it in its existing web worker (see libs/flagship/v2ParameterSearch). + */ +export type ParameterSearchFile = components['schemas']['MetadataParameterSearchFileContent']; +export type ParameterSearchFileParameter = + components['schemas']['MetadataParameterSearchFileParameter']; + +/** The file format this app reads; a later format needs code that reads it. */ +export const SUPPORTED_SEARCH_FILE_FORMAT = 1; + +/** + * The search file for a catalog version. Naming the version lets the + * browser keep the response (the API marks a named version's file as + * long-lived, and it arrives gzip-encoded). + */ +export async function fetchParameterSearchFile( + countryId: string, + policyengineVersion: string, + client: PublicApiClient = createPublicApiClient() +): Promise { + const file = await requireBody( + client.GET('/v2/parameters/search-file', { + params: { + query: { + country_id: publicApiCountryId(countryId), + policyengine_version: policyengineVersion, + }, + }, + }) + ); + if (file.format !== SUPPORTED_SEARCH_FILE_FORMAT) { + throw new Error( + `The Public API sent search file format ${file.format}; this app reads format ${SUPPORTED_SEARCH_FILE_FORMAT}.` + ); + } + return file; +} diff --git a/app/src/api/reformStore.ts b/app/src/api/reformStore.ts index 411c7f572..c9deff73c 100644 --- a/app/src/api/reformStore.ts +++ b/app/src/api/reformStore.ts @@ -1,5 +1,13 @@ import { convertDateRangeMapToValueIntervals } from '@/adapters/conversionHelpers'; import { fetchPolicyById } from '@/api/policy'; +import { + createPublicApiClient, + publicApiCountryId, + requireResult, + type PublicApiClient, +} from '@/api/publicApi/client'; +import { finiteJsonValueForApi, parameterValueFromApi } from '@/api/publicApi/jsonValues'; +import type { components } from '@/api/publicApi/schema'; import { AccountAssociationRoutes, BrowserOnlyIdError, @@ -7,29 +15,40 @@ import { isLegacyId, type ApiStoreContext, } from '@/api/v2Associations'; +import { FOREVER } from '@/constants'; import { createReformPolicy } from '@/libs/flagship/runReport'; import { clampStoreText } from '@/libs/flagship/storeText'; +import { V3_API_ROUTES, type V3ApiRoutes } from '@/libs/flagship/v3ApiRoutes'; import { Reform, ReformBaseline, ReformProvenance } from '@/types/ingredients/Reform'; +import type { V2CatalogReference } from '@/types/metadata'; import type { ParameterMetadataCollection } from '@/types/metadata/parameterMetadata'; import type { PolicyMetadataParams } from '@/types/metadata/policyMetadata'; import { Parameter } from '@/types/subIngredients/parameter'; import { ValueInterval } from '@/types/subIngredients/valueInterval'; -import type { - ProvenanceWire, - UserPolicyCreateBody, - UserPolicyItem, - UserPolicyPatchBody, -} from '@/types/v2User'; +import type { ProvenanceWire, UserPolicyItem } from '@/types/v2User'; +import { coercePolicyParameterValue, resolvePolicyParameterValueSpec } from '@/utils/valueCoercion'; + +/** + * The `/v2/user-policies` bodies, as the Public API's OpenAPI document gives + * them. The API refuses a row that names neither `policy_id` nor + * `legacy_policy_id`. + */ +type UserPolicyCreateRequest = components['schemas']['UserPolicyCreateRequest']; +type UserPolicyPatchRequest = components['schemas']['UserPolicyPatchRequest']; /** * Store for the flagship app's reform objects. * - * Either way, saving a reform materializes its parameters as a v1 policy - * (`POST /{country}/policy`, which dedupes identical ones), and a save - * that cannot create the policy fails. Signed out (and outside the US and - * UK) the reform, with its policy ID, lives in localStorage. Signed in, it - * is a row in the account's `/v2/user-policies` carrying the policy ID, - * the name, the baseline, and the provenance. + * Saving a reform first makes its parameters a policy, and a save that + * cannot create the policy fails. Where saved work is in the browser + * (sign-in not configured, or outside the US and UK) the reform, with the + * ID of its v1 policy (`POST /{country}/policy`), lives in localStorage. + * Where it is in the account, signed in or signed out, the reform is a row + * in `/v2/user-policies` carrying the name, the baseline, the provenance + * and the policy: a v1 policy (`legacy_policy_id`, ApiReformStore) before + * the switch, and a `/v2/policies` policy (`policy_id`, ApiV2ReformStore) + * after it. V3_API_ROUTES picks the account store. Both APIs deduplicate + * identical policies. */ export type NewReform = Omit; export type ReformUpdates = Partial>; @@ -301,7 +320,7 @@ export class ApiReformStore implements ReformStore { } /** The row for a reform; its parameters become a v1 policy first when they are not one yet. */ - private async creationBody(reform: NewReform): Promise { + private async creationBody(reform: NewReform): Promise { const countryId = this.routes.writeCountry(reform.countryId); const legacyPolicyId = isLegacyId(reform.policyId) ? Number(reform.policyId) @@ -351,7 +370,7 @@ export class ApiReformStore implements ReformStore { if (!isAccountId(reformId)) { throw new BrowserOnlyIdError(); } - const body: UserPolicyPatchBody = {}; + const body: UserPolicyPatchRequest = {}; if (updates.label !== undefined) { body.name = clampStoreText(updates.label) || null; } @@ -403,10 +422,329 @@ export class ApiReformStore implements ReformStore { } } -/** The reform store for a session: the account's when given one, else localStorage. */ +/* Saved reforms on the v2 routes (V3_API_ROUTES 'v2'). */ + +type V2PolicyItem = components['schemas']['PolicyItem']; +type V2PolicyCreateBody = components['schemas']['PolicyCreateRequest']; + +/** A v2 policy's JSON value: infinities as the API spells them (JSON has none). */ +function v2PolicyValue(value: unknown): unknown { + if (value === Infinity) { + return 'Infinity'; + } + if (value === -Infinity) { + return '-Infinity'; + } + return value; +} + +/** A date as the UTC midnight the v2 API stores (v1 periods are whole days). */ +function utcMidnight(date: string): string { + return `${date}T00:00:00Z`; +} + +/** + * The `POST /v2/policies` body for a reform's parameters: each dated value + * names its parameter's catalog UUID (`parameterIds`, from the parameters + * loaded on demand), and is typed as a v1 run types it when the + * parameter's metadata is loaded. Dates keep v1's whole-day periods + * (including its 2100-12-31 "forever"), as the dual-write copies of v1 + * policies do, so identical changes deduplicate to one v2 policy. + */ +export function v2ReformPolicyBody( + countryId: string, + parameters: Parameter[], + catalog: V2CatalogReference, + parameterIds: Record, + metadata?: ParameterMetadataCollection +): V2PolicyCreateBody { + return { + country_id: publicApiCountryId(countryId), + tax_benefit_model_id: catalog.taxBenefitModelId, + parameter_values: parameters.flatMap((parameter) => { + const parameterId = parameterIds[parameter.name]; + if (!parameterId) { + throw new Error( + `${parameter.name} is not a parameter of the PolicyEngine model ${catalog.policyengineVersion}` + ); + } + const valueSpec = + metadata?.[parameter.name]?.type === 'parameter' + ? resolvePolicyParameterValueSpec(parameter.name, metadata) + : null; + return parameter.values.map((interval) => ({ + parameter_id: parameterId, + value: finiteJsonValueForApi( + v2PolicyValue( + valueSpec + ? coercePolicyParameterValue(interval.value, valueSpec, parameter.name) + : interval.value + ), + parameter.name + ), + start_date: utcMidnight(interval.startDate), + end_date: utcMidnight(interval.endDate), + })); + }), + }; +} + +/** Creates (or, as the API deduplicates, finds) a reform's v2 policy; returns its ID. */ +export type CreateV2ReformPolicy = ( + countryId: string, + parameters: Parameter[], + catalog: V2CatalogReference, + parameterIds: Record, + metadata?: ParameterMetadataCollection +) => Promise; + +export async function createV2ReformPolicy( + countryId: string, + parameters: Parameter[], + catalog: V2CatalogReference, + parameterIds: Record, + metadata?: ParameterMetadataCollection, + client: PublicApiClient = createPublicApiClient() +): Promise { + const body = v2ReformPolicyBody(countryId, parameters, catalog, parameterIds, metadata); + const result = await requireResult( + client.POST('/v2/policies', { + params: { + query: { + country_id: body.country_id, + policyengine_version: catalog.policyengineVersion, + }, + }, + body, + }) + ); + return result.item.id; +} + +/** A v2 policy. Policies never change, so callers may cache them. */ +export async function fetchV2PolicyItem( + countryId: string, + policyId: string, + client: PublicApiClient = createPublicApiClient() +): Promise { + const result = await requireResult( + client.GET('/v2/policies/{policy_id}', { + params: { + path: { policy_id: policyId }, + query: { country_id: publicApiCountryId(countryId) }, + }, + }) + ); + return result.item; +} + +/** A v2 policy's values as reform parameters, each oldest first; an open end is v1's forever. */ +export function parametersFromV2Policy( + policy: Pick +): Parameter[] { + const byName = new Map(); + for (const parameterValue of policy.parameter_values) { + const intervals = byName.get(parameterValue.parameter_name) ?? []; + intervals.push({ + startDate: parameterValue.start_date.slice(0, 10), + endDate: parameterValue.end_date ? parameterValue.end_date.slice(0, 10) : FOREVER, + value: parameterValueFromApi(parameterValue.value, parameterValue.parameter_name), + }); + byName.set(parameterValue.parameter_name, intervals); + } + return [...byName].map(([name, values]) => ({ + name, + values: values.sort((first, second) => first.startDate.localeCompare(second.startDate)), + })); +} + +export interface ApiV2ReformStoreOptions extends ApiReformStoreOptions { + /** The loaded v2 catalog version: its PolicyEngine.py version and model ID. */ + getV2Catalog?: () => V2CatalogReference | null | undefined; + /** + * The catalog IDs of the named parameters, loading any not loaded yet + * (they come with each parameter loaded on demand); names the catalog + * version does not have are left out. + */ + resolveParameterIds?: ( + countryId: string, + names: readonly string[] + ) => Promise>; + createV2Policy?: CreateV2ReformPolicy; + /** Reads a v2 policy. */ + fetchV2Policy?: (countryId: string, policyId: string) => Promise; +} + +/** The model catalog must be loaded from the v2 routes before a v2 policy can be made. */ +export class V2CatalogUnavailableError extends Error { + constructor() { + super('The PolicyEngine model has not loaded yet, so this reform cannot be saved.'); + this.name = 'V2CatalogUnavailableError'; + } +} + +/** Whether an account row is a v3 reform: reforms always carry provenance. */ +function isV3Reform(item: UserPolicyItem): boolean { + return item.provenance !== null && (item.policy_id !== null || isLegacyId(item.legacy_policy_id)); +} + +/** + * Reforms in the account (`/v2/user-policies`) on v2 policies. A save makes + * the parameters a `/v2/policies` policy and the row points at it with + * `policy_id`; parameter edits make a new policy and repoint the row (the + * API then clears any `legacy_policy_id`); a rename only patches the name. + * Rows saved before the switch that hold only a v1 policy + * (`legacy_policy_id`) still open: their parameters are read from + * `GET /{country}/policy/{id}` until the one-time migration links them to + * v2 policies. + */ +export class ApiV2ReformStore implements ReformStore { + private readonly routes: AccountAssociationRoutes; + private readonly getParameterMetadata: NonNullable; + private readonly getV2Catalog: NonNullable; + private readonly resolveParameterIds: NonNullable; + private readonly createV2Policy: CreateV2ReformPolicy; + private readonly fetchV2Policy: NonNullable; + private readonly fetchPolicyJson: NonNullable; + + constructor( + private readonly context: ApiStoreContext, + options: ApiV2ReformStoreOptions = {} + ) { + this.routes = new AccountAssociationRoutes(context, 'user-policies', 'Reform'); + this.getParameterMetadata = options.getParameterMetadata ?? (() => null); + this.getV2Catalog = options.getV2Catalog ?? (() => null); + this.resolveParameterIds = options.resolveParameterIds ?? (async () => ({})); + this.createV2Policy = options.createV2Policy ?? createV2ReformPolicy; + this.fetchV2Policy = options.fetchV2Policy ?? fetchV2PolicyItem; + this.fetchPolicyJson = options.fetchPolicyJson ?? fetchV1PolicyJson; + } + + /** The v2 policy for a set of parameters, created for the loaded catalog version. */ + private async materialize(countryId: string, parameters: Parameter[]): Promise { + const catalog = this.getV2Catalog(); + if (!catalog) { + throw new V2CatalogUnavailableError(); + } + const parameterIds = await this.resolveParameterIds( + countryId, + parameters.map((parameter) => parameter.name) + ); + return this.createV2Policy( + countryId, + parameters, + catalog, + parameterIds, + this.getParameterMetadata() ?? undefined + ); + } + + async create(reform: NewReform): Promise { + const countryId = this.routes.writeCountry(reform.countryId); + const policyId = isAccountId(reform.v2PolicyId) + ? reform.v2PolicyId + : await this.materialize(countryId, reform.parameters); + const item = await this.routes.create({ + country_id: countryId, + policy_id: policyId, + ...(reform.label ? { name: clampStoreText(reform.label) } : {}), + baseline: reform.baseline, + provenance: provenanceToWire(reform.provenance), + }); + return this.reformFromItem(item, reform.parameters); + } + + async findMine(countryId?: string): Promise { + if (countryId && countryId !== this.context.countryId) { + return []; + } + const items = (await this.routes.list()).filter(isV3Reform); + const loaded = await Promise.allSettled(items.map((item) => this.toReform(item))); + return byRecentUpdate( + loaded.map((outcome, index) => + outcome.status === 'fulfilled' + ? outcome.value + : { ...this.reformFromItem(items[index], []), parametersUnavailable: true } + ) + ); + } + + async findById(reformId: string): Promise { + const item = await this.routes.get(reformId); + return item && isV3Reform(item) ? this.toReform(item) : null; + } + + async update(reformId: string, updates: ReformUpdates): Promise { + // Refuse a browser ID before creating a policy for it. + if (!isAccountId(reformId)) { + throw new BrowserOnlyIdError(); + } + const body: UserPolicyPatchRequest = {}; + if (updates.label !== undefined) { + body.name = clampStoreText(updates.label) || null; + } + if (updates.baseline !== undefined) { + body.baseline = updates.baseline; + } + if (updates.provenance !== undefined) { + body.provenance = provenanceToWire(updates.provenance); + } + if (updates.parameters !== undefined) { + body.policy_id = await this.materialize(this.context.countryId, updates.parameters); + } else if (isAccountId(updates.v2PolicyId)) { + body.policy_id = updates.v2PolicyId; + } + const item = await this.routes.patch(reformId, body); + return updates.parameters !== undefined + ? this.reformFromItem(item, updates.parameters) + : this.toReform(item); + } + + delete(reformId: string): Promise { + return this.routes.delete(reformId); + } + + private async toReform(item: UserPolicyItem): Promise { + const parameters = item.policy_id + ? parametersFromV2Policy(await this.fetchV2Policy(item.country_id, item.policy_id)) + : parametersFromPolicyJson( + await this.fetchPolicyJson(item.country_id, String(item.legacy_policy_id)), + this.getParameterMetadata() + ); + return this.reformFromItem(item, parameters); + } + + private reformFromItem(item: UserPolicyItem, parameters: Parameter[]): Reform { + return { + id: item.id, + countryId: item.country_id, + label: item.name ?? undefined, + policyId: isLegacyId(item.legacy_policy_id) ? String(item.legacy_policy_id) : null, + v2PolicyId: item.policy_id, + parameters, + baseline: (item.baseline ?? 'current-law') as ReformBaseline, + provenance: provenanceFromWire(item.provenance), + createdAt: item.created_at, + updatedAt: item.updated_at, + }; + } +} + +/** + * The reform store for a session: the account's when given one, else + * localStorage. With an account, `routes` (V3_API_ROUTES unless a caller + * says otherwise) picks v1 or v2 policies; reforms kept in this browser + * make v1 policies either way. + */ export function getReformStore( account?: ApiStoreContext | null, - options?: ApiReformStoreOptions + options?: ApiV2ReformStoreOptions, + routes: V3ApiRoutes = V3_API_ROUTES ): ReformStore { - return account ? new ApiReformStore(account, options) : new LocalStorageReformStore(options); + if (!account) { + return new LocalStorageReformStore(options); + } + return routes === 'v2' + ? new ApiV2ReformStore(account, options) + : new ApiReformStore(account, options); } diff --git a/app/src/api/v2UserClient.ts b/app/src/api/v2UserClient.ts index 11cbdddac..3f8645d30 100644 --- a/app/src/api/v2UserClient.ts +++ b/app/src/api/v2UserClient.ts @@ -114,8 +114,15 @@ async function readEnvelope(response: Response): Promise | n } } +/** The message for a failed response: the API's own, else one for its status. */ +export function failureMessage(status: number, apiMessage?: string | null): string { + return apiMessage || STATUS_MESSAGES[status] || `Request failed (${status})`; +} + /** The header naming who a request is made as. */ -async function credentialHeaders(credentials: V2Credentials): Promise> { +export async function credentialHeaders( + credentials: V2Credentials +): Promise> { if (credentials.kind === 'none') { return {}; } @@ -175,12 +182,7 @@ export function createV2UserClient({ const envelope = await readEnvelope(response); if (!response.ok) { - throw new V2ApiError( - response.status, - envelope?.message || - STATUS_MESSAGES[response.status] || - `Request failed (${response.status})` - ); + throw new V2ApiError(response.status, failureMessage(response.status, envelope?.message)); } if (!envelope || envelope.status !== 'ok') { throw new V2ApiError( diff --git a/app/src/components/flagship/ModelCatalogGate.tsx b/app/src/components/flagship/ModelCatalogGate.tsx new file mode 100644 index 000000000..f3040a863 --- /dev/null +++ b/app/src/components/flagship/ModelCatalogGate.tsx @@ -0,0 +1,50 @@ +import { ErrorState } from '@/components/flagship/states'; +import { Spinner } from '@/components/ui'; +import { colors, spacing, typography } from '@/designTokens'; +import { useFlagshipMetadataStatus } from '@/hooks/useFlagshipMetadataStatus'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; + +/** + * On the v2 routes, a v3 page waits for the small model catalog (the + * version, regions, years and entities it is built on) behind a short + * loading page, the only one: everything else it shows waits in its own + * skeletons. A failure says so and asks again. On the v1 routes the page + * shows at once, as before, with its skeletons standing for the whole + * metadata. + */ +function V2ModelCatalogGate({ children }: { children: React.ReactNode }) { + const metadata = useFlagshipMetadataStatus(); + if (metadata.status === 'error') { + return ( +
+ +
+ ); + } + if (metadata.status === 'loading') { + return ( +
+ +
+ ); + } + return <>{children}; +} + +function V1ModelCatalogGate({ children }: { children: React.ReactNode }) { + return <>{children}; +} + +export default V3_API_ROUTES === 'v2' ? V2ModelCatalogGate : V1ModelCatalogGate; diff --git a/app/src/components/flagship/WorkspaceLayout.tsx b/app/src/components/flagship/WorkspaceLayout.tsx index 852886afe..7ec072b26 100644 --- a/app/src/components/flagship/WorkspaceLayout.tsx +++ b/app/src/components/flagship/WorkspaceLayout.tsx @@ -1,4 +1,6 @@ +import ModelCatalogGate from '@/components/flagship/ModelCatalogGate'; import { spacing } from '@/designTokens'; +import { useV3SearchFileInBackground } from '@/hooks/useV3SearchCatalog'; interface WorkspaceLayoutProps { children: React.ReactNode; @@ -13,6 +15,13 @@ interface WorkspaceLayoutProps { */ export default function WorkspaceLayout({ children, wide = false }: WorkspaceLayoutProps) { const contentWidth = wide ? 1400 : 760; + // On the v2 routes, the search file starts downloading once the page has + // rendered, so it is in by the time someone searches (nothing on v1). + useV3SearchFileInBackground(); - return
{children}
; + return ( +
+ {children} +
+ ); } diff --git a/app/src/components/flagship/policyBrowser/ParameterRow.tsx b/app/src/components/flagship/policyBrowser/ParameterRow.tsx index afe361bde..2425b3a99 100644 --- a/app/src/components/flagship/policyBrowser/ParameterRow.tsx +++ b/app/src/components/flagship/policyBrowser/ParameterRow.tsx @@ -1,10 +1,13 @@ import { IconCheck, IconPlus } from '@tabler/icons-react'; +import { SkeletonLine } from '@/components/flagship/states'; import { colors, spacing, typography } from '@/designTokens'; import { ParameterTreeNode } from '@/types/metadata'; interface ParameterRowProps { node: ParameterTreeNode; value: string | null; + /** The value is still loading: a placeholder stands for it. */ + valueLoading?: boolean; inDraft: boolean; divided: boolean; onAdd: () => void; @@ -16,7 +19,14 @@ interface ParameterRowProps { * Either way a click opens it for editing. The description waits in the * tooltip, so the list reads as names. */ -export default function ParameterRow({ node, value, inDraft, divided, onAdd }: ParameterRowProps) { +export default function ParameterRow({ + node, + value, + valueLoading = false, + inDraft, + divided, + onAdd, +}: ParameterRowProps) { return ( - option.id === draft.baselineReformId) : undefined; + // Current law for the comparison's parameters: on the v2 routes they + // load once it is chosen (the draft's own load with the draft). + const baselineNames = useMemo(() => (reform ? reformParameterNames([reform]) : []), [reform]); + const baselineParameters = useEnsureV3Parameters(baselineNames); const forRun: RunReportBaseline | undefined = reform && { reformId: reform.id, label: savedReformName(reform), @@ -71,8 +78,18 @@ export function useDraftBaseline(draft: DraftReform) { options, reform, forRun, - /** The chosen comparison is not known until the saved reforms load. */ - pending: !!draft.baselineReformId && isPending, + /** + * The chosen comparison is not known until the saved reforms load, nor + * what it changes until current law for its parameters is in. + */ + pending: (!!draft.baselineReformId && isPending) || (!!reform && baselineParameters.waiting), + /** + * Current law for the comparison's parameters failed to load (on the v2 + * routes; on v1 it comes with the whole metadata): what it changes is + * unknown, so the run waits for a retry. + */ + failed: !!reform && V3_API_ROUTES === 'v2' && baselineParameters.status === 'error', + retry: baselineParameters.retry, /** The draft changes just what its comparison changes: the report would show no change. */ matchesDraft: !!reform && draftChanges.length > 0 && sameReformParameters(baselineChanges, draftChanges), diff --git a/app/src/components/flagship/reform/DraftHeader.tsx b/app/src/components/flagship/reform/DraftHeader.tsx index 45da4249e..0b77cf974 100644 --- a/app/src/components/flagship/reform/DraftHeader.tsx +++ b/app/src/components/flagship/reform/DraftHeader.tsx @@ -5,6 +5,7 @@ import { useSelector } from 'react-redux'; import { ReformNotFoundError } from '@/api/reformStore'; import { BrowserOnlyIdError } from '@/api/v2Associations'; import { isV2ApiError } from '@/api/v2UserClient'; +import { ErrorState } from '@/components/flagship/states'; import { Button, Popover, @@ -275,7 +276,18 @@ interface DraftRunRequest { runBaseline: RunReportBaseline | undefined; } -export function DraftRunBar({ draft }: { draft: DraftReform }) { +export function DraftRunBar({ + draft, + currentLawUnavailable = false, +}: { + draft: DraftReform; + /** + * The draft's current-law values are still loading, or failed to (on the + * v2 routes). Saving keeps only the values that differ from current law, + * so neither save can run until they are in. + */ + currentLawUnavailable?: boolean; +}) { const nav = useAppNavigate(); const queryClient = useQueryClient(); const runReport = useRunFlagshipReport(); @@ -502,7 +514,7 @@ export function DraftRunBar({ draft }: { draft: DraftReform }) { @@ -525,7 +537,9 @@ export function DraftRunBar({ draft }: { draft: DraftReform }) { starting || saveMutation.isPending || baseline.pending || - baseline.matchesDraft + baseline.failed || + baseline.matchesDraft || + currentLawUnavailable } title={ !empty && adjusted.length === 0 @@ -569,6 +583,13 @@ export function DraftRunBar({ draft }: { draft: DraftReform }) {

)} + {baseline.failed && ( + + )} {(runReport.error || saveError) && (

state.metadata.parameters); - const draftable = useSelector(selectAddableParameterPaths); + // The parameters a draft can take: their groups show as one table row. + const draftable = useV3SearchCatalog().addablePaths; const lastYear = Number(useSelector(getDateRange).maxDate.slice(0, 4)); const focus = useProvisionFocus(); // One row open at a time: the one being worked on. diff --git a/app/src/components/flagship/reform/ParametersStatus.tsx b/app/src/components/flagship/reform/ParametersStatus.tsx index 1c6a30f01..64846a5cc 100644 --- a/app/src/components/flagship/reform/ParametersStatus.tsx +++ b/app/src/components/flagship/reform/ParametersStatus.tsx @@ -109,21 +109,36 @@ function DraftTableSkeleton({ rows }: { rows: number }) { ); } +/** What Build waits on: its loading state, and how to ask again after a failure. */ +export interface ParametersLoader { + status: 'loading' | 'error' | 'ready'; + retry: () => unknown; +} + /** * Build waits on the model's parameters: the add bar has nothing to * search and the table cannot group breakdowns until they arrive. While * they load, placeholders hold the bar's place (and the table's, for a * draft of `paths`, a row per group as the table draws them); if they - * fail, the error says so and asks for them again. + * fail, the error says so and asks for them again. `loader` names what is + * awaited (on the v2 routes, the search file and the draft's parameters); + * without one, the model's metadata. */ -export default function ParametersStatus({ paths = [] }: { paths?: string[] }) { +export default function ParametersStatus({ + paths = [], + loader, +}: { + paths?: string[]; + loader?: ParametersLoader; +}) { const metadata = useFlagshipMetadataStatus(); + const awaited = loader ?? metadata; const rows = new Set(paths.map(groupKeyFromPath)).size; - if (metadata.status === 'error') { + if (awaited.status === 'error') { return (

- +
); } diff --git a/app/src/components/flagship/reform/ProvisionDetails.tsx b/app/src/components/flagship/reform/ProvisionDetails.tsx index b2b5d9a7a..537d4f4fd 100644 --- a/app/src/components/flagship/reform/ProvisionDetails.tsx +++ b/app/src/components/flagship/reform/ProvisionDetails.tsx @@ -12,6 +12,7 @@ import { } from '@/components/ui'; import { FOREVER } from '@/constants'; import { colors, spacing, typography } from '@/designTokens'; +import { useV3SearchCatalog } from '@/hooks/useV3SearchCatalog'; import { currentLawForYear, DraftProvision, @@ -41,7 +42,6 @@ import { setValueFromYear, } from '@/libs/flagship/uprating'; import { getDateRange, getTaxYears } from '@/libs/metadataUtils'; -import { selectParameterEntriesByPath } from '@/libs/parameterSearch'; import { ChangesCard } from '@/pages/reportBuilder/modals/policyCreation/ChangesCard'; import { RootState } from '@/store'; import { ValueInterval, ValueIntervalCollection } from '@/types/subIngredients/valueInterval'; @@ -99,7 +99,7 @@ function fitsGrid(provision: DraftProvision | undefined): boolean { */ export default function ProvisionDetails({ draft, path, group, focusPath }: ProvisionDetailsProps) { const parameters = useSelector((state: RootState) => state.metadata.parameters); - const entriesByPath = useSelector(selectParameterEntriesByPath); + const entriesByPath = useV3SearchCatalog().entriesByPath; const { minDate, maxDate } = useSelector(getDateRange); const taxYears = useSelector(getTaxYears); const year = draftYear(draft); diff --git a/app/src/docs/ARCHITECTURE-COUNTRY-ID.md b/app/src/docs/ARCHITECTURE-COUNTRY-ID.md index fc190c930..e6824ad65 100644 --- a/app/src/docs/ARCHITECTURE-COUNTRY-ID.md +++ b/app/src/docs/ARCHITECTURE-COUNTRY-ID.md @@ -41,9 +41,11 @@ Components read the country from this hook, not from Redux. ## Redux copy and metadata loading -`MetadataBootstrap` in `providers.tsx` copies the country into Redux with `setCurrentCountry(countryId)` and calls `useFetchMetadata(countryId)`. Loading is non-blocking: pages render while metadata loads. +`MetadataBootstrap` in `providers.tsx` copies the country into Redux with `setCurrentCountry(countryId)` and calls `useFetchMetadata(countryId, routes)`. Loading is non-blocking: pages render while metadata loads. -`useFetchMetadata` (`src/hooks/useMetadata.ts`) dispatches `fetchMetadataThunk(countryId)` only when there is no metadata yet or it belongs to another country, so moving between pages of one country does not refetch. Hooks that need metadata for the current country compare `metadata.currentCountry` with the country they were given. +`routes` comes from `metadataRoutesForPage(useSelectedLayoutSegments())` (`src/libs/flagship/v3ApiRoutes.ts`): `'v1'` (`GET /{country}/metadata`, the whole model) for the older report builder, the routes `V3_API_ROUTES` names for the v3 workspace (`'v2'` loads only the small `GET /v2/model-catalog`, and v3 loads parameters on demand), and, once `V3_API_ROUTES` is `'v2'`, `null` (load nothing) for pages that only redirect into v3: the country index, the old Home launcher (`(calculator)/home`, to Build), the old Library and bill tracker (`(calculator)/library` and `(calculator)/tracker`, to Reforms) and the paused Ask page (`(workspace)/ask`, to Build). The page each one lands on loads its own metadata. + +`useFetchMetadata` (`src/hooks/useMetadata.ts`) dispatches `fetchMetadataThunk(metadataRequest(countryId, routes))` only when there is no metadata yet, it belongs to another country, or it came from other routes, so moving between pages of one country does not refetch. Hooks that need metadata for the current country compare `metadata.currentCountry` with the country they were given. ## Implementation checklist @@ -62,4 +64,5 @@ When adding a country-dependent feature: - `src/contexts/CountryContext.tsx`: `CountryProvider` and `useCountryContext` - `src/hooks/useCurrentCountry.ts`: hook that reads the country - `src/hooks/useMetadata.ts`: `useFetchMetadata` +- `src/libs/flagship/v3ApiRoutes.ts`: `V3_API_ROUTES` and which routes a page loads metadata from - `src/reducers/metadataReducer.ts`: metadata state and `setCurrentCountry` diff --git a/app/src/hooks/useFlagshipHousehold.ts b/app/src/hooks/useFlagshipHousehold.ts index d82d928e4..268f72d7d 100644 --- a/app/src/hooks/useFlagshipHousehold.ts +++ b/app/src/hooks/useFlagshipHousehold.ts @@ -2,8 +2,10 @@ import { useQuery } from '@tanstack/react-query'; import { PolicyAdapter } from '@/adapters/PolicyAdapter'; import { fetchPolicyById } from '@/api/policy'; import { useParameterMetadata } from '@/hooks/useParameterMetadata'; +import { useV3Policies } from '@/hooks/useV3Policies'; import { useParallelQueries } from '@/hooks/utils/queryUtils'; import { runFlagshipHouseholdPayload } from '@/libs/flagship/householdRun'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; import { policyKeys } from '@/libs/queryKeys'; import type { V1HouseholdCreateEnvelope } from '@/models/household/v1Types'; import type { Policy } from '@/types/ingredients/Policy'; @@ -44,22 +46,14 @@ export function useFlagshipHouseholdPayload({ }); } -/** - * The report's policies in the app's model, for the v2 household - * breakdowns that send a policy's parameters with their request (earnings - * variation, marginal tax rates). Fetched as the report page fetches them, - * under the same keys, so a policy the page already holds is not fetched - * again. Nothing runs until `enabled`. - */ -export function useFlagshipHouseholdPolicies({ - countryId, - policyIds, - enabled, -}: { +interface HouseholdPoliciesArgs { countryId: string; policyIds: string[]; enabled: boolean; -}) { +} + +/** On the v1 routes: as before v3 loaded parameters on demand. */ +function useV1HouseholdPolicies({ countryId, policyIds, enabled }: HouseholdPoliciesArgs) { const { parameters, isReady } = useParameterMetadata(countryId); const results = useParallelQueries(policyIds, { queryKey: policyKeys.byId, @@ -81,3 +75,34 @@ export function useFlagshipHouseholdPolicies({ retry: () => Promise.all(results.queries.map((query) => query.refetch())), }; } + +/** + * On the v2 routes: read as stored, then parsed once the parameters each + * names are in, as the report page reads them (under the same keys). + */ +function useV2HouseholdPolicies({ countryId, policyIds, enabled }: HouseholdPoliciesArgs) { + const results = useV3Policies(countryId, policyIds, enabled); + const { policies } = results; + const error = + results.failedPolicyIds.length > 0 + ? new Error(`Could not load policy ${results.failedPolicyIds.join(', ')}`) + : null; + return { + policies, + /** Waiting for the model's parameters, or for a policy. */ + isLoading: enabled && !error && policies.length < new Set(policyIds).size, + error, + retry: results.retry, + }; +} + +/** + * The report's policies in the app's model, for the v2 household + * breakdowns that send a policy's parameters with their request (earnings + * variation, marginal tax rates). Fetched as the report page fetches them, + * under the same keys, so a policy the page already holds is not fetched + * again. Nothing runs until `enabled`. The routes v3 uses (V3_API_ROUTES, + * a constant) pick the implementation. + */ +export const useFlagshipHouseholdPolicies = + V3_API_ROUTES === 'v2' ? useV2HouseholdPolicies : useV1HouseholdPolicies; diff --git a/app/src/hooks/useFlagshipMetadataStatus.ts b/app/src/hooks/useFlagshipMetadataStatus.ts index 4c18ee752..b1d74e95e 100644 --- a/app/src/hooks/useFlagshipMetadataStatus.ts +++ b/app/src/hooks/useFlagshipMetadataStatus.ts @@ -1,7 +1,8 @@ import { useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useCurrentCountry } from '@/hooks/useCurrentCountry'; -import { fetchMetadataThunk } from '@/reducers/metadataReducer'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; +import { fetchMetadataThunk, metadataRequest } from '@/reducers/metadataReducer'; import type { AppDispatch, RootState } from '@/store'; export type FlagshipMetadataStatus = 'loading' | 'error' | 'ready'; @@ -22,12 +23,20 @@ export function useFlagshipMetadataStatus(): { const dispatch = useDispatch(); const loading = useSelector((state: RootState) => state.metadata.loading); const error = useSelector((state: RootState) => state.metadata.error); + // Ready only with metadata from the routes v3 uses (see v3ApiRoutes): on + // the v2 routes that is the small model catalog, and v3 loads parameters + // on demand behind it. const ready = useSelector( (state: RootState) => - state.metadata.version !== null && state.metadata.currentCountry === countryId + state.metadata.version !== null && + state.metadata.currentCountry === countryId && + (state.metadata.routes ?? 'v1') === V3_API_ROUTES ); - const retry = useCallback(() => dispatch(fetchMetadataThunk(countryId)), [dispatch, countryId]); + const retry = useCallback( + () => dispatch(fetchMetadataThunk(metadataRequest(countryId, V3_API_ROUTES))), + [dispatch, countryId] + ); const status: FlagshipMetadataStatus = !loading && error ? 'error' : ready && !loading ? 'ready' : 'loading'; diff --git a/app/src/hooks/useFlagshipReport.ts b/app/src/hooks/useFlagshipReport.ts index 6ab5bda02..6f96a7e8c 100644 --- a/app/src/hooks/useFlagshipReport.ts +++ b/app/src/hooks/useFlagshipReport.ts @@ -1,16 +1,15 @@ import { useQuery } from '@tanstack/react-query'; -import { PolicyAdapter, ReportAdapter, SimulationAdapter } from '@/adapters'; -import { fetchPolicyById } from '@/api/policy'; +import { ReportAdapter, SimulationAdapter } from '@/adapters'; import { fetchReportById } from '@/api/report'; import { fetchSimulationById } from '@/api/simulation'; import { useCurrentCountry } from '@/hooks/useCurrentCountry'; -import { useParameterMetadata } from '@/hooks/useParameterMetadata'; +import { useV3Policies } from '@/hooks/useV3Policies'; import { isNotFoundError, retryUnlessNotFound } from '@/libs/flagship/loadError'; import { GC_TIME_5_MIN } from '@/libs/queryConfig'; import { Policy } from '@/types/ingredients/Policy'; import { Report } from '@/types/ingredients/Report'; import { Simulation } from '@/types/ingredients/Simulation'; -import { policyKeys, reportKeys, simulationKeys } from '../libs/queryKeys'; +import { reportKeys, simulationKeys } from '../libs/queryKeys'; import { extractUniqueIds, useParallelQueries } from './utils/queryUtils'; /** @@ -55,8 +54,6 @@ export interface FlagshipReportData { */ export function useFlagshipReport(id: string): FlagshipReportData { const country = useCurrentCountry(); - const { parameters: parameterMetadata, isReady: isParameterMetadataReady } = - useParameterMetadata(country); const direct = isApiReportId(id); const { @@ -87,19 +84,10 @@ export function useFlagshipReport(id: string): FlagshipReportData { .filter((s): s is Simulation => !!s); const policyIds = extractUniqueIds(simulations, 'policyId'); - const policyResults = useParallelQueries(policyIds, { - queryKey: policyKeys.byId, - queryFn: async (policyId) => - PolicyAdapter.fromMetadata(await fetchPolicyById(country, policyId), parameterMetadata), - enabled: direct && isParameterMetadataReady && policyIds.length > 0, - staleTime: 5 * 60 * 1000, - structuralSharing: false, - }); - const policies = policyResults.queries.map((q) => q.data).filter((p): p is Policy => !!p); - const failedPolicyIds = policyIds.filter((_, index) => { - const query = policyResults.queries[index]; - return !!query?.error && !query.data; - }); + // Each policy is read as stored, the parameters it names are loaded (on + // the v2 routes) or already in (v1), and then it is parsed. + const policyResults = useV3Policies(country, policyIds, direct); + const { policies, failedPolicyIds } = policyResults; if (!direct) { return { @@ -133,9 +121,8 @@ export function useFlagshipReport(id: string): FlagshipReportData { retry: () => Promise.all([ reportError ? refetchReport() : null, - ...[...simulationResults.queries, ...policyResults.queries] - .filter((query) => query.error) - .map((query) => query.refetch()), + ...simulationResults.queries.filter((query) => query.error).map((query) => query.refetch()), + policyResults.retry(), ]), }; } diff --git a/app/src/hooks/useFlagshipStores.ts b/app/src/hooks/useFlagshipStores.ts index fecc89b22..f7ce0708f 100644 --- a/app/src/hooks/useFlagshipStores.ts +++ b/app/src/hooks/useFlagshipStores.ts @@ -1,6 +1,6 @@ -import { useMemo, useRef } from 'react'; +import { useMemo } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { useSelector } from 'react-redux'; +import { useStore } from 'react-redux'; import { findReportRecordByLink, getFlagshipReportStore, @@ -8,15 +8,22 @@ import { type FlagshipReportRecord, type FlagshipReportStore, } from '@/api/flagshipReportStore'; -import { fetchV1PolicyJson, getReformStore, type ReformStore } from '@/api/reformStore'; +import { + fetchV1PolicyJson, + fetchV2PolicyItem, + getReformStore, + type ReformStore, +} from '@/api/reformStore'; import { fetchSharedReport } from '@/api/sharedReport'; import { isAccountId, type ApiStoreContext } from '@/api/v2Associations'; import { useCountryContext } from '@/contexts/CountryContext'; import { useCurrentCountry } from '@/hooks/useCurrentCountry'; +import { useLoadV3Parameters } from '@/hooks/useV3Parameters'; import { useAuthStatus } from '@/libs/auth/AuthContext'; import { getBrowserUserId } from '@/libs/auth/browserUser'; import { useV2UserClient } from '@/libs/auth/useV2UserClient'; import { unwrapReport } from '@/libs/flagship/reportSchema'; +import { selectV2Catalog, selectV2ParameterIds } from '@/reducers/metadataReducer'; import type { RootState } from '@/store'; import { isAssociationCountry } from '@/types/v2User'; @@ -65,32 +72,57 @@ export function useV3SavedWork(): V3SavedWork { const v1PolicyJsonKey = (countryId: string, policyId: string) => ['v1-policy-json', countryId, policyId] as const; -/** How long a fetched v1 policy stays cached without a reader. */ -const V1_POLICY_JSON_GC_TIME = 30 * 60 * 1000; +/** A v2 policy (`GET /v2/policies/{id}`), per country. */ +const v2PolicyKey = (countryId: string, policyId: string) => + ['v2-policy', countryId, policyId] as const; + +/** How long a fetched policy (v1 or v2) stays cached without a reader. */ +const SAVED_POLICY_GC_TIME = 30 * 60 * 1000; +/** + * The reform store for this session. Which policies an account's reforms + * point at (v1 or `/v2/policies`) follows V3_API_ROUTES (see + * libs/flagship/v3ApiRoutes); on v2, the catalog IDs a policy names come + * with the parameters loaded on demand. + */ export function useReformStore(): ReformStore { const { account } = useV3SavedWork(); - const parameters = useSelector((state: RootState) => state.metadata?.parameters); - // Read through a ref so metadata arriving later does not rebuild the store. - const parametersRef = useRef(parameters); - parametersRef.current = parameters; + const loadParameters = useLoadV3Parameters(); + const reduxStore = useStore(); const queryClient = useQueryClient(); + // Metadata is read from the store when the store asks for it, so + // metadata arriving later does not rebuild the store. return useMemo( () => getReformStore(account, { - getParameterMetadata: () => parametersRef.current, - // v1 policies never change, so each is fetched once per session + getParameterMetadata: () => reduxStore.getState().metadata?.parameters, + getV2Catalog: () => selectV2Catalog(reduxStore.getState()), + resolveParameterIds: async (_countryId, names) => { + await loadParameters(names); + const ids = selectV2ParameterIds(reduxStore.getState()); + return Object.fromEntries( + names.filter((name) => ids[name]).map((name) => [name, ids[name]]) + ); + }, + // Policies never change, so each is fetched once per session // rather than on every list of the account's reforms. fetchPolicyJson: (countryId, policyId) => queryClient.fetchQuery({ queryKey: v1PolicyJsonKey(countryId, policyId), queryFn: () => fetchV1PolicyJson(countryId, policyId), staleTime: Infinity, - gcTime: V1_POLICY_JSON_GC_TIME, + gcTime: SAVED_POLICY_GC_TIME, + }), + fetchV2Policy: (countryId, policyId) => + queryClient.fetchQuery({ + queryKey: v2PolicyKey(countryId, policyId), + queryFn: () => fetchV2PolicyItem(countryId, policyId), + staleTime: Infinity, + gcTime: SAVED_POLICY_GC_TIME, }), }), - [account, queryClient] + [account, loadParameters, queryClient, reduxStore] ); } diff --git a/app/src/hooks/useMetadata.ts b/app/src/hooks/useMetadata.ts index e6724d212..ec32c1182 100644 --- a/app/src/hooks/useMetadata.ts +++ b/app/src/hooks/useMetadata.ts @@ -1,7 +1,8 @@ import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { fetchMetadataThunk } from '@/reducers/metadataReducer'; +import { fetchMetadataThunk, metadataRequest } from '@/reducers/metadataReducer'; import { AppDispatch, RootState } from '@/store'; +import type { MetadataRoutes } from '@/types/metadata'; /** * Hook that ensures metadata is fetched for the current country. @@ -10,17 +11,25 @@ import { AppDispatch, RootState } from '@/store'; * - Component mounts and no metadata exists * - countryId parameter changes (e.g., when URL route changes) * - Current country in state differs from requested country + * - The metadata in state came from other routes than `routes` (v1's + * whole-model route, or v2's small model catalog; see v3ApiRoutes) + * + * Nothing loads when `routes` is null (a page that only redirects). * * Components should use useSelector to read metadata from Redux state */ -export function useFetchMetadata(countryId: string): void { +export function useFetchMetadata(countryId: string, routes: MetadataRoutes | null = 'v1'): void { const dispatch = useDispatch(); const metadata = useSelector((state: RootState) => state.metadata); + const loadedRoutes = metadata.routes ?? 'v1'; useEffect(() => { - // Only fetch if we don't already have metadata for this country - if (!metadata.version || countryId !== metadata.currentCountry) { - dispatch(fetchMetadataThunk(countryId)); + // Only fetch if we don't already have metadata for this country from these routes + if (routes === null) { + return; + } + if (!metadata.version || countryId !== metadata.currentCountry || loadedRoutes !== routes) { + dispatch(fetchMetadataThunk(metadataRequest(countryId, routes))); } - }, [countryId, metadata.currentCountry, metadata.version, dispatch]); + }, [countryId, routes, loadedRoutes, metadata.currentCountry, metadata.version, dispatch]); } diff --git a/app/src/hooks/useV3Parameters.ts b/app/src/hooks/useV3Parameters.ts new file mode 100644 index 000000000..681f0ce4c --- /dev/null +++ b/app/src/hooks/useV3Parameters.ts @@ -0,0 +1,183 @@ +import { useCallback, useMemo } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useDispatch, useSelector, useStore } from 'react-redux'; +import { fetchCatalogParameters } from '@/api/publicApi/catalogParameters'; +import { useCurrentCountry } from '@/hooks/useCurrentCountry'; +import { useFlagshipMetadataStatus } from '@/hooks/useFlagshipMetadataStatus'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; +import { + catalogParametersLoaded, + selectV2CatalogVersion, + selectV2LoadId, +} from '@/reducers/metadataReducer'; +import type { AppDispatch, RootState } from '@/store'; + +/** + * Parameters with their current-law values, for the v3 views that read + * them from the metadata store (`state.metadata.parameters`). + * + * On the v1 routes the whole metadata holds every parameter, so they are + * ready once it is. On the v2 routes the store starts with none: a view + * names what it shows (a reform's parameters, a report's changes) and + * those load in one request, with the labels of the nodes above them; the + * view shows its loading state until they are in. Names the catalog + * version does not have count as loaded, so nothing asks for them again. + */ +export interface V3ParametersStatus { + status: 'loading' | 'error' | 'ready'; + /** + * Whether the names are being loaded on demand right now: only ever on + * the v2 routes. On v1, 'loading' means the whole metadata is not in + * yet, which views that never waited for it before can ignore. + */ + waiting: boolean; + /** The failure's message, when the status is 'error'. */ + message: string | null; + retry: () => unknown; +} + +/** How long a batch's query stays cached without a reader; its parameters stay in the store. */ +const PARAMETER_QUERY_GC_TIME = 5 * 60 * 1000; + +/** + * A batch's query key. It names the model catalog load as well as the + * version: the parameters live in the store, which a new load empties, so + * a batch loaded under an earlier load is asked for again. + */ +function catalogParametersKey( + countryId: string, + policyengineVersion: string | null, + loadId: string | null, + names: readonly string[] +) { + return ['v2-catalog-parameters', countryId, policyengineVersion, loadId, ...names] as const; +} + +/** Which of `names` the store lacks values for, sorted, each once. */ +function namesToLoad( + parameters: RootState['metadata']['parameters'], + missingNames: readonly string[] | undefined, + names: readonly string[] +): string[] { + const missing = new Set(missingNames ?? []); + return [...new Set(names)] + .filter((name) => parameters[name]?.values === undefined && !missing.has(name)) + .sort(); +} + +async function loadIntoStore( + dispatch: AppDispatch, + countryId: string, + policyengineVersion: string, + loadId: string, + names: readonly string[] +): Promise { + const loaded = await fetchCatalogParameters(countryId, policyengineVersion, names); + dispatch(catalogParametersLoaded({ countryId, policyengineVersion, loadId, ...loaded })); + return true; +} + +function useV1EnsureParameters(_names: readonly string[]): V3ParametersStatus { + const metadata = useFlagshipMetadataStatus(); + return { + status: metadata.status, + waiting: false, + message: metadata.message, + retry: metadata.retry, + }; +} + +function useV2EnsureParameters(names: readonly string[]): V3ParametersStatus { + const countryId = useCurrentCountry(); + const dispatch = useDispatch(); + const metadata = useFlagshipMetadataStatus(); + const policyengineVersion = useSelector(selectV2CatalogVersion); + const loadId = useSelector(selectV2LoadId); + const parameters = useSelector((state: RootState) => state.metadata.parameters); + const missingNames = useSelector((state: RootState) => state.metadata.v2MissingParameterNames); + const toLoad = useMemo( + () => namesToLoad(parameters, missingNames, names), + [names, parameters, missingNames] + ); + const query = useQuery({ + queryKey: catalogParametersKey(countryId, policyengineVersion, loadId, toLoad), + queryFn: () => loadIntoStore(dispatch, countryId, policyengineVersion!, loadId!, toLoad), + enabled: policyengineVersion !== null && loadId !== null && toLoad.length > 0, + staleTime: Infinity, + gcTime: PARAMETER_QUERY_GC_TIME, + }); + + if (toLoad.length === 0) { + return { status: 'ready', waiting: false, message: null, retry: metadata.retry }; + } + if (metadata.status === 'error') { + return { status: 'error', waiting: false, message: metadata.message, retry: metadata.retry }; + } + if (query.isError) { + return { + status: 'error', + waiting: false, + message: query.error instanceof Error ? query.error.message : null, + retry: query.refetch, + }; + } + return { status: 'loading', waiting: true, message: null, retry: query.refetch }; +} + +/** + * Whether the named parameters, with their values, are in the metadata + * store for the routes v3 uses (V3_API_ROUTES, a constant, so every render + * calls the same hooks); on the v2 routes, loads those that are not. + */ +export const useEnsureV3Parameters: (names: readonly string[]) => V3ParametersStatus = + V3_API_ROUTES === 'v2' ? useV2EnsureParameters : useV1EnsureParameters; + +/** Loads names, or answers null when they are all in already (so callers can act at once). */ +export type LoadV3Parameters = (names: readonly string[]) => Promise | null; + +function useV1LoadParameters(): LoadV3Parameters { + return useCallback(() => null, []); +} + +function useV2LoadParameters(): LoadV3Parameters { + const countryId = useCurrentCountry(); + const dispatch = useDispatch(); + const store = useStore(); + const queryClient = useQueryClient(); + return useCallback( + (names: readonly string[]) => { + const state = store.getState(); + const policyengineVersion = selectV2CatalogVersion(state); + const loadId = selectV2LoadId(state); + const toLoad = namesToLoad( + state.metadata.parameters, + state.metadata.v2MissingParameterNames, + names + ); + if (toLoad.length === 0) { + return null; + } + if (policyengineVersion === null || loadId === null) { + return Promise.reject(new Error('The model catalog has not loaded yet.')); + } + return queryClient + .fetchQuery({ + queryKey: catalogParametersKey(countryId, policyengineVersion, loadId, toLoad), + queryFn: () => loadIntoStore(dispatch, countryId, policyengineVersion, loadId, toLoad), + staleTime: Infinity, + gcTime: PARAMETER_QUERY_GC_TIME, + }) + .then(() => undefined); + }, + [countryId, dispatch, store, queryClient] + ); +} + +/** + * A function that loads the named parameters into the store and resolves + * once they are in, for actions such as adding a parameter to a draft. It + * answers null when they are in already, always so on the v1 routes, so + * the action can go ahead at once. + */ +export const useLoadV3Parameters: () => LoadV3Parameters = + V3_API_ROUTES === 'v2' ? useV2LoadParameters : useV1LoadParameters; diff --git a/app/src/hooks/useV3Policies.ts b/app/src/hooks/useV3Policies.ts new file mode 100644 index 000000000..7c6feae50 --- /dev/null +++ b/app/src/hooks/useV3Policies.ts @@ -0,0 +1,145 @@ +import { useMemo } from 'react'; +import { useQueries, type QueryObserverResult } from '@tanstack/react-query'; +import { useSelector } from 'react-redux'; +import { PolicyAdapter } from '@/adapters'; +import { fetchPolicyById } from '@/api/policy'; +import { useParameterMetadata } from '@/hooks/useParameterMetadata'; +import { useEnsureV3Parameters } from '@/hooks/useV3Parameters'; +import { useParallelQueries } from '@/hooks/utils/queryUtils'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; +import { policyKeys } from '@/libs/queryKeys'; +import type { RootState } from '@/store'; +import type { Policy } from '@/types/ingredients/Policy'; +import type { PolicyMetadata } from '@/types/metadata/policyMetadata'; + +/** + * The policies a v3 report shows, in the app's model. A policy is parsed + * against the metadata of the parameters it names (which types its + * values). On the v1 routes each policy is read and parsed once the whole + * metadata is in, as before v3 loaded parameters on demand. On the v2 + * routes each is first read as stored (`GET /{country}/policy/{id}`), then + * the parameters it names load in one request, then it is parsed. A policy + * that cannot be read or parsed, or whose parameters fail to load, is + * listed as failed. + */ +export interface V3Policies { + policies: Policy[]; + /** The policies that failed to load or parse, with nothing to show. */ + failedPolicyIds: string[]; + /** Waiting for a policy, or (on the v2 routes) for the parameters it names. */ + isLoading: boolean; + /** Asks again for whatever failed. */ + retry: () => Promise; +} + +function useV1Policies(countryId: string, policyIds: string[], enabled: boolean): V3Policies { + const { parameters, isReady } = useParameterMetadata(countryId); + const results = useParallelQueries(policyIds, { + queryKey: policyKeys.byId, + queryFn: async (policyId) => + PolicyAdapter.fromMetadata(await fetchPolicyById(countryId, policyId), parameters), + enabled: enabled && isReady && policyIds.length > 0, + staleTime: 5 * 60 * 1000, + structuralSharing: false, + }); + return { + policies: results.queries + .map((query) => query.data) + .filter((policy): policy is Policy => !!policy), + failedPolicyIds: policyIds.filter((_, index) => { + const query = results.queries[index]; + return !!query?.error && !query.data; + }), + isLoading: results.isLoading, + retry: () => + Promise.all(results.queries.filter((query) => query.error).map((query) => query.refetch())), + }; +} + +interface StoredPolicies { + data: (PolicyMetadata | undefined)[]; + /** Read and failed, with nothing to show. */ + failed: boolean[]; + isLoading: boolean; + refetchFailed: () => Promise; +} + +/** + * The stored policies' results, kept by the query library while nothing + * changes (it compares this result deeply), so what is parsed from them + * keeps its identity from render to render. + */ +function combineStoredPolicies(results: QueryObserverResult[]): StoredPolicies { + return { + data: results.map((result) => result.data), + failed: results.map((result) => !!result.error && !result.data), + isLoading: results.some((result) => result.isLoading), + refetchFailed: () => + Promise.all(results.filter((result) => result.error).map((result) => result.refetch())), + }; +} + +function parse(stored: PolicyMetadata, parameters: RootState['metadata']['parameters']) { + try { + return PolicyAdapter.fromMetadata(stored, parameters); + } catch { + return null; + } +} + +function namedParameters(stored: PolicyMetadata | undefined): string[] { + return stored ? Object.keys(stored.policy_json ?? {}) : []; +} + +function useV2Policies(countryId: string, policyIds: string[], enabled: boolean): V3Policies { + const uniqueIds = [...new Set(policyIds)]; + const stored = useQueries({ + queries: uniqueIds.map((policyId) => ({ + queryKey: policyKeys.storedById(policyId), + queryFn: () => fetchPolicyById(countryId, policyId), + enabled, + staleTime: 5 * 60 * 1000, + })), + combine: combineStoredPolicies, + }); + const names = [...new Set(stored.data.flatMap(namedParameters))]; + const parameterStatus = useEnsureV3Parameters(names); + const parameters = useSelector((state: RootState) => state.metadata.parameters); + const ready = parameterStatus.status === 'ready'; + const parametersFailed = parameterStatus.status === 'error'; + + // Undefined: not parsed yet; null: failed. A policy that names no + // parameter (current law) needs none of them loaded. + const parsed = useMemo( + () => + stored.data.map((policy) => + policy && (ready || namedParameters(policy).length === 0) + ? parse(policy, parameters) + : undefined + ), + [stored.data, ready, parameters] + ); + const policies = useMemo(() => parsed.filter((policy): policy is Policy => !!policy), [parsed]); + + const failedPolicyIds = uniqueIds.filter( + (_, index) => + stored.failed[index] || + parsed[index] === null || + (parametersFailed && !!stored.data[index] && parsed[index] === undefined) + ); + const waitingForParameters = parameterStatus.waiting && stored.data.some(Boolean); + return { + policies, + failedPolicyIds, + isLoading: enabled && (stored.isLoading || waitingForParameters), + retry: () => + Promise.all([stored.refetchFailed(), parametersFailed ? parameterStatus.retry() : null]), + }; +} + +/** The report's policies for the routes v3 uses (V3_API_ROUTES, a constant). */ +export const useV3Policies: ( + countryId: string, + policyIds: string[], + enabled: boolean +) => V3Policies = V3_API_ROUTES === 'v2' ? useV2Policies : useV1Policies; diff --git a/app/src/hooks/useV3SearchCatalog.ts b/app/src/hooks/useV3SearchCatalog.ts new file mode 100644 index 000000000..83a39d966 --- /dev/null +++ b/app/src/hooks/useV3SearchCatalog.ts @@ -0,0 +1,179 @@ +import { useCallback, useEffect, useMemo } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { useSelector } from 'react-redux'; +import { fetchParameterSearchFile } from '@/api/publicApi/searchFile'; +import { useCurrentCountry } from '@/hooks/useCurrentCountry'; +import { useFlagshipMetadataStatus } from '@/hooks/useFlagshipMetadataStatus'; +import { buildV2SearchCatalog } from '@/libs/flagship/v2ParameterSearch'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; +import { + createParameterSearchIndex, + selectAddableParameterPaths, + selectConceptClusters, + selectParameterEntriesByPath, + selectParameterSearchEntries, + selectParameterSearchIndex, + type ParameterSearchEntry, + type ParameterSearchIndex, +} from '@/libs/parameterSearch'; +import { selectV2CatalogVersion } from '@/reducers/metadataReducer'; +import type { RootState } from '@/store'; + +/** + * What v3's parameter search, add bar and draft editor read about which + * parameters a reform can take: search entries and their index, the + * addable paths, concept clusters, and each path's label. + * + * On the v1 routes these come from the whole metadata (as before). On the + * v2 routes they come from the catalog version's search file, which loads + * in the background once the small model catalog is in; until it arrives + * the status is 'loading' and nothing is offered. + */ +export interface V3SearchCatalog { + status: 'loading' | 'error' | 'ready'; + /** The failure's message, when the status is 'error'. */ + message: string | null; + retry: () => unknown; + entries: ParameterSearchEntry[]; + index: ParameterSearchIndex; + entriesByPath: Map; + addablePaths: Set; + clusters: string[][]; + /** A parameter's or node's own label, or null when it has none. */ + labelFor: (path: string) => string | null; +} + +const NO_ENTRIES: ParameterSearchEntry[] = []; +const NO_INDEX: ParameterSearchIndex = createParameterSearchIndex([]); +const NO_ENTRIES_BY_PATH = new Map(); +const NO_PATHS = new Set(); +const NO_CLUSTERS: string[][] = []; + +/** The query key of a catalog version's search file. */ +export function v2SearchFileKey(countryId: string, policyengineVersion: string | null | undefined) { + return ['v2-parameter-search-file', countryId, policyengineVersion] as const; +} + +/** A catalog version's file never changes, so it is fetched once per session. */ +const SEARCH_FILE_QUERY_OPTIONS = { staleTime: Infinity, gcTime: Infinity } as const; + +function useV1SearchCatalog(): V3SearchCatalog { + const metadata = useFlagshipMetadataStatus(); + const entries = useSelector(selectParameterSearchEntries); + const index = useSelector(selectParameterSearchIndex); + const entriesByPath = useSelector(selectParameterEntriesByPath); + const addablePaths = useSelector(selectAddableParameterPaths); + const clusters = useSelector(selectConceptClusters); + const parameters = useSelector((state: RootState) => state.metadata.parameters); + const labelFor = useCallback((path: string) => parameters?.[path]?.label ?? null, [parameters]); + const status = entries.length > 0 ? 'ready' : metadata.status === 'error' ? 'error' : 'loading'; + return { + status, + message: status === 'error' ? metadata.message : null, + retry: metadata.retry, + entries, + index, + entriesByPath, + addablePaths, + clusters, + labelFor, + }; +} + +function useV2SearchCatalog(): V3SearchCatalog { + const countryId = useCurrentCountry(); + const metadata = useFlagshipMetadataStatus(); + const policyengineVersion = useSelector(selectV2CatalogVersion); + const query = useQuery({ + queryKey: v2SearchFileKey(countryId, policyengineVersion), + queryFn: () => fetchParameterSearchFile(countryId, policyengineVersion!), + enabled: policyengineVersion !== null, + ...SEARCH_FILE_QUERY_OPTIONS, + }); + const catalog = useMemo( + () => (query.data ? buildV2SearchCatalog(query.data) : null), + [query.data] + ); + const labelFor = useCallback( + (path: string) => catalog?.collection[path]?.label ?? null, + [catalog] + ); + + if (metadata.status === 'error') { + return emptyCatalog('error', metadata.message, metadata.retry, labelFor); + } + if (query.isError) { + return emptyCatalog( + 'error', + query.error instanceof Error ? query.error.message : null, + query.refetch, + labelFor + ); + } + if (!catalog) { + return emptyCatalog('loading', null, query.refetch, labelFor); + } + return { + status: 'ready', + message: null, + retry: query.refetch, + entries: catalog.entries, + index: catalog.index, + entriesByPath: catalog.entriesByPath, + addablePaths: catalog.addablePaths, + clusters: catalog.clusters, + labelFor, + }; +} + +function emptyCatalog( + status: 'loading' | 'error', + message: string | null, + retry: () => unknown, + labelFor: (path: string) => string | null +): V3SearchCatalog { + return { + status, + message, + retry, + entries: NO_ENTRIES, + index: NO_INDEX, + entriesByPath: NO_ENTRIES_BY_PATH, + addablePaths: NO_PATHS, + clusters: NO_CLUSTERS, + labelFor, + }; +} + +/** + * The search catalog for the routes v3 uses (V3_API_ROUTES, a constant, so + * every render calls the same hooks). + */ +export const useV3SearchCatalog: () => V3SearchCatalog = + V3_API_ROUTES === 'v2' ? useV2SearchCatalog : useV1SearchCatalog; + +/** + * Starts the search file's download once a v3 page has rendered, so it is + * in by the time someone searches. Nothing on the v1 routes, where the + * whole metadata already holds what search reads. + */ +function useV2SearchFileInBackground(): void { + const countryId = useCurrentCountry(); + const policyengineVersion = useSelector(selectV2CatalogVersion); + const queryClient = useQueryClient(); + useEffect(() => { + if (policyengineVersion === null) { + return; + } + void queryClient.prefetchQuery({ + queryKey: v2SearchFileKey(countryId, policyengineVersion), + queryFn: () => fetchParameterSearchFile(countryId, policyengineVersion), + ...SEARCH_FILE_QUERY_OPTIONS, + }); + }, [countryId, policyengineVersion, queryClient]); +} + +function noBackgroundLoad(): void {} + +export const useV3SearchFileInBackground: () => void = + V3_API_ROUTES === 'v2' ? useV2SearchFileInBackground : noBackgroundLoad; diff --git a/app/src/libs/flagship/policyTreeIndex.ts b/app/src/libs/flagship/policyTreeIndex.ts index eab07c8d8..27704eef4 100644 --- a/app/src/libs/flagship/policyTreeIndex.ts +++ b/app/src/libs/flagship/policyTreeIndex.ts @@ -88,3 +88,22 @@ export function folderTrail(index: PolicyTreeIndex, path: string): ParameterTree } return trail; } + +/** + * How many of `addablePaths` sit at or below each folder, by path, as + * buildPolicyTreeIndex counts the whole tree: a parameter counts for every + * folder its path runs through (`a.b[0].c` counts for `a`, `a.b` and + * `a.b[0]`). A folder missing from the map has nothing a draft can add. + */ +export function addableCountsByFolder(addablePaths: Iterable): Map { + const counts = new Map(); + for (const path of addablePaths) { + for (let index = 1; index < path.length; index += 1) { + if (path[index] === '.' || path[index] === '[') { + const folder = path.slice(0, index); + counts.set(folder, (counts.get(folder) ?? 0) + 1); + } + } + } + return counts; +} diff --git a/app/src/libs/flagship/reportRerun.ts b/app/src/libs/flagship/reportRerun.ts index f5615a869..327485b67 100644 --- a/app/src/libs/flagship/reportRerun.ts +++ b/app/src/libs/flagship/reportRerun.ts @@ -90,13 +90,15 @@ function savedBaseline( : undefined; } const reform = savedReform?.(report.baselineReformId); + const label = reform?.label || 'Untitled reform'; + // The saved reform's own v1 policy; one saved only as a v2 policy has + // none, so the run makes one from its parameters, as Build's runs do. const policyId = baselinePolicyId ?? (Number(reform?.policyId) || undefined); - if (!policyId) { - throw new Error('The report’s comparison could not be read'); + if (policyId) { + return { reformId: report.baselineReformId, label, policyId }; } - return { - reformId: report.baselineReformId, - label: reform?.label || 'Untitled reform', - policyId, - }; + if (reform && !reform.parametersUnavailable && reform.parameters.length > 0) { + return { reformId: report.baselineReformId, label, parameters: reform.parameters }; + } + throw new Error('The report’s comparison could not be read'); } diff --git a/app/src/libs/flagship/uprating.ts b/app/src/libs/flagship/uprating.ts index d54b4bb80..d107517af 100644 --- a/app/src/libs/flagship/uprating.ts +++ b/app/src/libs/flagship/uprating.ts @@ -107,6 +107,11 @@ export function growthOf( return provision?.growth ?? 'fixed'; } +/** The parameter paths of the indexes a value can grow with in this country. */ +export function growthIndexPaths(countryId: string): string[] { + return (INDEXES[countryId] ?? []).map((index) => index.path); +} + export function availableIndexes( countryId: string, parameters: ParameterMetadataCollection diff --git a/app/src/libs/flagship/v2ParameterSearch.ts b/app/src/libs/flagship/v2ParameterSearch.ts new file mode 100644 index 000000000..56638fc07 --- /dev/null +++ b/app/src/libs/flagship/v2ParameterSearch.ts @@ -0,0 +1,165 @@ +import type { ParameterSearchFile, ParameterSearchFileParameter } from '@/api/publicApi/searchFile'; +import { + buildConceptAliases, + buildConceptClusters, + buildParameterSearchEntries, + buildStateNames, + createParameterSearchIndex, + type ParameterSearchEntry, + type ParameterSearchIndex, +} from '@/libs/parameterSearch'; +import type { ParameterMetadataCollection } from '@/types/metadata/parameterMetadata'; + +/** + * v3's parameter search on the v2 routes: the search file of the catalog + * version, turned into the collection v3's existing search builders read + * (`libs/parameterSearch`, unchanged), so the same entries, clusters, + * aliases and index come out as from v1's whole metadata. + * + * The collection holds labels, units and descriptions only: values are not + * in the file and load on demand. Which parameters a draft can take comes + * from the file's `editable` and `editable_changes`, for a given date. + */ + +/** + * v1's label for a parameter or node without one: "bracket N" for a + * bracket, otherwise the last part of its path with spaces for underscores. + */ +export function v1FallbackLabel(path: string): string { + const bracket = /\[(\d+)\]$/.exec(path); + if (bracket) { + return `bracket ${Number(bracket[1]) + 1}`; + } + return (path.split('.').pop() ?? path).replaceAll('_', ' '); +} + +/** Every node above `path`: `a.b[0].c` has `a`, `a.b`, `a.b[0]`. */ +function ancestorPaths(path: string): string[] { + const ancestors: string[] = []; + for (let index = 1; index < path.length; index += 1) { + if (path[index] === '.' || path[index] === '[') { + ancestors.push(path.slice(0, index)); + } + } + return ancestors; +} + +/** + * The search file as a parameter collection: each parameter a reform can + * change (economy and household set, as the file lists only those), each + * parameter a reform cannot change (flags off, so search leaves it out but + * its label still feeds concept clusters and aliases), every labelled node, + * and every unlabelled node above a parameter with v1's fallback label, as + * v1's metadata listed every node. + */ +export function searchCollectionFromFile(file: ParameterSearchFile): ParameterMetadataCollection { + const collection: ParameterMetadataCollection = {}; + for (const node of file.nodes) { + collection[node.path] = { type: 'parameterNode', parameter: node.path, label: node.label }; + } + for (const parameter of file.parameters) { + collection[parameter.path] = { + type: 'parameter', + parameter: parameter.path, + label: parameter.label ?? v1FallbackLabel(parameter.path), + unit: parameter.unit, + description: parameter.description, + economy: true, + household: true, + }; + } + for (const parameter of file.other_parameters) { + collection[parameter.path] = { + type: 'parameter', + parameter: parameter.path, + label: parameter.label ?? v1FallbackLabel(parameter.path), + economy: false, + household: false, + }; + } + for (const path of Object.keys(collection)) { + for (const ancestor of ancestorPaths(path)) { + if (!collection[ancestor]) { + collection[ancestor] = { + type: 'parameterNode', + parameter: ancestor, + label: v1FallbackLabel(ancestor), + }; + } + } + } + return collection; +} + +/** Whether a draft can edit the parameter's current-law value on `date` (YYYY-MM-DD). */ +export function editableOn(parameter: ParameterSearchFileParameter, date: string): boolean { + let editable = parameter.editable; + const changes = [...(parameter.editable_changes ?? [])].sort((first, second) => + first.start_date.localeCompare(second.start_date) + ); + for (const change of changes) { + if (change.start_date.slice(0, 10) <= date) { + editable = change.editable; + } + } + return editable; +} + +/** What v3's search and draft editor read, built from one search file for one date. */ +export interface V2SearchCatalog { + /** Labels, units and descriptions of every parameter and node in the file. */ + collection: ParameterMetadataCollection; + /** The parameters a draft can take on the date, as search entries. */ + entries: ParameterSearchEntry[]; + index: ParameterSearchIndex; + entriesByPath: Map; + addablePaths: Set; + clusters: string[][]; +} + +const catalogsByFile = new WeakMap>(); + +/** Today, as the date part of an ISO timestamp. */ +export function todayIsoDate(): string { + return new Date().toISOString().slice(0, 10); +} + +/** + * The search catalog for a file on a date, built once per file and date + * (the file is large, so every reader shares one build). + */ +export function buildV2SearchCatalog( + file: ParameterSearchFile, + date: string = todayIsoDate() +): V2SearchCatalog { + const byDate = catalogsByFile.get(file) ?? new Map(); + catalogsByFile.set(file, byDate); + const built = byDate.get(date); + if (built) { + return built; + } + + const collection = searchCollectionFromFile(file); + const editable = new Set( + file.parameters.filter((parameter) => editableOn(parameter, date)).map((entry) => entry.path) + ); + const entries = buildParameterSearchEntries(collection).filter((entry) => + editable.has(entry.path) + ); + const clusters = buildConceptClusters(collection); + const catalog: V2SearchCatalog = { + collection, + entries, + index: createParameterSearchIndex( + entries, + clusters, + buildConceptAliases(collection), + buildStateNames(collection) + ), + entriesByPath: new Map(entries.map((entry) => [entry.path, entry])), + addablePaths: new Set(entries.map((entry) => entry.path)), + clusters, + }; + byDate.set(date, catalog); + return catalog; +} diff --git a/app/src/libs/flagship/v3ApiRoutes.ts b/app/src/libs/flagship/v3ApiRoutes.ts new file mode 100644 index 000000000..dfaaae401 --- /dev/null +++ b/app/src/libs/flagship/v3ApiRoutes.ts @@ -0,0 +1,74 @@ +import type { MetadataRoutes } from '@/types/metadata'; + +/** + * Which Public API routes the v3 workspace uses. + * + * - 'v1': `GET /{country}/metadata` for the whole model at start, and saved + * reforms as `/v2/user-policies` rows pointing at v1 policies + * (`POST /{country}/policy`, `legacy_policy_id`). + * - 'v2': only the small `/v2/model-catalog` at start; parameters, the + * parameter tree and search load on demand from the v2 catalog routes; + * saved reforms are `/v2/user-policies` rows pointing at `/v2/policies` + * (`policy_id`). + * + * This constant is the only place v3 chooses. It is not a runtime flag or an + * environment variable, so every build behaves the same. It stays 'v1' until + * the switch pull request (plan item 4.01), which changes this line to 'v2' + * (and the test that pins it). The older report builder, the `(calculator)` + * route group, stays on v1 regardless. + */ +export type V3ApiRoutes = MetadataRoutes; + +export const V3_API_ROUTES: V3ApiRoutes = 'v1'; + +/** The Next.js route group holding the v3 workspace pages. */ +export const V3_WORKSPACE_ROUTE_GROUP = '(workspace)'; + +/** + * Where model metadata loads from for a page in this route group (the + * segment below `[countryId]`): v3 follows `v3Routes` (V3_API_ROUTES unless + * a test names the routes after the switch); everything else stays on v1. + */ +export function metadataRoutesForRouteGroup( + routeGroup: string | null, + v3Routes: V3ApiRoutes = V3_API_ROUTES +): V3ApiRoutes { + return routeGroup === V3_WORKSPACE_ROUTE_GROUP ? v3Routes : 'v1'; +} + +/** + * Pages that only send the browser on into v3, by their segments below + * `[countryId]`: the country's index (to Build, or to the older report + * builder when the v3 shell is off, which loads its own metadata), the old + * Home launcher (to Build), the old Library and bill tracker (to Reforms), + * and the paused Ask page (to Build). A page that redirects elsewhere, such + * as `(calculator)/reports/create/[userReportId]` into the older report + * builder, is not one of them. + */ +const REDIRECT_ONLY_PAGES: readonly (readonly string[])[] = [ + [], + ['(calculator)', 'home'], + ['(calculator)', 'library'], + ['(calculator)', 'tracker'], + [V3_WORKSPACE_ROUTE_GROUP, 'ask'], +]; + +/** + * Where model metadata loads from for a page, by its segments below + * `[countryId]` (`useSelectedLayoutSegments`). After the switch, a page + * that only redirects into v3 loads nothing (its destination loads its + * own), so it does not start v1's large download; before the switch it + * loads v1 as it always has. Every other page follows its route group. + */ +export function metadataRoutesForPage( + segments: readonly string[], + v3Routes: V3ApiRoutes = V3_API_ROUTES +): MetadataRoutes | null { + const redirectOnly = REDIRECT_ONLY_PAGES.some( + (page) => page.length === segments.length && page.every((segment, i) => segments[i] === segment) + ); + if (redirectOnly && v3Routes === 'v2') { + return null; + } + return metadataRoutesForRouteGroup(segments[0] ?? null, v3Routes); +} diff --git a/app/src/libs/flagship/v3ParameterNames.ts b/app/src/libs/flagship/v3ParameterNames.ts new file mode 100644 index 000000000..dca1b708f --- /dev/null +++ b/app/src/libs/flagship/v3ParameterNames.ts @@ -0,0 +1,62 @@ +import { groupKeyFromPath } from '@/libs/flagship/parameterGroups'; +import { growthIndexPaths } from '@/libs/flagship/uprating'; +import type { Reform } from '@/types/ingredients/Reform'; + +/** + * Which parameters a v3 view needs from the metadata store, so that on the + * v2 routes, where the store holds only what has been loaded, the view can + * load them in one request before it shows (see useEnsureV3Parameters). + */ + +const pathsByGroupKey = new WeakMap, Map>(); + +function groupIndex(addablePaths: Set): Map { + let index = pathsByGroupKey.get(addablePaths); + if (!index) { + index = new Map(); + for (const path of addablePaths) { + const key = groupKeyFromPath(path); + index.set(key, [...(index.get(key) ?? []), path]); + } + pathsByGroupKey.set(addablePaths, index); + } + return index; +} + +/** + * A parameter and the members of the group the draft editor shows it in + * (a breakdown's other members, a schedule's other brackets), among the + * parameters a draft can take. + */ +export function groupMemberPaths(path: string, addablePaths: Set): string[] { + return [...new Set([path, ...(groupIndex(addablePaths).get(groupKeyFromPath(path)) ?? [])])]; +} + +/** + * What the draft editor reads for a draft: each provision, its group's + * members, the index a provision grows with, and the indexes a value can + * be set to grow with. + */ +export function draftParameterNames( + provisions: ReadonlyArray<{ path: string; growth?: string }>, + addablePaths: Set, + countryId: string +): string[] { + const names = new Set(growthIndexPaths(countryId)); + for (const provision of provisions) { + for (const path of groupMemberPaths(provision.path, addablePaths)) { + names.add(path); + } + if (provision.growth && provision.growth !== 'fixed' && provision.growth !== 'current_law') { + names.add(provision.growth); + } + } + return [...names].sort(); +} + +/** Every parameter the reforms change. */ +export function reformParameterNames(reforms: ReadonlyArray>): string[] { + return [ + ...new Set(reforms.flatMap((reform) => reform.parameters.map((parameter) => parameter.name))), + ].sort(); +} diff --git a/app/src/libs/queryKeys.ts b/app/src/libs/queryKeys.ts index 5f6260fff..ba14d3e7e 100644 --- a/app/src/libs/queryKeys.ts +++ b/app/src/libs/queryKeys.ts @@ -69,6 +69,8 @@ export const policyKeys = { all: ['policies'] as const, byId: (policyId: string) => [...policyKeys.all, 'policy_id', policyId] as const, byUser: (userId: string) => [...policyKeys.all, 'user_id', userId] as const, + /** A v1 policy as stored (`GET /{country}/policy/{id}`), before it is parsed. */ + storedById: (policyId: string) => [...policyKeys.all, 'stored', policyId] as const, }; export const householdKeys = { diff --git a/app/src/pages/flagship/Ask.page.tsx b/app/src/pages/flagship/Ask.page.tsx index a4a85a06c..17c7662f2 100644 --- a/app/src/pages/flagship/Ask.page.tsx +++ b/app/src/pages/flagship/Ask.page.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { IconAdjustments, IconChevronRight, @@ -7,7 +7,7 @@ import { IconPlus, } from '@tabler/icons-react'; import ReactMarkdown from 'react-markdown'; -import { useSelector } from 'react-redux'; +import { useSelector, useStore } from 'react-redux'; import remarkGfm from 'remark-gfm'; import { askChatEndpoint, @@ -16,22 +16,22 @@ import { toolActivityLabel, } from '@/api/askChat'; import ChatComposer from '@/components/flagship/ChatComposer'; +import { ErrorState } from '@/components/flagship/states'; import WorkspaceLayout from '@/components/flagship/WorkspaceLayout'; import { Stack, Text, Title } from '@/components/ui'; import { useAppNavigate } from '@/contexts/NavigationContext'; import { colors, spacing, typography } from '@/designTokens'; import { useCurrentCountry } from '@/hooks/useCurrentCountry'; +import { useEnsureV3Parameters, useLoadV3Parameters } from '@/hooks/useV3Parameters'; +import { useV3SearchCatalog } from '@/hooks/useV3SearchCatalog'; import { addDraftProvision, provisionFromSearchEntry, useDraftReform } from '@/libs/draftReform'; import { ChatReformBridge, provisionsFromChatReform, reformFromToolInput, } from '@/libs/flagship/chatDraftBridge'; -import { - ParameterSearchEntry, - searchParameters, - selectParameterSearchIndex, -} from '@/libs/parameterSearch'; +import { V3_API_ROUTES } from '@/libs/flagship/v3ApiRoutes'; +import { ParameterSearchEntry, searchParameters } from '@/libs/parameterSearch'; import { RootState } from '@/store'; import { formatValue, getCurrentValue, parseValueFromQuestion } from '@/utils/parameterValues'; @@ -58,6 +58,11 @@ interface ChatTurnState { suggestions: string[]; /** Provisions extracted from the model's validated reform, if any */ bridge: ChatReformBridge | null; + /** + * The validated reform while its current law loads (on the v2 routes), + * or after that load failed; null once its provisions are in `bridge`. + */ + bridgeLoad: { status: 'loading' | 'error'; reform: Record } | null; } interface AskTurn { @@ -164,8 +169,22 @@ export default function AskPage() { const nav = useAppNavigate(); const countryId = useCurrentCountry(); const draft = useDraftReform(); - const index = useSelector(selectParameterSearchIndex); + // From the whole metadata on the v1 routes, the search file on v2. + const catalog = useV3SearchCatalog(); + const index = catalog.index; const parameters = useSelector((state: RootState) => state.metadata.parameters); + const store = useStore(); + const loadParameters = useLoadV3Parameters(); + // On the v2 routes the search file loads in the background: until it is + // in, nothing can match, so the page says so instead of answering empty. + const searchLoading = V3_API_ROUTES === 'v2' && catalog.status !== 'ready'; + // Current law for the matches shown, which on the v2 routes load once listed. + const shownMatchPaths = useMemo( + () => turns.flatMap((turn) => (turn.matches ?? []).map((entry) => entry.path)), + [turns] + ); + const matchParameters = useEnsureV3Parameters(shownMatchPaths); + const [pickError, setPickError] = useState(null); const examples = EXAMPLES[countryId] ?? EXAMPLES.default; const draftPaths = new Set(draft?.provisions.map((p) => p.path) ?? []); @@ -188,10 +207,53 @@ export default function AskPage() { }); }; + /** Applies a patch to one turn's chat state. */ + const patchChatAt = (turnIndex: number) => (patch: (chat: ChatTurnState) => ChatTurnState) => + setTurns((prev) => + prev.map((turn, index) => + index === turnIndex && turn.chat ? { ...turn, chat: patch(turn.chat) } : turn + ) + ); + + /** + * Turns the chat's validated reform into draft provisions. Their current + * law comes with the reform's parameters, which on the v2 routes load + * first; a failed load leaves no provisions and says so, with a retry. + */ + const bridgeChatReform = ( + reform: Record, + patchChat: (patch: (chat: ChatTurnState) => ChatTurnState) => void + ): Promise | undefined => { + const bridgeReform = () => { + const bridge = provisionsFromChatReform( + reform, + index.entries, + store.getState().metadata.parameters + ); + patchChat((chat) => ({ + ...chat, + bridge: bridge.provisions.length > 0 ? bridge : chat.bridge, + bridgeLoad: null, + })); + }; + const pending = loadParameters(Object.keys(reform)); + if (!pending) { + bridgeReform(); + return undefined; + } + patchChat((chat) => ({ ...chat, bridgeLoad: { status: 'loading', reform } })); + return pending.then(bridgeReform, () => + patchChat((chat) => ({ ...chat, bridgeLoad: { status: 'error', reform } })) + ); + }; + const sendChatTurn = (question: string) => { abortRef.current?.abort(); const controller = new AbortController(); abortRef.current = controller; + // The turn this question starts: work that finishes after another + // question (the reform's draft step) still belongs to it. + const turnIndex = turns.length; const history: AskChatMessage[] = turns.flatMap((turn) => { const messages: AskChatMessage[] = [{ role: 'user', content: turn.question }]; @@ -220,7 +282,14 @@ export default function AskPage() { { question, matches: [], - chat: { answer: '', status: 'streaming', activity: null, suggestions: [], bridge: null }, + chat: { + answer: '', + status: 'streaming', + activity: null, + suggestions: [], + bridge: null, + bridgeLoad: null, + }, }, ]); @@ -236,12 +305,8 @@ export default function AskPage() { patchLastChat((chat) => ({ ...chat, activity: toolActivityLabel(toolName) })), onToolUse: ({ toolName, toolInput }) => { const reform = reformFromToolInput(toolName, toolInput); - if (!reform) { - return; - } - const bridge = provisionsFromChatReform(reform, index.entries, parameters); - if (bridge.provisions.length > 0) { - patchLastChat((chat) => ({ ...chat, bridge })); + if (reform) { + void bridgeChatReform(reform, patchChatAt(turnIndex)); } }, onSuggestions: (suggestions) => patchLastChat((chat) => ({ ...chat, suggestions })), @@ -287,7 +352,8 @@ export default function AskPage() { const send = (raw?: string) => { const question = (raw ?? input).trim(); - if (!question) { + // Nothing can match until the search file is in; the question waits in the box. + if (!question || searchLoading) { return; } setInput(''); @@ -305,19 +371,42 @@ export default function AskPage() { }; const addMatch = (entry: ParameterSearchEntry, question: string) => { - const provision = provisionFromSearchEntry(entry, parameters?.[entry.path]?.values); - const proposed = parseValueFromQuestion(question, entry.unit); - addDraftProvision( - countryId, - proposed === undefined ? provision : { ...provision, value: proposed }, - 'chat', - 'ask-keyword-v0' - ); + const add = () => { + setPickError(null); + const provision = provisionFromSearchEntry( + entry, + store.getState().metadata.parameters?.[entry.path]?.values + ); + const proposed = parseValueFromQuestion(question, entry.unit); + addDraftProvision( + countryId, + proposed === undefined ? provision : { ...provision, value: proposed }, + 'chat', + 'ask-keyword-v0' + ); + }; + // A match joins the draft at current law, which on the v2 routes loads first. + const pending = loadParameters([entry.path]); + if (pending) { + pending.then(add, () => setPickError('Could not load that parameter. Try picking it again.')); + } else { + add(); + } }; const renderMatchRow = (entry: ParameterSearchEntry, question: string) => { const inDraft = draftPaths.has(entry.path); - const baseline = getCurrentValue(parameters?.[entry.path]?.values); + const values = parameters?.[entry.path]?.values; + const baseline = getCurrentValue(values); + // On the v2 routes a listed match's current law loads after it shows. + const currentLaw = + values !== undefined + ? formatValue(baseline, entry.unit) + : matchParameters.waiting + ? 'loading…' + : matchParameters.status === 'error' + ? 'could not load' + : formatValue(baseline, entry.unit); return (