From f8240d2edf57c6217f43dfb7d18b436ec26924cd Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 14:53:36 -0500 Subject: [PATCH] docs(openapi): refresh the info block (description, contact) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spec's `info.description` undersold the API as "Customer and time-entry records" — 16 entities ago. Expand to the full entity list and call out the actual feature surface that operators care about when evaluating the API (idempotency, Link-header pagination, Prometheus /metrics, CSV export with formula-injection mitigation). Add a `contact` field pointing to the GitHub security policy. The project's vulnerability-report channel (private advisory + email fallback) is documented in SECURITY.md but Swagger UI's info panel never surfaced it — operators reading the docs in a browser had to leave the spec page to find the policy. The OpenAPI `contact` slot fits the link cleanly (Swagger UI renders contact.name + contact.url under "Contact the developer"). Pin the new fields with assertions in tests/api/openapi.test.js so a future re-write can't silently drop the license/contact pair — both matter for SBOM tooling and downstream Apache-2.0 redistributors that read the spec to populate their own metadata. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/config/openapi.js | 19 +++++++++++++++++-- tests/api/openapi.test.js | 10 ++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/config/openapi.js b/app/config/openapi.js index 9dbc7f5..4aa4099 100644 --- a/app/config/openapi.js +++ b/app/config/openapi.js @@ -428,10 +428,25 @@ const spec = { title: 'TimeTrackerAPI', version: pkg.version || '1.0.0', description: - 'Open-source Node.js + PostgreSQL TimeTrackerAPI. Customer and ' + - 'time-entry records, scoped by company via an `authKey` header. ' + + 'Open-source Node.js + PostgreSQL TimeTrackerAPI. 16 ' + + 'company-scoped entities (Customer, TimeEntry, Worker, ' + + 'BillingType, InventoryItem, Company, Job, Invoice, ' + + 'CustomerPayment, InvoiceJob, ProductEntry, VersionInfo, ' + + 'PurchaseOrderVendor, PurchaseOrderHeader, ' + + 'PurchaseOrderLine, InventoryTransaction), Stripe-style ' + + 'idempotency on every POST, RFC 5988 Link-header ' + + 'pagination, Prometheus `/metrics`, CSV export with OWASP ' + + 'formula-injection mitigation. Auth via `authKey` header. ' + 'Source: https://github.com/CryptoJones/TimeTrackerAPI / ' + 'https://codeberg.org/CryptoJones/TimeTrackerAPI.', + // Security-vulnerability reports route through the + // private channels in SECURITY.md — link surfaces in + // Swagger UI's info panel so docs consumers can find the + // policy without leaving the spec. + contact: { + name: 'Security policy (private vuln reports)', + url: 'https://github.com/CryptoJones/TimeTrackerAPI/security/policy', + }, license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0', diff --git a/tests/api/openapi.test.js b/tests/api/openapi.test.js index 11146fc..819d44d 100644 --- a/tests/api/openapi.test.js +++ b/tests/api/openapi.test.js @@ -37,6 +37,16 @@ describe('OpenAPI spec', () => { expect(res.body.openapi).toMatch(/^3\./); expect(res.body.info.title).toBe('TimeTrackerAPI'); expect(res.body.info.version).toBeDefined(); + // license is required for downstream Apache-2.0 compliance + // tooling (e.g. SBOM scanners). Pin its presence and SPDX + // identifier so a future re-write can't silently drop it. + expect(res.body.info.license).toBeDefined(); + expect(res.body.info.license.name).toBe('Apache 2.0'); + // contact.url surfaces the security policy in Swagger UI's + // info panel — operators reading the docs find the + // vuln-report channel without leaving the spec page. + expect(res.body.info.contact).toBeDefined(); + expect(res.body.info.contact.url).toMatch(/security/i); }); test('spec includes all v1 paths', async () => {