diff --git a/etc/openapi.yaml b/etc/openapi.yaml new file mode 100644 index 00000000..ecd4c605 --- /dev/null +++ b/etc/openapi.yaml @@ -0,0 +1,484 @@ +--- +openapi: 3.0.3 +info: + title: Stomp Test + description: A sample application for websockets and stomp on Wildfly + contact: + email: mail@johannes-beck.name + license: + name: "The Apache Software License, Version 2.0" + url: http://www.apache.org/licenses/LICENSE-2.0.txt + version: "1.8" +tags: +- name: Quotes + description: receive quotes for shares +- name: Shares + description: subscribe to shares on the stock market +paths: + /rest: + get: + description: Link to available resources + responses: + "200": + description: The root resource + content: + application/json: + schema: + $ref: '#/components/schemas/index' + application/xml: + schema: + $ref: '#/components/schemas/index' + /rest/quotes: + get: + tags: + - Quotes + description: get quotes + parameters: + - name: X-Caller-ID + in: header + schema: + type: string + - name: X-Request-ID + in: header + schema: + type: string + - name: key + in: query + description: Stock symbols + schema: + type: array + items: + type: string + example: + - GOOG + responses: + "200": + description: Quotes received + content: + application/xml: + schema: + $ref: '#/components/schemas/quotes' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/quote' + "404": + description: No subscription found + /rest/quotes/{key}: + get: + tags: + - Quotes + description: get a quote + parameters: + - name: X-Caller-ID + in: header + schema: + type: string + - name: X-Request-ID + in: header + schema: + type: string + - name: key + in: path + description: "Stock symbol, see [quote.cnbc.com](https://quote.cnbc.com)" + required: true + schema: + type: string + example: BMW.DE + responses: + "200": + description: Quote received + content: + application/json: + schema: + $ref: '#/components/schemas/quote' + application/xml: + schema: + $ref: '#/components/schemas/quote' + "404": + description: Subscription not found + /rest/shares: + get: + tags: + - Shares + description: List all subscriptions + parameters: + - name: X-Caller-ID + in: header + schema: + type: string + - name: X-Request-ID + in: header + schema: + type: string + responses: + "200": + description: All subscriptions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/share' + application/xml: + schema: + $ref: '#/components/schemas/shares' + post: + tags: + - Shares + description: Add a share to your list of subscriptions + operationId: addShare + parameters: + - name: X-Caller-ID + in: header + schema: + type: string + - name: X-Request-ID + in: header + schema: + type: string + - name: Correlation-Id + in: header + description: provide a Correlation-Id header to receive a response for your + operation when it finished. + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/share' + application/xml: + schema: + $ref: '#/components/schemas/share' + responses: + "201": + description: Share queued for subscription + content: + application/json: + schema: + $ref: '#/components/schemas/share' + application/xml: + schema: + $ref: '#/components/schemas/share' + "500": + description: Queuing failed + "400": + description: Invalid data + content: + application/json: + schema: + $ref: '#/components/schemas/errors' + application/xml: + schema: + $ref: '#/components/schemas/errors' + /rest/shares/{key}: + get: + tags: + - Shares + description: Find a share subscription + parameters: + - name: X-Caller-ID + in: header + schema: + type: string + - name: X-Request-ID + in: header + schema: + type: string + - name: key + in: path + description: "Stock symbol, see [quote.cnbc.com](https://quote.cnbc.com)" + required: true + schema: + type: string + example: BMW.DE + responses: + "200": + description: Subscription found + content: + application/json: + schema: + $ref: '#/components/schemas/share' + application/xml: + schema: + $ref: '#/components/schemas/share' + "404": + description: Subscription not found + delete: + tags: + - Shares + description: Remove a subscription of a share + parameters: + - name: X-Caller-ID + in: header + schema: + type: string + - name: X-Request-ID + in: header + schema: + type: string + - name: key + in: path + description: Stock symbol + required: true + schema: + type: string + example: GOOG + responses: + "200": + description: Subscription removed + content: + application/json: + schema: + $ref: '#/components/schemas/share' + application/xml: + schema: + $ref: '#/components/schemas/share' + "404": + description: Subscription was not found + /rest/{path}: + options: + parameters: + - name: path + in: path + required: true + schema: + type: string + requestBody: + content: + '*/*': + schema: + type: string + responses: + "200": + description: OK +components: + schemas: + error: + description: Structured error message + type: object + properties: + message: + description: The error message + type: string + xml: + attribute: true + path: + description: "On validation errors: the property which was invalid" + type: string + xml: + attribute: true + invalidValue: + description: "On validation errors: the value which was invalid" + type: string + xml: + attribute: true + xml: + name: error + errors: + description: Error response + type: object + properties: + error: + type: array + items: + $ref: '#/components/schemas/error' + requestUri: + description: Request URI + type: string + xml: + attribute: true + type: + description: Error type + type: string + xml: + attribute: true + xml: + name: errors + index: + description: Index page with HATEOS links + type: object + properties: + links: + type: array + items: {} + xml: + name: link + xml: + name: index + link: + description: HTTP Link + type: object + properties: + href: + description: the link target as a URI-Reference + type: string + example: https://microsoft.com + nullable: false + rel: + description: The relation type of a link + type: string + example: self + title: + description: used to label the destination of a link such that it can be + used as a human-readable identifier + type: string + type: + description: a hint indicating what the media type of the result of dereferencing + the link should be + type: string + example: text/html + method: + description: HTTP method + type: string + example: get + quote: + description: A quote is the current price for a share + required: + - share + type: object + properties: + share: + description: the share + type: object + allOf: + - $ref: '#/components/schemas/share' + price: + format: float + description: the price + type: number + xml: + attribute: true + example: 12.34 + currency: + description: currency code + default: EUR + type: string + xml: + attribute: true + from: + format: date + description: "date of origin, as defined in `ISO8601`" + type: string + xml: + attribute: true + externalDocs: + url: https://en.wikipedia.org/wiki/ISO_8601 + example: 2022-03-10 + links: + type: array + items: + description: HTTP Link + type: object + properties: + href: + description: the link target as a URI-Reference + type: string + example: https://microsoft.com + nullable: false + rel: + description: The relation type of a link + type: string + example: self + title: + description: used to label the destination of a link such that it + can be used as a human-readable identifier + type: string + type: + description: a hint indicating what the media type of the result of + dereferencing the link should be + type: string + example: text/html + method: + description: HTTP method + type: string + example: get + readOnly: true + xml: + name: link + readOnly: true + xml: + name: quote + quotes: + type: object + properties: + quotes: + type: array + items: + $ref: '#/components/schemas/quote' + xml: + name: quote + readOnly: true + xml: + name: quotes + share: + description: "Shares are identified by stock symbols, and may have an name for\ + \ readability." + required: + - key + type: object + properties: + key: + description: Stock symbol + maxLength: 25 + minLength: 1 + pattern: "[A-Z0-9.]*" + type: string + xml: + attribute: true + example: MSFT + name: + description: Human readable name + maxLength: 80 + minLength: 1 + type: string + xml: + attribute: true + example: Microsoft Corp. + links: + type: array + items: + description: HTTP Link + type: object + properties: + href: + description: the link target as a URI-Reference + type: string + example: https://microsoft.com + nullable: false + rel: + description: The relation type of a link + type: string + example: self + title: + description: used to label the destination of a link such that it + can be used as a human-readable identifier + type: string + type: + description: a hint indicating what the media type of the result of + dereferencing the link should be + type: string + example: text/html + method: + description: HTTP method + type: string + example: get + readOnly: true + xml: + name: link + xml: + name: share + shares: + type: object + properties: + shares: + type: array + items: + $ref: '#/components/schemas/share' + xml: + name: share + readOnly: true + xml: + name: shares diff --git a/pom.xml b/pom.xml index d9efccca..5aa20b04 100644 --- a/pom.xml +++ b/pom.xml @@ -200,8 +200,8 @@ provided - io.opentelemetry - opentelemetry-api + io.opentelemetry + opentelemetry-api io.micrometer @@ -242,7 +242,7 @@ org.apache.commons commons-lang3 - 3.14.0 + 3.14.0 commons-io @@ -282,7 +282,7 @@ x1.wildfly service-registry - 1.3.3 + 1.3.4 com.google.guava @@ -629,6 +629,29 @@ + + smallrye-open-api-maven-plugin + io.smallrye + 3.8.0 + + + + generate-schema + + + + + ${project.name} + ${app.majorVersion}.${app.minorVersion} + ${project.description} + ${infoTermsOfService} + ${project.developers[0].email} + ${infoContactName} + ${infoContactUrl} + ${project.licenses[0].name} + ${project.licenses[0].url} + + diff --git a/src/main/java/x1/stomp/boundary/QuoteResource.java b/src/main/java/x1/stomp/boundary/QuoteResource.java index 4a938902..70ddf6fe 100644 --- a/src/main/java/x1/stomp/boundary/QuoteResource.java +++ b/src/main/java/x1/stomp/boundary/QuoteResource.java @@ -101,8 +101,11 @@ public class QuoteResource { @Path("/{key}") @Formatted @Operation(description = "get a quote") - @Parameters({ @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_CALLER_ID), - @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_REQUEST_ID) }) + @Parameters({ + @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_CALLER_ID, + schema = @Schema(implementation = String.class)), + @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_REQUEST_ID, + schema = @Schema(implementation = String.class)) }) @APIResponse(responseCode = "200", description = "Quote received", content = @Content(schema = @Schema(implementation = Quote.class))) @APIResponse(responseCode = "404", description = "Subscription not found") @@ -125,8 +128,11 @@ public Response getQuote(@Parameter(description = "Stock symbol, see [quote.cnbc @Path("/") @Formatted @Operation(description = "get quotes") - @Parameters({ @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_CALLER_ID), - @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_REQUEST_ID) }) + @Parameters({ + @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_CALLER_ID, + schema = @Schema(implementation = String.class)), + @Parameter(in = ParameterIn.HEADER, name = MDCFilter.X_REQUEST_ID, + schema = @Schema(implementation = String.class)) }) @APIResponse(responseCode = "200", description = "Quotes received", content = { @Content(schema = @Schema(implementation = QuoteWrapper.class), mediaType = APPLICATION_XML), diff --git a/src/main/java/x1/stomp/boundary/ShareResource.java b/src/main/java/x1/stomp/boundary/ShareResource.java index c5d387c4..a8446a3d 100644 --- a/src/main/java/x1/stomp/boundary/ShareResource.java +++ b/src/main/java/x1/stomp/boundary/ShareResource.java @@ -107,8 +107,9 @@ public class ShareResource { @Wrapped(element = "shares") @Formatted @Operation(description = "List all subscriptions") - @Parameters({ @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID), - @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID) }) + @Parameters({ + @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID, schema = @Schema(implementation = String.class)), + @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID, schema = @Schema(implementation = String.class)) }) @APIResponse(responseCode = "200", description = "All subscriptions", content = { @Content(schema = @Schema(type = SchemaType.ARRAY, implementation = Share.class), @@ -126,8 +127,9 @@ public List listAllShares() { @Path("/{key}") @Formatted @Operation(description = "Find a share subscription") - @Parameters({ @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID), - @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID) }) + @Parameters({ + @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID, schema = @Schema(implementation = String.class)), + @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID, schema = @Schema(implementation = String.class)) }) @APIResponse(responseCode = "200", description = "Subscription found", content = @Content(schema = @Schema(implementation = Share.class))) @APIResponse(responseCode = "404", description = "Subscription not found") @@ -146,8 +148,9 @@ public Response findShare(@Parameter(description = "Stock symbol, see [quote.cnb @POST @Formatted @Operation(description = "Add a share to your list of subscriptions", operationId = "addShare") - @Parameters({ @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID, example = "test", allowEmptyValue = true), - @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID, example = "12345", allowEmptyValue = true) }) + @Parameters({ + @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID, schema = @Schema(implementation = String.class)), + @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID, schema = @Schema(implementation = String.class)) }) @APIResponse(responseCode = "201", description = "Share queued for subscription", content = @Content(schema = @Schema(implementation = Share.class))) @APIResponse(responseCode = "500", description = "Queuing failed") @@ -158,8 +161,8 @@ public Response addShare( @Parameter(required = true, description = "The share which is will be added for subscription") @NotNull @Valid Share share, @Parameter( - description = "provide a Correlation-Id header to receive a response for your operation when it finished.", - allowEmptyValue = true, example = "12345") @HeaderParam(value = "Correlation-Id") String correlationId) { + description = "provide a Correlation-Id header to receive a response for your operation when it finished.") + @HeaderParam(value = "Correlation-Id") String correlationId) { try { var jmsCorrelationId = Objects.requireNonNullElse(correlationId, UUID.randomUUID().toString()); context.createProducer().setJMSCorrelationID(jmsCorrelationId).setProperty("type", "share") @@ -179,8 +182,9 @@ public Response addShare( @Path("/{key}") @Formatted @Operation(description = "Remove a subscription of a share") - @Parameters({ @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID), - @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID) }) + @Parameters({ + @Parameter(in = ParameterIn.HEADER, name = X_CALLER_ID, schema = @Schema(implementation = String.class)), + @Parameter(in = ParameterIn.HEADER, name = X_REQUEST_ID, schema = @Schema(implementation = String.class)) }) @APIResponse(responseCode = "200", description = "Subscription removed", content = @Content(schema = @Schema(implementation = Share.class))) @APIResponse(responseCode = "404", description = "Subscription was not found")