Add delivery and supplier HTTP APIs with expanded response docs.
Wire store-backed CRUD for goods receipts and suppliers, and teach the API doc generator to expand $ref/allOf/array samples so route markdown shows full JSON shapes.
This commit is contained in:
@@ -36,7 +36,10 @@ const options = {
|
||||
{ name: 'Company', description: 'Company CRUD' },
|
||||
{ name: 'Unit', description: 'Company-scoped units of measure' },
|
||||
{ name: 'Brand', description: 'Company-scoped brands' },
|
||||
{ name: 'Supplier', description: 'Company-scoped suppliers (Lieferanten)' },
|
||||
{ name: 'Delivery', description: 'Company-scoped goods receipts (Lieferungen)' },
|
||||
{ name: 'Image', description: 'Content-addressed AVIF by fullHash' },
|
||||
{ name: 'Posterminal', description: 'Company-scoped POS terminals' },
|
||||
],
|
||||
},
|
||||
apis: [
|
||||
@@ -64,41 +67,68 @@ function schemaRefName(schema) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function formatSchema(schema, components, indent = 0) {
|
||||
if (!schema) return '_none_';
|
||||
/**
|
||||
* Build a sample JSON value from an OpenAPI schema (expands $ref / allOf / array items).
|
||||
* @param {object} schema
|
||||
* @param {object} [components]
|
||||
* @param {number} [depth]
|
||||
* @returns {*}
|
||||
*/
|
||||
function toExample(schema, components, depth = 0) {
|
||||
if (!schema || depth > 8) return null;
|
||||
|
||||
if (schema.example !== undefined) return schema.example;
|
||||
|
||||
if (schema.$ref) {
|
||||
const name = schemaRefName(schema);
|
||||
const resolved = components?.schemas?.[name];
|
||||
if (resolved) {
|
||||
return formatSchema({ ...resolved, title: name }, components, indent);
|
||||
const resolved = components?.schemas?.[schemaRefName(schema)];
|
||||
return resolved ? toExample(resolved, components, depth) : {};
|
||||
}
|
||||
|
||||
if (Array.isArray(schema.allOf) && schema.allOf.length) {
|
||||
const out = {};
|
||||
for (const part of schema.allOf) {
|
||||
const sample = toExample(part, components, depth + 1);
|
||||
if (sample && typeof sample === 'object' && !Array.isArray(sample)) {
|
||||
Object.assign(out, sample);
|
||||
}
|
||||
}
|
||||
return `\`${name}\``;
|
||||
}
|
||||
if (schema.example !== undefined) {
|
||||
return '```json\n' + JSON.stringify(schema.example, null, 2) + '\n```';
|
||||
}
|
||||
if (schema.type === 'object' && schema.properties) {
|
||||
const lines = ['```json', '{'];
|
||||
const keys = Object.keys(schema.properties);
|
||||
keys.forEach((key, i) => {
|
||||
const prop = schema.properties[key];
|
||||
const comma = i < keys.length - 1 ? ',' : '';
|
||||
let value = '…';
|
||||
if (prop.type === 'string') value = prop.format === 'email' ? '"user@example.com"' : '"…"';
|
||||
else if (prop.type === 'integer' || prop.type === 'number') value = prop.nullable ? 'null' : '1';
|
||||
else if (prop.type === 'boolean') value = 'true';
|
||||
else if (prop.type === 'array') value = '[]';
|
||||
else if (prop.type === 'object' || prop.$ref) value = '{ … }';
|
||||
if (prop.example !== undefined) value = JSON.stringify(prop.example);
|
||||
lines.push(` "${key}": ${value}${comma}`);
|
||||
});
|
||||
lines.push('}', '```');
|
||||
return lines.join('\n');
|
||||
return out;
|
||||
}
|
||||
|
||||
if (schema.type === 'array') {
|
||||
return '```json\n[]\n```';
|
||||
if (schema.items) {
|
||||
return [toExample(schema.items, components, depth + 1)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return '```json\n' + JSON.stringify(schema, null, 2) + '\n```';
|
||||
|
||||
if (schema.type === 'object' || schema.properties) {
|
||||
const out = {};
|
||||
for (const [key, prop] of Object.entries(schema.properties || {})) {
|
||||
out[key] = toExample(prop, components, depth + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
if (schema.type === 'string') {
|
||||
if (schema.enum?.length) return schema.enum[0];
|
||||
if (schema.format === 'email') return 'user@example.com';
|
||||
if (schema.format === 'date-time') return '2026-08-10T00:00:00.000Z';
|
||||
if (schema.format === 'date') return '2026-08-10';
|
||||
return '…';
|
||||
}
|
||||
|
||||
if (schema.type === 'integer') return 1;
|
||||
if (schema.type === 'number') return 1;
|
||||
if (schema.type === 'boolean') return true;
|
||||
if (schema.nullable) return null;
|
||||
return '…';
|
||||
}
|
||||
|
||||
function formatSchema(schema, components) {
|
||||
if (!schema) return '_none_';
|
||||
const example = toExample(schema, components);
|
||||
return '```json\n' + JSON.stringify(example, null, 2) + '\n```';
|
||||
}
|
||||
|
||||
function contentSchema(content) {
|
||||
|
||||
Reference in New Issue
Block a user