Files
Investidor-V2/tools/territorial-openapi.mjs
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

240 lines
6.9 KiB
JavaScript

import fs from 'node:fs';
const id = {
name: 'id',
in: 'path',
required: true,
schema: { type: 'string', format: 'uuid' },
};
const string = { type: 'string' };
const schema = (properties) => ({ type: 'object', properties });
const paths = {};
const operation = (summary, parameters = [], body = null) => ({
summary,
security: [{ laravelSession: [] }],
parameters: [
...parameters,
...(body
? [
{
name: 'Idempotency-Key',
in: 'header',
required: true,
schema: { type: 'string', format: 'uuid' },
},
]
: []),
],
...(body
? {
requestBody: {
required: true,
content: { 'application/json': { schema: body } },
},
}
: {}),
responses: Object.fromEntries(
[
200,
...(summary.startsWith('Create source') ? [201] : []),
401,
403,
404,
409,
422,
429,
].map((code) => [
code,
{
description:
code < 300
? 'JSON result with X-Correlation-ID; private, no-store'
: 'Request rejected; validation and workflow errors use Problem Details within territorial middleware',
},
]),
),
});
for (const collection of [
'countries',
'provinces',
'districts',
'admin-posts',
'localities',
'search',
])
paths['/' + collection] = {
get: operation(
'Read currently published territories: ' + collection,
[
'q',
'cursor',
'parent_id',
'country_id',
'province_id',
'district_id',
'admin_post_id',
]
.map((name) => ({ name, in: 'query', schema: string }))
.concat([
{
name: 'limit',
in: 'query',
schema: { type: 'integer', minimum: 1, maximum: 100 },
},
]),
),
};
paths['/territories/{type}/{id}'] = {
get: operation('Read published territory and translated names', [
id,
{
name: 'type',
in: 'path',
required: true,
schema: {
enum: [
'country',
'province',
'district',
'admin_post',
'locality',
],
},
},
]),
};
const territory = schema({
name: string,
code: string,
location_type: {
enum: ['country', 'province', 'district', 'admin_post', 'locality'],
},
parent_id: { type: ['string', 'null'], format: 'uuid' },
source_id: { type: 'string', format: 'uuid' },
valid_from: { type: 'string', format: 'date' },
valid_to: { type: ['string', 'null'], format: 'date' },
reason: { type: 'string', minLength: 10, maxLength: 1000 },
});
territory.required = [
'name',
'code',
'location_type',
'source_id',
'valid_from',
'reason',
];
paths['/territories'] = {
post: operation(
'Create private territorial draft; designated editor with MFA',
[],
territory,
),
};
paths['/territories/{id}'] = {
patch: operation(
'Replace draft attributes and invalidate editorial approvals',
[id],
{
...territory,
required: [...territory.required, 'version_no'],
properties: {
...territory.properties,
version_no: { type: 'integer', minimum: 1 },
},
},
),
};
paths['/territories/{id}/transitions'] = {
post: operation(
'Submit, review, publish, reject or archive; independent actors required',
[id],
{
...schema({
action: {
enum: [
'pending_review',
'approved',
'published',
'rejected',
'archived',
],
},
version_no: { type: 'integer', minimum: 1 },
reason: { type: 'string', minLength: 10 },
}),
required: ['action', 'version_no', 'reason'],
},
),
};
paths['/sources'] = {
post: operation('Create source in pending state', [], {
...schema({
name: string,
responsible_entity: string,
reference: string,
license: string,
checksum: { type: 'string', pattern: '^[0-9a-f]{64}$' },
source_version: string,
collected_at: { type: 'string', format: 'date' },
original_crs: string,
}),
required: [
'name',
'reference',
'checksum',
'source_version',
'collected_at',
],
}),
};
paths['/sources/{id}/transitions'] = {
post: operation(
'Independent source approval or revocation with evidence reference',
[id],
{
...schema({
action: { enum: ['approved', 'revoked'] },
approval_reference: { type: 'string', minLength: 10 },
}),
required: ['action', 'approval_reference'],
},
),
};
paths['/sources'].get = operation(
'Management-only source metadata and decision evidence, MFA required',
);
paths['/drafts'] = {
get: operation(
'Management-only territorial editorial records, MFA required',
),
};
fs.writeFileSync(
'docs/investmoz/gis/territorial-openapi.json',
JSON.stringify(
{
openapi: '3.1.0',
info: {
title: 'InvestMoz territorial registry — implemented subset',
version: '0.1.0',
description:
'Local session adapter. OAuth/OIDC, geometric queries, assignments, imports, hierarchy endpoint and administrative UI remain unimplemented. Response schemas require expansion before external certification.',
},
servers: [{ url: '/api/v1/geo' }],
paths,
components: {
securitySchemes: {
laravelSession: {
type: 'apiKey',
in: 'cookie',
name: 'deployment_session_cookie',
description:
'Actual name configured by Laravel; write requests also require Laravel CSRF/origin protection. This is not OAuth/OIDC.',
},
},
},
},
null,
2,
) + '\n',
);
console.log('Territorial OpenAPI: ' + Object.keys(paths).length + ' paths');