Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36f1b939b4
|
||
|
|
77d63135af
|
||
|
|
603e518a34 |
No files matched your search
@@ -6,6 +6,8 @@ Following [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic
|
||||
|
||||
### Changed
|
||||
|
||||
- UI API extension edi 1.1.0 (requires QQQ 4.1.0-RC.15, UI API 1.6.0): operationIds, standard error responses with BasicErrorResponseV1, extension tags, and retained deprecated EdiErrorV1 schema.
|
||||
|
||||
- Derive qbit metadata version from the Maven artifact version, keeping release metadata and help bundle versions in sync.
|
||||
|
||||
- Require QQQ 4.1.0-RC.7 for core help-bundle registration and coverage validation.
|
||||
|
||||
@@ -95,8 +95,8 @@ transports/guides and local certificates/identities.
|
||||
|
||||
## UI API
|
||||
|
||||
With optional Javalin present, the producer registers the `edi` extension, version `1.0.0`,
|
||||
under `/qqq/v1/qbit/edi`. All routes are endpoint specs and pass strict UI-route auditing.
|
||||
With optional Javalin present, the producer registers the `edi` extension, version `1.1.0`,
|
||||
under `/qqq/v1/qbit/edi`, requiring QQQ 4.1.0-RC.15 (UI API 1.6.0). All routes are endpoint specs and pass strict UI-route auditing.
|
||||
|
||||
| Method | Path after `/qqq/v1/qbit/edi` | Result |
|
||||
|---|---|---|
|
||||
@@ -133,15 +133,15 @@ The mapper saves `edi_map_rule` through the **core table API**, using its normal
|
||||
`edi.map_rule.insert|edit|delete` permissions and record locks. There are no custom rule-write routes.
|
||||
The Next mapper page is separate phase-f work.
|
||||
|
||||
The generated contract is `src/main/resources/openapi/qqq-v1/edi.yaml`; all component names are
|
||||
`Edi...V1`. Regenerate intentionally with:
|
||||
The generated contract is `src/main/resources/openapi/qqq-v1/edi.yaml`; extension component names are
|
||||
`Edi...V1`, alongside core `BasicErrorResponseV1`. `EdiErrorV1` remains deprecated for compatibility. Regenerate with the locally installed migration build:
|
||||
|
||||
```powershell
|
||||
./mvnw.cmd -B '-Dtest=EdiApiContractTest' '-Dedi.openapi.update=true' test
|
||||
./mvnw.cmd -o -B '-Dqqq.snapshot.version=4.1.99-SNAPSHOT' '-Dtest=EdiApiContractTest' '-Dedi.openapi.update=true' test
|
||||
```
|
||||
|
||||
RC.6 provides `UiApiContract.check`, used to compare generated and checked-in YAML and run
|
||||
`OpenApiCompatibilityChecker` against this initial fragment baseline. The extension also appears
|
||||
Core provides `UiApiContract.check`, used to compare generated and checked-in YAML, validate
|
||||
operation IDs and references, and run `OpenApiCompatibilityChecker` against the committed fragment baseline. The extension also appears
|
||||
in the host's `/qqq/v1/openapi.yaml` and `/qqq/v1/version`.
|
||||
|
||||
## Inbound operation
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<qqq.snapshot.version>4.1.0-RC.7</qqq.snapshot.version>
|
||||
<qqq.snapshot.version>4.1.0-RC.15</qqq.snapshot.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.kingsrook.qqq.middleware.javalin.specs.BasicResponse;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.extensions.AbstractJsonEndpointSpec;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.extensions.MiddlewareSpecExtension;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.extensions.MiddlewareSpecExtensionsMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.specs.v1.responses.BasicErrorResponseV1;
|
||||
import com.kingsrook.qqq.openapi.model.HttpMethod;
|
||||
import com.kingsrook.qqq.openapi.model.In;
|
||||
import com.kingsrook.qqq.openapi.model.Parameter;
|
||||
@@ -44,7 +45,7 @@ import com.kingsrook.qqq.openapi.model.Type;
|
||||
import io.javalin.http.HttpStatus;
|
||||
|
||||
/*******************************************************************************
|
||||
** The EDI 1.0.0 UI API: four spec'd operations in the default qbit namespace.
|
||||
** The EDI 1.1.0 UI API: four spec'd operations in the default qbit namespace.
|
||||
*******************************************************************************/
|
||||
public final class EdiApi
|
||||
{
|
||||
@@ -68,7 +69,7 @@ public final class EdiApi
|
||||
*******************************************************************************/
|
||||
public static MiddlewareSpecExtension extension()
|
||||
{
|
||||
return (new MiddlewareSpecExtension().withName("edi").withSemanticVersion("1.0.0")
|
||||
return (new MiddlewareSpecExtension().withName("edi").withSemanticVersion("1.1.0")
|
||||
.withDescription("EDI sandbox and mapper, using the signed-in user's permissions.")
|
||||
.withSpec(GuideTreeSpec.class).withSpec(MapTargetSpec.class).withSpec(PreviewSpec.class).withSpec(ValidateSpec.class));
|
||||
}
|
||||
@@ -148,11 +149,11 @@ public final class EdiApi
|
||||
@Override
|
||||
public List<BasicResponse> defineAdditionalBasicResponses()
|
||||
{
|
||||
return (List.of(new BasicResponse(HttpStatus.BAD_REQUEST, "Invalid request or sample exceeds 5 MiB", "EdiErrorV1"),
|
||||
new BasicResponse(HttpStatus.UNAUTHORIZED, "Sign in required", "EdiErrorV1"),
|
||||
new BasicResponse(HttpStatus.FORBIDDEN, "Permission denied", "EdiErrorV1"),
|
||||
new BasicResponse(HttpStatus.NOT_FOUND, "Missing or invisible record", "EdiErrorV1"),
|
||||
new BasicResponse(HttpStatus.CONTENT_TOO_LARGE, "Host HTTP request size limit exceeded", "EdiErrorV1")));
|
||||
return (List.of(new BasicResponse(HttpStatus.BAD_REQUEST, "Invalid request or sample exceeds 5 MiB", BasicErrorResponseV1.class.getSimpleName()),
|
||||
new BasicResponse(HttpStatus.UNAUTHORIZED, "Sign in required", BasicErrorResponseV1.class.getSimpleName()),
|
||||
new BasicResponse(HttpStatus.FORBIDDEN, "Permission denied", BasicErrorResponseV1.class.getSimpleName()),
|
||||
new BasicResponse(HttpStatus.NOT_FOUND, "Missing or invisible record", BasicErrorResponseV1.class.getSimpleName()),
|
||||
new BasicResponse(HttpStatus.CONTENT_TOO_LARGE, "Host HTTP request size limit exceeded", BasicErrorResponseV1.class.getSimpleName())));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -489,7 +489,7 @@ public final class EdiSchemas
|
||||
@Override
|
||||
public Schema toSchema()
|
||||
{
|
||||
return (new Schema().withType(Type.OBJECT).withDescription("Core middleware error body.")
|
||||
return (new Schema().withType(Type.OBJECT).withDescription("Deprecated since 1.1.0: errors use BasicErrorResponseV1.")
|
||||
.withProperties(new TreeMap<>(Map.ofEntries(
|
||||
Map.entry("error", new Schema().withType(Type.STRING).withNullable(true))))));
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
EdiAckV1:
|
||||
description: "Concatenated acknowledgement previews; type is 997, 999, TA1,\
|
||||
\ CONTRL, NONE or MIXED. text is null for PHI."
|
||||
@@ -93,7 +132,7 @@ components:
|
||||
type: "string"
|
||||
type: "object"
|
||||
EdiErrorV1:
|
||||
description: "Core middleware error body."
|
||||
description: "Deprecated since 1.1.0: errors use BasicErrorResponseV1."
|
||||
properties:
|
||||
error:
|
||||
nullable: true
|
||||
@@ -397,21 +436,55 @@ 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 <token>`, 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"
|
||||
description: "EDI sandbox and mapper, using the signed-in user's permissions."
|
||||
title: "QQQ Middleware API extension: edi"
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
openapi: "3.0.3"
|
||||
paths:
|
||||
/qqq/v1/qbit/edi/guides/{guideId}/tree:
|
||||
get:
|
||||
description: "Read the guide tree. Uses the caller session and table read permissions.\
|
||||
\ Preview also requires edi.map.preview. Rule writes use core table CRUD."
|
||||
operationId: "getQbitEdiGuidesByGuideIdTree"
|
||||
parameters:
|
||||
- description: "Positive record identifier."
|
||||
in: "path"
|
||||
@@ -424,25 +497,25 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Invalid request or sample exceeds 5 MiB"
|
||||
401:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Sign in required"
|
||||
403:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Permission denied"
|
||||
404:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Missing or invisible record"
|
||||
200:
|
||||
content:
|
||||
@@ -454,17 +527,39 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Host HTTP request size limit exceeded"
|
||||
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: "Read the guide tree"
|
||||
tags:
|
||||
- "edi"
|
||||
/qqq/v1/qbit/edi/maps/{mapId}/target:
|
||||
get:
|
||||
description: "Read target metadata and map rules. Uses the caller session and\
|
||||
\ table read permissions. Preview also requires edi.map.preview. Rule writes\
|
||||
\ use core table CRUD."
|
||||
operationId: "getQbitEdiMapsByMapIdTarget"
|
||||
parameters:
|
||||
- description: "Positive record identifier."
|
||||
in: "path"
|
||||
@@ -477,25 +572,25 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Invalid request or sample exceeds 5 MiB"
|
||||
401:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Sign in required"
|
||||
403:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Permission denied"
|
||||
404:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Missing or invisible record"
|
||||
200:
|
||||
content:
|
||||
@@ -507,17 +602,39 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Host HTTP request size limit exceeded"
|
||||
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: "Read target metadata and map rules"
|
||||
tags:
|
||||
- "edi"
|
||||
/qqq/v1/qbit/edi/sandbox/preview:
|
||||
post:
|
||||
description: "Preview an encrypted sample with optional unsaved rules. Uses\
|
||||
\ the caller session and table read permissions. Preview also requires edi.map.preview.\
|
||||
\ Rule writes use core table CRUD."
|
||||
operationId: "postQbitEdiSandboxPreview"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
@@ -529,25 +646,25 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Invalid request or sample exceeds 5 MiB"
|
||||
401:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Sign in required"
|
||||
403:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Permission denied"
|
||||
404:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Missing or invisible record"
|
||||
200:
|
||||
content:
|
||||
@@ -559,17 +676,39 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Host HTTP request size limit exceeded"
|
||||
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: "Preview an encrypted sample with optional unsaved rules"
|
||||
tags:
|
||||
- "edi"
|
||||
/qqq/v1/qbit/edi/maps/{mapId}/validate:
|
||||
post:
|
||||
description: "Validate unsaved map rules. Uses the caller session and table\
|
||||
\ read permissions. Preview also requires edi.map.preview. Rule writes use\
|
||||
\ core table CRUD."
|
||||
operationId: "postQbitEdiMapsByMapIdValidate"
|
||||
parameters:
|
||||
- description: "Positive record identifier."
|
||||
in: "path"
|
||||
@@ -588,25 +727,25 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Invalid request or sample exceeds 5 MiB"
|
||||
401:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Sign in required"
|
||||
403:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Permission denied"
|
||||
404:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Missing or invisible record"
|
||||
200:
|
||||
content:
|
||||
@@ -618,9 +757,36 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EdiErrorV1"
|
||||
$ref: "#/components/schemas/BasicErrorResponseV1"
|
||||
description: "Host HTTP request size limit exceeded"
|
||||
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: "Validate unsaved map rules"
|
||||
tags:
|
||||
- "edi"
|
||||
servers:
|
||||
- description: "The QQQ server that serves this document (paths include /qqq/v1)."
|
||||
url: "/"
|
||||
tags:
|
||||
- description: "Operations of the edi extension."
|
||||
name: "edi"
|
||||
@@ -35,7 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
class EdiApiContractTest
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Use RC.6's real helper, including compatibility comparison against this first baseline.
|
||||
** Check the generated contract and compatibility against the committed baseline.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void contract() throws Exception
|
||||
@@ -53,15 +53,15 @@ class EdiApiContractTest
|
||||
assertThat(UiApiContract.check(generated, committed, committed, 1,
|
||||
"mvnw.cmd -B -Dtest=EdiApiContractTest -Dedi.openapi.update=true test")).isEmpty();
|
||||
var model = version.generateExtensionOpenAPIModel("qqq", extension);
|
||||
assertThat(model.getComponents().getSchemas().keySet()).allMatch(n -> n.startsWith("Edi") && n.endsWith("V1"));
|
||||
assertThat(model.getComponents().getSchemas().keySet()).allMatch(n -> (n.startsWith("Edi") && n.endsWith("V1")) || n.equals("BasicErrorResponseV1"));
|
||||
assertThat(model.getPaths()).hasSize(4);
|
||||
assertThat(model.getPaths().keySet()).allMatch(n -> n.startsWith("/qqq/v1/qbit/edi/"));
|
||||
assertThat(extension.getEffectivePathPrefix()).isEqualTo("/qbit/edi");
|
||||
assertThat(extension.getSemanticVersion()).isEqualTo("1.0.0");
|
||||
assertThat(extension.getSemanticVersion()).isEqualTo("1.1.0");
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Producer registers the extension and all metadata validates with RC.6.
|
||||
** Producer registers the extension and all metadata validates with core middleware.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void registration() throws Exception
|
||||
|
||||
@@ -169,8 +169,18 @@ class EdiApiHttpTest
|
||||
private HttpResponse<String> request(String method, String path, Object body, String role) throws Exception
|
||||
{
|
||||
String text = body == null ? "" : body instanceof String s ? s : JSON.writeValueAsString(body);
|
||||
return (client.send(HttpRequest.newBuilder(URI.create(baseUrl + path)).header("Cookie", "sessionUUID=" + role + suffix)
|
||||
.header("Content-Type", "application/json").method(method, HttpRequest.BodyPublishers.ofString(text)).build(), HttpResponse.BodyHandlers.ofString()));
|
||||
var response = client.send(HttpRequest.newBuilder(URI.create(baseUrl + path)).header("Cookie", "sessionUUID=" + role + suffix)
|
||||
.header("Content-Type", "application/json").method(method, HttpRequest.BodyPublishers.ofString(text)).build(), HttpResponse.BodyHandlers.ofString());
|
||||
if(response.statusCode() >= 400)
|
||||
{
|
||||
JsonNode error = JSON.readTree(response.body());
|
||||
for(String field : List.of("error", "code", "message", "traceId"))
|
||||
{
|
||||
assertThat(error.path(field).isTextual()).as(field + " in " + response.body()).isTrue();
|
||||
assertThat(error.path(field).asText()).isNotBlank();
|
||||
}
|
||||
}
|
||||
return (response);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
Reference in new issue
Block a user