From 725446709bdda22689efe78ed8a5ce6a11684efd Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 7 Nov 2026 17:59:16 +0000 Subject: [PATCH 1/2] build(deps): align with Gitea registry (parent 2.2.1, QQQ 4.1.0-RC.26) pom.xml: parent -> 2.2.1 pom.xml: qqq.snapshot.version: 4.1.0-RC.13 -> 4.1.0-RC.26 pom.xml: added qqq.version=4.1.0-RC.26 (parent BOM import) Co-Authored-By: Claude Opus 5.5 --- pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ffed9fa..435c5c5 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ + 4.1.0-RC.26 0.1.0-SNAPSHOT 21 21 @@ -28,7 +29,7 @@ com.kingsrook qbit-build-parent - 2.0.0 + 2.2.1 com.kingsrook.qbits @@ -248,7 +249,7 @@ true - 4.1.0-RC.13 + 4.1.0-RC.26 -- 2.54.0 From f0ecacc3e938f4377d4fbb5874ea59628e437c98 Mon Sep 17 00:00:00 2001 From: James Maes <6+james.maes@noreply.git.kof22.com> Date: Sat, 7 Nov 2026 12:30:25 -0600 Subject: [PATCH 2/2] test: regenerate OpenAPI contract for QQQ 4.1.0-RC.26 QQQ RC.26 adds the shared BasicErrorResponseV1 schema to extension models; regenerate the committed fragment and allow that core schema. Co-Authored-By: Claude Opus 5.5 --- .../openapi/qqq-v1/db-performance.yaml | 186 ++++++++++++++++++ .../dbperformance/DbPerfApiContractTest.java | 2 +- 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/src/main/resources/openapi/qqq-v1/db-performance.yaml b/src/main/resources/openapi/qqq-v1/db-performance.yaml index 6c47106..e5570c1 100644 --- a/src/main/resources/openapi/qqq-v1/db-performance.yaml +++ b/src/main/resources/openapi/qqq-v1/db-performance.yaml @@ -1,5 +1,44 @@ components: schemas: + BasicErrorResponseV1: + description: "The error body of every UI API operation (core and extensions)." + properties: + error: + description: "The error message (the same as message; kept for clients of\ + \ UI API 1.0 to 1.5)." + type: "string" + code: + description: "A stable, machine-readable code. Well-known values: BAD_REQUEST\ + \ (400), UNAUTHENTICATED (401),\nPERMISSION_DENIED (403), NOT_FOUND (404),\ + \ CONFLICT (409), RATE_LIMITED (429), INTERNAL_ERROR (500),\nERROR (other).\ + \ Extensions may add codes; treat an unknown code by the HTTP status.\ + \ Since 1.6.0." + type: "string" + message: + description: "A message to show the user. Since 1.6.0." + type: "string" + fieldErrors: + description: "Errors about particular request fields, when known. Since\ + \ 1.6.0." + items: + properties: + field: + description: "The field (or parameter) name." + type: "string" + message: + description: "The error about that field." + type: "string" + code: + description: "An optional machine-readable code, e.g. REQUIRED, TOO_LONG,\ + \ PATTERN." + type: "string" + type: "object" + type: "array" + traceId: + description: "Identifies this request in the server logs (also the X-QQQ-Trace-Id\ + \ response header); quote it when reporting a problem. Since 1.6.0." + type: "string" + type: "object" DbPerformanceActivityV1: properties: blockingChains: @@ -256,9 +295,42 @@ components: type: "object" securitySchemes: sessionUuidCookie: + description: "The session cookie set by POST /qqq/v1/manageSession (web clients)." in: "cookie" name: "sessionUUID" type: "apiKey" + bearerAuth: + bearerFormat: "JWT or session UUID" + description: "`Authorization: Bearer `, where the token is either\n-\ + \ an OAuth2 access token (a signed JWT) issued by the app's identity provider\ + \ to the app's client,\n or to its native client (`values.nativeClientId`\ + \ in GET /qqq/v1/metaData/authentication), for\n example from the device-authorization\ + \ grant or auth-code + PKCE. It is verified against the\n provider's keys\ + \ (issuer, audience, expiry, signature) and gives the same user and permissions\n\ + \ as signing in on the web. When it expires (401), refresh it at the provider\ + \ and retry; or\n- a session UUID returned by POST /qqq/v1/manageSession,\ + \ which the server keeps refreshed." + scheme: "bearer" + type: "http" + oauth2: + description: "The app's OAuth2 / OpenID Connect provider. The real endpoints\ + \ come from the provider's discovery\ndocument: `{values.baseUrl}.well-known/openid-configuration`,\ + \ with values from\nGET /qqq/v1/metaData/authentication. Native clients use\ + \ `values.nativeClientId` (a public client)\nwith auth-code + PKCE (loopback\ + \ or custom-scheme redirect URI registered at the provider) or the\ndevice-authorization\ + \ grant (`device_authorization_endpoint`, RFC 8628), then send the access\ + \ token\nas `Authorization: Bearer`." + flows: + authorizationCode: + authorizationUrl: "/authorize" + scopes: + email: "Email address" + offline_access: "A refresh token, to renew the access token without signing\ + \ in again" + openid: "OpenID Connect sign-in" + profile: "Name, username and groups (permissions come from the groups)" + tokenUrl: "/token" + type: "oauth2" info: contact: email: "contact@kingsrook.com" @@ -303,10 +375,37 @@ paths: schema: $ref: "#/components/schemas/DbPerformanceOverviewV1" description: "Successful Response" + 404: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Not found, or not visible to this user (code NOT_FOUND)." + 409: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Conflict with the current state, e.g. an edit conflict or\ + \ an invalid transition (code CONFLICT)." + 429: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Too many requests (code RATE_LIMITED); retry later." security: - sessionUuidCookie: - "N/A" + - bearerAuth: + - "N/A" + - oauth2: + - "openid" + - "profile" + - "email" summary: "Database Performance overview" + tags: + - "db-performance" /qqq/v1/qbit/db-performance/activity: get: description: "Requires db_performance.view and uses the caller's session." @@ -343,10 +442,37 @@ paths: schema: $ref: "#/components/schemas/DbPerformanceActivityV1" description: "Successful Response" + 404: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Not found, or not visible to this user (code NOT_FOUND)." + 409: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Conflict with the current state, e.g. an edit conflict or\ + \ an invalid transition (code CONFLICT)." + 429: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Too many requests (code RATE_LIMITED); retry later." security: - sessionUuidCookie: - "N/A" + - bearerAuth: + - "N/A" + - oauth2: + - "openid" + - "profile" + - "email" summary: "Database Performance activity" + tags: + - "db-performance" /qqq/v1/qbit/db-performance/explain: post: description: "PLAN requires db_performance.explain; ANALYZE requires db_performance.explain_analyze.\ @@ -383,10 +509,37 @@ paths: schema: $ref: "#/components/schemas/DbPerformanceExplainResultV1" description: "Successful Response" + 404: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Not found, or not visible to this user (code NOT_FOUND)." + 409: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Conflict with the current state, e.g. an edit conflict or\ + \ an invalid transition (code CONFLICT)." + 429: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Too many requests (code RATE_LIMITED); retry later." security: - sessionUuidCookie: - "N/A" + - bearerAuth: + - "N/A" + - oauth2: + - "openid" + - "profile" + - "email" summary: "Explain a query (PostgreSQL JSON or H2 text plan)" + tags: + - "db-performance" /qqq/v1/qbit/db-performance/statements/lookup: get: description: "Requires db_performance.view and uses the caller's session." @@ -428,7 +581,40 @@ paths: schema: $ref: "#/components/schemas/DbPerformanceLookupV1" description: "Successful Response" + 404: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Not found, or not visible to this user (code NOT_FOUND)." + 409: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Conflict with the current state, e.g. an edit conflict or\ + \ an invalid transition (code CONFLICT)." + 429: + content: + application/json: + schema: + $ref: "#/components/schemas/BasicErrorResponseV1" + description: "Too many requests (code RATE_LIMITED); retry later." security: - sessionUuidCookie: - "N/A" + - bearerAuth: + - "N/A" + - oauth2: + - "openid" + - "profile" + - "email" summary: "Database Performance lookup" + tags: + - "db-performance" +servers: +- description: "The QQQ server that serves this document (paths include /qqq/v1)." + url: "/" +tags: +- description: "Operations of the db-performance extension." + name: "db-performance" diff --git a/src/test/java/com/kingsrook/qbits/dbperformance/DbPerfApiContractTest.java b/src/test/java/com/kingsrook/qbits/dbperformance/DbPerfApiContractTest.java index a63aeb3..bd74f89 100644 --- a/src/test/java/com/kingsrook/qbits/dbperformance/DbPerfApiContractTest.java +++ b/src/test/java/com/kingsrook/qbits/dbperformance/DbPerfApiContractTest.java @@ -56,7 +56,7 @@ class DbPerfApiContractTest } assertThat(UiApiContract.check(generated, Files.readString(path), null, 1, "mvnw -Dtest=DbPerfApiContractTest -Ddbperf.openapi.update=true test")).isEmpty(); var model = version.generateExtensionOpenAPIModel("qqq", extension); - assertThat(model.getComponents().getSchemas().keySet()).allMatch(name -> name.startsWith("DbPerformance") && name.endsWith("V1")); + assertThat(model.getComponents().getSchemas().keySet()).allMatch(name -> (name.startsWith("DbPerformance") || name.equals("BasicErrorResponseV1")) && name.endsWith("V1")); assertThat(model.getPaths().keySet()).containsExactlyInAnyOrder("/qqq/v1/qbit/db-performance/overview", "/qqq/v1/qbit/db-performance/activity", "/qqq/v1/qbit/db-performance/explain", "/qqq/v1/qbit/db-performance/statements/lookup"); assertThat(extension.getSemanticVersion()).isEqualTo("1.0.0"); -- 2.54.0