From 83b64c9f054e2717a1e8bbd8a0597c29b3988d28 Mon Sep 17 00:00:00 2001 From: Thomas Bayer Date: Sat, 6 Dec 2025 17:06:07 +0100 Subject: [PATCH 1/5] feat: add `MCExcludeFromSchema` annotation and migrate `statusCode` to `status` in YAML tutorials and schemas - Introduced `MCExcludeFromSchema` annotation to allow exclusion of fields during schema generation. - Replaced `statusCode` with `status` across YAML tutorial and schema files for consistency. - Updated `JsonSchemaGenerator` and `AttributeInfo` to support exclusion logic based on the new annotation. --- .../membrane/annot/MCExcludeFromSchema.java | 39 +++++++++++++++++++ .../annot/generator/JsonSchemaGenerator.java | 7 +++- .../membrane/annot/model/AttributeInfo.java | 26 +++++++++++++ .../interceptor/flow/ReturnInterceptor.java | 35 ++++++++++++----- .../core/config/spring/k8s/EnvelopeTest.java | 2 +- .../custom-error-messages/apis.yaml | 4 +- .../examples/extending-membrane/if/apis.yaml | 2 +- .../monitoring-tracing/prometheus/apis.yaml | 6 +-- .../openapi/validation-security/apis.yaml | 2 +- .../openapi/validation-simple/apis.yaml | 2 +- .../examples/openapi/validation/apis.yaml | 4 +- .../call-authentication/apis.yaml | 2 +- .../routing-traffic/shadowing/apis.yaml | 6 +-- .../ssl-tls/api-with-tls-pem/apis.yaml | 2 +- .../ssl-tls/api-with-tls-pkcs12/apis.yaml | 2 +- .../examples/templating/json/apis.yaml | 2 +- .../examples/templating/xml/apis.yaml | 4 +- .../tutorials/advanced/10-PathParameters.yaml | 2 +- distribution/tutorials/advanced/60-if.yaml | 2 +- .../advanced/70-Scripting-Groovy.yaml | 2 +- .../getting-started/50-Short-Circuit.yaml | 2 +- .../getting-started/60-SetHeader.yaml | 2 +- .../getting-started/70-Template.yaml | 2 +- distribution/tutorials/json/20-JSONPath.yaml | 2 +- .../json/60-Json-Transformation.yaml | 2 +- .../tutorials/xml/10-JSON-to-XML.yaml | 2 +- .../tutorials/xml/15-XML-to-JSON.yaml | 2 +- distribution/tutorials/xml/20-XPath.yaml | 2 +- 28 files changed, 127 insertions(+), 42 deletions(-) create mode 100644 annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java diff --git a/annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java b/annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java new file mode 100644 index 0000000000..e78253e6df --- /dev/null +++ b/annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java @@ -0,0 +1,39 @@ +/* Copyright 2025 predic8 GmbH, www.predic8.com + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +package com.predic8.membrane.annot; + +import java.lang.annotation.*; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * Annotation to exclude specific fields from being included in schemas + * during schema generation for different configuration formats. + * + * This annotation can be applied to fields to specify whether they should + * be excluded from JSON Schema (YAML) or XSD (XML configuration). + * + * - Setting `json` to true excludes the field from JSON Schema. + * - Setting `xsd` to true excludes the field from XSD. + */ +@Retention(RUNTIME) +@Target(METHOD) +public @interface MCExcludeFromSchema { + boolean json() default false; // excludes from JSON Schema (YAML) + boolean xsd() default false; // excludes from XSD (XML configuration) +} + + diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java index a233aa5984..b61f8b3725 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java +++ b/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java @@ -173,6 +173,11 @@ private FileObject createFile(MainInfo main) throws IOException { private void processMCAttributes(ElementInfo i, SchemaObject so) { i.getAis().forEach(ai -> { + + // skip attributes marked with @MCExcludeFromSchema + if (ai.isExcludedFromJsonSchema()) + return; + // hide id only on top-level elements if ("id".equals(ai.getXMLName()) && i.getAnnotation().topLevel()) { return; @@ -251,7 +256,6 @@ boolean isFlowFromWebSocket(ChildElementInfo cei) { } private AbstractSchema processList(ElementInfo i, AbstractSchema so, ChildElementInfo cei, ArrayList sos) { - SchemaObject items = object("items"); if (shouldGenerateParserType(cei)) { @@ -291,7 +295,6 @@ private void addChildsAsProperties(Model m, MainInfo main, ChildElementInfo cei, .ref("#/$defs/" + ei.getXSDTypeName(m))) .description(getDescriptionContent(ei)) .required(cei.isRequired()); - } } diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java index f10b6aa576..82013cbee3 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java @@ -195,4 +195,30 @@ public boolean isRequired() { public void setRequired(boolean required) { this.required = required; } + + /** + * Determines whether the current field or method should be excluded from the JSON schema during schema generation. + * @return {@code true} if the field or method is excluded from the JSON schema; {@code false} otherwise. + */ + public boolean isExcludedFromJsonSchema() { + MCExcludeFromSchema ann = getE().getAnnotation(MCExcludeFromSchema.class); + return ann != null && ann.json(); + } + + + /** + * Determines whether the current field or method should be excluded from the XSD schema during schema generation. + * + * The exclusion is determined based on the presence and configuration of the {@code MCExcludeFromSchema} annotation. + * + * @return {@code true} if the field or method is excluded from the XSD schema; {@code false} otherwise. + * + * Hint: Not implemented yet in XSD Schema Generator + */ + @SuppressWarnings("unused") + public boolean isExcludedFromXsdSchema() { + MCExcludeFromSchema ann = getE().getAnnotation(MCExcludeFromSchema.class); + return ann != null && ann.xsd(); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java index 985695dfdf..6b3729a6a2 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java @@ -51,7 +51,7 @@ public class ReturnInterceptor extends AbstractInterceptor { private static final Logger log = LoggerFactory.getLogger(ReturnInterceptor.class.getName()); - private int statusCode = 200; + private int status = 200; private String contentType = null; @Override @@ -76,9 +76,9 @@ private Response getOrCreateResponse(Exchange exc) throws IOException { response = createResponseFromRequest(exc); } - if (statusCode != 0) { - response.setStatusCode(statusCode); - response.setStatusMessage(getMessageForStatusCode(statusCode)); + if (status != 0) { + response.setStatusCode(status); + response.setStatusMessage(getMessageForStatusCode(status)); } if (contentType!=null) { @@ -92,7 +92,7 @@ private Response getOrCreateResponse(Exchange exc) throws IOException { } private Response createResponseFromRequest(Exchange exc) throws IOException { - Response.ResponseBuilder builder = new Response.ResponseBuilder().status(statusCode); + Response.ResponseBuilder builder = new Response.ResponseBuilder().status(status); String reqContentType = exc.getRequest().getHeader().getContentType(); if (reqContentType != null) { builder.contentType(reqContentType); @@ -112,7 +112,7 @@ public String getDisplayName() { @Override public String getShortDescription() { - return (contentType != null) ? format("Sends a response with a status code of %d and a content type of %s.", statusCode, contentType) : format("Sends a response with a status code of %d.", statusCode); + return (contentType != null) ? format("Sends a response with a status code of %d and a content type of %s.", status, contentType) : format("Sends a response with a status code of %d.", status); } @Override @@ -121,21 +121,38 @@ public EnumSet getAppliedFlow() { } /** + * @deprecated Use status instead. * @description HTTP status code to be returned. * @default 200 * @example 400 */ + @MCExcludeFromSchema(json = true) @MCAttribute public void setStatusCode(int statusCode) { - this.statusCode = statusCode; + // Gets included in JSON schema but not XSD schema. + this.status = statusCode; } public int getStatusCode() { - return statusCode; + return status; } /** - * @description Content type of the response. If not set, the content type of the request (if available) or no content type will be used. + * @description HTTP status code to be returned. + * @default 200 + * @example 400 + */ + @MCAttribute + public void setStatus(int status) { + this.status = status; + } + + public int getStatus() { + return status; + } + + /** + * @description Content-Type of the response. If not set, the content type of the request (if available) or no content type will be used. * @default null * @example application/json; charset=utf-8 */ diff --git a/core/src/test/java/com/predic8/membrane/core/config/spring/k8s/EnvelopeTest.java b/core/src/test/java/com/predic8/membrane/core/config/spring/k8s/EnvelopeTest.java index 919712b1c8..d928776500 100644 --- a/core/src/test/java/com/predic8/membrane/core/config/spring/k8s/EnvelopeTest.java +++ b/core/src/test/java/com/predic8/membrane/core/config/spring/k8s/EnvelopeTest.java @@ -83,7 +83,7 @@ void routerConfConfig() { contentType: application/json src: '{ "ok": 1 }' - return: - statusCode: 200 + status: 200 --- api: port: 2000 diff --git a/distribution/examples/extending-membrane/error-handling/custom-error-messages/apis.yaml b/distribution/examples/extending-membrane/error-handling/custom-error-messages/apis.yaml index d3b21cbb30..3c45cc942b 100644 --- a/distribution/examples/extending-membrane/error-handling/custom-error-messages/apis.yaml +++ b/distribution/examples/extending-membrane/error-handling/custom-error-messages/apis.yaml @@ -104,7 +104,7 @@ api: contentType: text/plain src: Ordinary error! - return: - statusCode: 500 + status: 500 - if: test: param.case == 'd' flow: @@ -115,6 +115,6 @@ api: XML Fehler Meldung vom Backend! - return: - statusCode: 500 + status: 500 # SOAP Service Mock for Debugging - sampleSoapService: {} \ No newline at end of file diff --git a/distribution/examples/extending-membrane/if/apis.yaml b/distribution/examples/extending-membrane/if/apis.yaml index 7cacef4110..24ccf0fa2b 100644 --- a/distribution/examples/extending-membrane/if/apis.yaml +++ b/distribution/examples/extending-membrane/if/apis.yaml @@ -69,4 +69,4 @@ api: - template: src: Success - return: - statusCode: 302 \ No newline at end of file + status: 302 \ No newline at end of file diff --git a/distribution/examples/monitoring-tracing/prometheus/apis.yaml b/distribution/examples/monitoring-tracing/prometheus/apis.yaml index f08a45a002..04981129bc 100644 --- a/distribution/examples/monitoring-tracing/prometheus/apis.yaml +++ b/distribution/examples/monitoring-tracing/prometheus/apis.yaml @@ -11,7 +11,7 @@ api: port: 2001 flow: - return: - statusCode: 200 + status: 200 --- @@ -19,7 +19,7 @@ api: port: 2002 flow: - return: - statusCode: 404 + status: 404 --- @@ -27,4 +27,4 @@ api: port: 2003 flow: - return: - statusCode: 500 \ No newline at end of file + status: 500 \ No newline at end of file diff --git a/distribution/examples/openapi/validation-security/apis.yaml b/distribution/examples/openapi/validation-security/apis.yaml index 66a8e20acf..87c7a2fb45 100644 --- a/distribution/examples/openapi/validation-security/apis.yaml +++ b/distribution/examples/openapi/validation-security/apis.yaml @@ -30,4 +30,4 @@ api: - template: src: Success! - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/examples/openapi/validation-simple/apis.yaml b/distribution/examples/openapi/validation-simple/apis.yaml index e54b3c6234..b6460ad184 100644 --- a/distribution/examples/openapi/validation-simple/apis.yaml +++ b/distribution/examples/openapi/validation-simple/apis.yaml @@ -22,6 +22,6 @@ api: contentType: application/json src: '{ "success": true }' - return: - statusCode: 201 + status: 201 # See examples/openapi-validator for a more detailed example \ No newline at end of file diff --git a/distribution/examples/openapi/validation/apis.yaml b/distribution/examples/openapi/validation/apis.yaml index 46041c0bf7..8ca2621d14 100644 --- a/distribution/examples/openapi/validation/apis.yaml +++ b/distribution/examples/openapi/validation/apis.yaml @@ -30,7 +30,7 @@ api: "email": "foo@baz.org" } - return: - statusCode: 201 + status: 201 --- @@ -53,7 +53,7 @@ api: } ] } - return: - statusCode: 200 + status: 200 --- # Monitoring endpoint for prometheus diff --git a/distribution/examples/orchestration/call-authentication/apis.yaml b/distribution/examples/orchestration/call-authentication/apis.yaml index 73f7ffe0e2..f16ab04cd9 100644 --- a/distribution/examples/orchestration/call-authentication/apis.yaml +++ b/distribution/examples/orchestration/call-authentication/apis.yaml @@ -46,4 +46,4 @@ api: - static: src: Please log in! - return: - statusCode: 401 \ No newline at end of file + status: 401 \ No newline at end of file diff --git a/distribution/examples/routing-traffic/shadowing/apis.yaml b/distribution/examples/routing-traffic/shadowing/apis.yaml index 8c2253bb7f..f6fe024ad4 100644 --- a/distribution/examples/routing-traffic/shadowing/apis.yaml +++ b/distribution/examples/routing-traffic/shadowing/apis.yaml @@ -26,7 +26,7 @@ api: flow: - log: {} - return: - statusCode: 200 + status 200 --- @@ -35,7 +35,7 @@ api: flow: - log: {} - return: - statusCode: 201 + status: 201 --- @@ -44,4 +44,4 @@ api: flow: - log: {} - return: - statusCode: 202 \ No newline at end of file + status: 202 \ No newline at end of file diff --git a/distribution/examples/security/ssl-tls/api-with-tls-pem/apis.yaml b/distribution/examples/security/ssl-tls/api-with-tls-pem/apis.yaml index d12d8358c6..4b5cb52d3d 100644 --- a/distribution/examples/security/ssl-tls/api-with-tls-pem/apis.yaml +++ b/distribution/examples/security/ssl-tls/api-with-tls-pem/apis.yaml @@ -30,4 +30,4 @@ api: "success": true } - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/examples/security/ssl-tls/api-with-tls-pkcs12/apis.yaml b/distribution/examples/security/ssl-tls/api-with-tls-pkcs12/apis.yaml index 62bddc8350..73ef16e8e3 100644 --- a/distribution/examples/security/ssl-tls/api-with-tls-pkcs12/apis.yaml +++ b/distribution/examples/security/ssl-tls/api-with-tls-pkcs12/apis.yaml @@ -31,4 +31,4 @@ api: "success": true } - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/examples/templating/json/apis.yaml b/distribution/examples/templating/json/apis.yaml index 55c7f33655..0f4a65c290 100644 --- a/distribution/examples/templating/json/apis.yaml +++ b/distribution/examples/templating/json/apis.yaml @@ -15,7 +15,7 @@ api: } # To forward to backend use target below instead of return - return: - statusCode: 200 + status: 200 # target: # host: YourBackendHost # port: YourBackendPort diff --git a/distribution/examples/templating/xml/apis.yaml b/distribution/examples/templating/xml/apis.yaml index 3354558e0d..3d759062ca 100644 --- a/distribution/examples/templating/xml/apis.yaml +++ b/distribution/examples/templating/xml/apis.yaml @@ -12,7 +12,7 @@ api: src: Buenas Noches, ${property.fn}sito! # To forward to backend use target below instead of return - return: - statusCode: 200 + status: 200 contentType: text/plain # target: # host: YourBackendHost @@ -27,4 +27,4 @@ api: - template: location: template.xml - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/advanced/10-PathParameters.yaml b/distribution/tutorials/advanced/10-PathParameters.yaml index e7a9e0f4f6..fa5d08136d 100644 --- a/distribution/tutorials/advanced/10-PathParameters.yaml +++ b/distribution/tutorials/advanced/10-PathParameters.yaml @@ -23,4 +23,4 @@ api: Customer: ${pathParam.id} Account: ${pathParam.accountNumber} - return: - statusCode: 200 + status: 200 diff --git a/distribution/tutorials/advanced/60-if.yaml b/distribution/tutorials/advanced/60-if.yaml index 4aea89219b..90e1473af5 100644 --- a/distribution/tutorials/advanced/60-if.yaml +++ b/distribution/tutorials/advanced/60-if.yaml @@ -19,7 +19,7 @@ api: - static: src: Please set X-Id header - return: - statusCode: 400 + status: 400 - if: test: path.startsWith("/foo") language: groovy diff --git a/distribution/tutorials/advanced/70-Scripting-Groovy.yaml b/distribution/tutorials/advanced/70-Scripting-Groovy.yaml index bb12ab4b36..2c944f2002 100644 --- a/distribution/tutorials/advanced/70-Scripting-Groovy.yaml +++ b/distribution/tutorials/advanced/70-Scripting-Groovy.yaml @@ -17,7 +17,7 @@ api: src: | println "I'm executed in the ${flow} flow" - return: - statusCode: 200 + status: 200 --- api: diff --git a/distribution/tutorials/getting-started/50-Short-Circuit.yaml b/distribution/tutorials/getting-started/50-Short-Circuit.yaml index 8ca88bf0c0..3fefc766dc 100644 --- a/distribution/tutorials/getting-started/50-Short-Circuit.yaml +++ b/distribution/tutorials/getting-started/50-Short-Circuit.yaml @@ -28,4 +28,4 @@ api: # Reverses the flow. # If there is no response yet it will copy the body from the request. - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/getting-started/60-SetHeader.yaml b/distribution/tutorials/getting-started/60-SetHeader.yaml index 758ff3c828..4b4910801b 100644 --- a/distribution/tutorials/getting-started/60-SetHeader.yaml +++ b/distribution/tutorials/getting-started/60-SetHeader.yaml @@ -22,4 +22,4 @@ api: name: X-Method value: ${method} # Variable or computed value - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/getting-started/70-Template.yaml b/distribution/tutorials/getting-started/70-Template.yaml index 3843516a6f..4b03fd320d 100644 --- a/distribution/tutorials/getting-started/70-Template.yaml +++ b/distribution/tutorials/getting-started/70-Template.yaml @@ -25,4 +25,4 @@ api: Path: ${path}, Date: ${new Date()} - return: - statusCode: 200 + status: 200 diff --git a/distribution/tutorials/json/20-JSONPath.yaml b/distribution/tutorials/json/20-JSONPath.yaml index 54865e7fa1..ba9e080c15 100644 --- a/distribution/tutorials/json/20-JSONPath.yaml +++ b/distribution/tutorials/json/20-JSONPath.yaml @@ -51,4 +51,4 @@ api: # Log the query parameter message: "First: ${params.animal}" - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/json/60-Json-Transformation.yaml b/distribution/tutorials/json/60-Json-Transformation.yaml index 301dc49225..aba4c40c44 100644 --- a/distribution/tutorials/json/60-Json-Transformation.yaml +++ b/distribution/tutorials/json/60-Json-Transformation.yaml @@ -51,4 +51,4 @@ api: ] } - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/xml/10-JSON-to-XML.yaml b/distribution/tutorials/xml/10-JSON-to-XML.yaml index aebe8f280a..c0404c5ae6 100644 --- a/distribution/tutorials/xml/10-JSON-to-XML.yaml +++ b/distribution/tutorials/xml/10-JSON-to-XML.yaml @@ -19,4 +19,4 @@ api: root: zoo # XML root element - beautifier: {} # Format - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/xml/15-XML-to-JSON.yaml b/distribution/tutorials/xml/15-XML-to-JSON.yaml index ce95254e35..c6d7f6e2ce 100644 --- a/distribution/tutorials/xml/15-XML-to-JSON.yaml +++ b/distribution/tutorials/xml/15-XML-to-JSON.yaml @@ -18,4 +18,4 @@ api: - xml2Json: {} - beautifier: {} # Format - return: - statusCode: 200 \ No newline at end of file + status: 200 \ No newline at end of file diff --git a/distribution/tutorials/xml/20-XPath.yaml b/distribution/tutorials/xml/20-XPath.yaml index fe16123564..13f923154a 100644 --- a/distribution/tutorials/xml/20-XPath.yaml +++ b/distribution/tutorials/xml/20-XPath.yaml @@ -50,5 +50,5 @@ api: - log: message: Dog found! - return: - statusCode: 200 + status: 200 contentType: application/xml \ No newline at end of file From edb472e5ebb59276c1b650bed8f0f90f61f7ca24 Mon Sep 17 00:00:00 2001 From: Thomas Bayer Date: Sat, 6 Dec 2025 18:10:51 +0100 Subject: [PATCH 2/5] refactor: remove `MCExcludeFromSchema` annotation and migrate exclusion logic to `MCAttribute` --- .../predic8/membrane/annot/MCAttribute.java | 15 ++++++- .../membrane/annot/MCExcludeFromSchema.java | 39 ------------------- .../membrane/annot/model/AttributeInfo.java | 8 ++-- .../interceptor/flow/ReturnInterceptor.java | 10 ++--- .../routing-traffic/shadowing/apis.yaml | 2 +- 5 files changed, 23 insertions(+), 51 deletions(-) delete mode 100644 annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java diff --git a/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java b/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java index 5c95c5f770..53865d6244 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java +++ b/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java @@ -19,11 +19,22 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * Injects an XML attribute using a setter method. + * Annotation for marking a method to represent an attribute in the Membrane configuration. + * Primarily used for defining properties and their representation across different configuration grammars, + * such as JSON Schema or XML Schema (XSD). + * + * Methods annotated with this are typically setters with specific behavior or constraints + * controlled by the annotation's attributes. + * + * Attributes: + * - attributeName: Specifies the name of the attribute. If not defined, a default naming convention might apply. + * - excludeFromJson: Indicates whether the attribute should be excluded from JSON Schema representation (default is false). + * - excludeFromXsd: Indicates whether the attribute should be excluded from XML Schema (XSD) representation (default is false). */ @Target(METHOD) @Retention(RUNTIME) public @interface MCAttribute { - String attributeName() default ""; + boolean excludeFromJson() default false; // excludes from JSON Schema (YAML) + boolean excludeFromXsd() default false; // excludes from XSD (XML configuration) } diff --git a/annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java b/annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java deleted file mode 100644 index e78253e6df..0000000000 --- a/annot/src/main/java/com/predic8/membrane/annot/MCExcludeFromSchema.java +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2025 predic8 GmbH, www.predic8.com - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ - -package com.predic8.membrane.annot; - -import java.lang.annotation.*; - -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - -/** - * Annotation to exclude specific fields from being included in schemas - * during schema generation for different configuration formats. - * - * This annotation can be applied to fields to specify whether they should - * be excluded from JSON Schema (YAML) or XSD (XML configuration). - * - * - Setting `json` to true excludes the field from JSON Schema. - * - Setting `xsd` to true excludes the field from XSD. - */ -@Retention(RUNTIME) -@Target(METHOD) -public @interface MCExcludeFromSchema { - boolean json() default false; // excludes from JSON Schema (YAML) - boolean xsd() default false; // excludes from XSD (XML configuration) -} - - diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java index 82013cbee3..74458d4ed6 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java @@ -201,8 +201,8 @@ public void setRequired(boolean required) { * @return {@code true} if the field or method is excluded from the JSON schema; {@code false} otherwise. */ public boolean isExcludedFromJsonSchema() { - MCExcludeFromSchema ann = getE().getAnnotation(MCExcludeFromSchema.class); - return ann != null && ann.json(); + MCAttribute ann = getE().getAnnotation(MCAttribute.class); + return ann != null && ann.excludeFromJson(); } @@ -217,8 +217,8 @@ public boolean isExcludedFromJsonSchema() { */ @SuppressWarnings("unused") public boolean isExcludedFromXsdSchema() { - MCExcludeFromSchema ann = getE().getAnnotation(MCExcludeFromSchema.class); - return ann != null && ann.xsd(); + MCAttribute ann = getE().getAnnotation(MCAttribute.class); + return ann != null && ann.excludeFromXsd(); } } \ No newline at end of file diff --git a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java index 6b3729a6a2..830dc5e77f 100644 --- a/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java +++ b/core/src/main/java/com/predic8/membrane/core/interceptor/flow/ReturnInterceptor.java @@ -39,7 +39,7 @@ * 2. If there is no response in the exchange, the body and contentType of the request is copied into a new response. *

*

- * The options statusCode and contentType will overwrite the values from the messages. + * The options status and contentType will overwrite the values from the messages. *

*

* This plugin is useful together with the template plugin. See examples/template. @@ -122,14 +122,14 @@ public EnumSet getAppliedFlow() { /** * @deprecated Use status instead. - * @description HTTP status code to be returned. + * @description HTTP status code to be returned. statusCode is kept only for backward compatibility use status instead. * @default 200 * @example 400 */ - @MCExcludeFromSchema(json = true) - @MCAttribute + @Deprecated + @MCAttribute(excludeFromJson = true) public void setStatusCode(int statusCode) { - // Gets included in JSON schema but not XSD schema. + // Is excluded from the JSON schema. But will be included in the XSD schema. this.status = statusCode; } diff --git a/distribution/examples/routing-traffic/shadowing/apis.yaml b/distribution/examples/routing-traffic/shadowing/apis.yaml index f6fe024ad4..89a1047139 100644 --- a/distribution/examples/routing-traffic/shadowing/apis.yaml +++ b/distribution/examples/routing-traffic/shadowing/apis.yaml @@ -26,7 +26,7 @@ api: flow: - log: {} - return: - status 200 + status: 200 --- From c1499d89fa4acb5c87ff5c401276bcda493c8b2e Mon Sep 17 00:00:00 2001 From: Thomas Bayer Date: Sat, 6 Dec 2025 18:28:13 +0100 Subject: [PATCH 3/5] refactor: simplify exclusion method names and streamline annotation handling in `AttributeInfo` and `JsonSchemaGenerator` --- .../annot/generator/JsonSchemaGenerator.java | 2 +- .../predic8/membrane/annot/model/AttributeInfo.java | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java b/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java index b61f8b3725..0e44ad7014 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java +++ b/annot/src/main/java/com/predic8/membrane/annot/generator/JsonSchemaGenerator.java @@ -175,7 +175,7 @@ private void processMCAttributes(ElementInfo i, SchemaObject so) { i.getAis().forEach(ai -> { // skip attributes marked with @MCExcludeFromSchema - if (ai.isExcludedFromJsonSchema()) + if (ai.excludedFromJsonSchema()) return; // hide id only on top-level elements diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java index 74458d4ed6..c0020c33f1 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java @@ -200,25 +200,23 @@ public void setRequired(boolean required) { * Determines whether the current field or method should be excluded from the JSON schema during schema generation. * @return {@code true} if the field or method is excluded from the JSON schema; {@code false} otherwise. */ - public boolean isExcludedFromJsonSchema() { - MCAttribute ann = getE().getAnnotation(MCAttribute.class); - return ann != null && ann.excludeFromJson(); + public boolean excludedFromJsonSchema() { + return annotation != null && annotation.excludeFromJson(); } /** * Determines whether the current field or method should be excluded from the XSD schema during schema generation. * - * The exclusion is determined based on the presence and configuration of the {@code MCExcludeFromSchema} annotation. + * The exclusion is determined based on the configuration of the {@code MCAttribute} annotation. * * @return {@code true} if the field or method is excluded from the XSD schema; {@code false} otherwise. * * Hint: Not implemented yet in XSD Schema Generator */ @SuppressWarnings("unused") - public boolean isExcludedFromXsdSchema() { - MCAttribute ann = getE().getAnnotation(MCAttribute.class); - return ann != null && ann.excludeFromXsd(); + public boolean excludedFromXsdSchema() { + return annotation != null && annotation.excludeFromXsd(); } } \ No newline at end of file From 195407ec6d134b26241b66b92401e181dc2ecb26 Mon Sep 17 00:00:00 2001 From: Thomas Bayer Date: Sat, 6 Dec 2025 18:29:36 +0100 Subject: [PATCH 4/5] refactor: remove redundant Javadoc tags in `AttributeInfo` methods --- .../java/com/predic8/membrane/annot/model/AttributeInfo.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java index c0020c33f1..afdca80f1f 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java @@ -75,7 +75,6 @@ public String getSchemaType(Types typeUtils) { /** * It is not checked how many values the enum has. There are enums link validateRequests of OpenAPIValidator * that have more than 2 values but they are also booleans at the configuration level - * @return */ private boolean isEnumBoolean(Types typeUtils) { return getEnumValues(typeUtils).contains("TRUE") && getEnumValues(typeUtils).contains("FALSE"); @@ -98,7 +97,6 @@ public boolean isBeanReference(Types typeUtils) { /** * Sets as side effect instance variables e.g. xsdType, isEnum, isBeanReference. - * @param typeUtils */ private void analyze(Types typeUtils) { if (xsdType != null) // already analyzed? From 9be78b8e6f709e1d64ea02713ff2d788fd10b9d8 Mon Sep 17 00:00:00 2001 From: Tobias Polley Date: Mon, 8 Dec 2025 13:34:34 +0100 Subject: [PATCH 5/5] expose 'excludeFromJson' and 'deprecated' to help reference --- .../com/predic8/membrane/annot/MCAttribute.java | 2 -- .../membrane/annot/generator/HelpReference.java | 4 ++++ .../annot/model/AbstractJavadocedInfo.java | 12 +++++++++++- .../membrane/annot/model/AttributeInfo.java | 15 --------------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java b/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java index 53865d6244..0eecbb64e4 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java +++ b/annot/src/main/java/com/predic8/membrane/annot/MCAttribute.java @@ -29,12 +29,10 @@ * Attributes: * - attributeName: Specifies the name of the attribute. If not defined, a default naming convention might apply. * - excludeFromJson: Indicates whether the attribute should be excluded from JSON Schema representation (default is false). - * - excludeFromXsd: Indicates whether the attribute should be excluded from XML Schema (XSD) representation (default is false). */ @Target(METHOD) @Retention(RUNTIME) public @interface MCAttribute { String attributeName() default ""; boolean excludeFromJson() default false; // excludes from JSON Schema (YAML) - boolean excludeFromXsd() default false; // excludes from XSD (XML configuration) } diff --git a/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java b/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java index d08ebeb433..3bb6042a78 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java +++ b/annot/src/main/java/com/predic8/membrane/annot/generator/HelpReference.java @@ -123,6 +123,7 @@ private void handle(Model m, MainInfo main, ElementInfo ei) throws XMLStreamExce xew.writeAttribute("mixed", "true"); xew.writeAttribute("topLevel", Boolean.toString(ei.getAnnotation().topLevel())); xew.writeAttribute("id", ei.getId()); + xew.writeAttribute("deprecated", Boolean.toString(ei.isDeprecated())); if (!ei.getAnnotation().topLevel()) { String primaryParentId = getPrimaryParentId(m, main, ei); if (primaryParentId != null) @@ -198,6 +199,7 @@ private void handle(Model m, MainInfo main, ChildElementInfo cei) throws XMLStre xew.writeStartElement("child"); xew.writeAttribute("min", cei.isRequired() ? "1" : "0"); xew.writeAttribute("max", cei.isList() ? "unbounded" : "1"); + xew.writeAttribute("deprecated", Boolean.toString(cei.isDeprecated())); handleDoc(cei); @@ -228,6 +230,8 @@ private void handle(AttributeInfo ai) throws XMLStreamException { xew.writeStartElement("attribute"); xew.writeAttribute("name", ai.getXMLName()); xew.writeAttribute("required", Boolean.toString(ai.isRequired())); + xew.writeAttribute("excludeFromJson", Boolean.toString(ai.excludedFromJsonSchema())); + xew.writeAttribute("deprecated", Boolean.toString(ai.isDeprecated())); handleDoc(ai); xew.writeEndElement(); } diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/AbstractJavadocedInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/AbstractJavadocedInfo.java index f36cf197a6..d8c5b2865d 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/AbstractJavadocedInfo.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/AbstractJavadocedInfo.java @@ -14,7 +14,9 @@ package com.predic8.membrane.annot.model; import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; import com.predic8.membrane.annot.MCAttribute; import com.predic8.membrane.annot.MCElement; @@ -24,7 +26,8 @@ * Common behavior of Javadoc handling for {@link MCAttribute}, {@link MCElement}, etc. */ public abstract class AbstractJavadocedInfo { - private Element docedE; + public static final String JAVA_LANG_DEPRECATED = "java.lang.Deprecated"; + private Element docedE; private boolean docGenerated; private Doc doc; @@ -50,4 +53,11 @@ public Doc getDoc(ProcessingEnvironment processingEnv) { return doc = new Doc(processingEnv, javadoc, getDocedE()); } + public boolean isDeprecated() { + for (AnnotationMirror am : docedE.getAnnotationMirrors()) + if (((TypeElement) am.getAnnotationType().asElement()).getQualifiedName().toString().equals(JAVA_LANG_DEPRECATED)) + return true; + return false; + } + } diff --git a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java index afdca80f1f..361e1304d6 100644 --- a/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java +++ b/annot/src/main/java/com/predic8/membrane/annot/model/AttributeInfo.java @@ -202,19 +202,4 @@ public boolean excludedFromJsonSchema() { return annotation != null && annotation.excludeFromJson(); } - - /** - * Determines whether the current field or method should be excluded from the XSD schema during schema generation. - * - * The exclusion is determined based on the configuration of the {@code MCAttribute} annotation. - * - * @return {@code true} if the field or method is excluded from the XSD schema; {@code false} otherwise. - * - * Hint: Not implemented yet in XSD Schema Generator - */ - @SuppressWarnings("unused") - public boolean excludedFromXsdSchema() { - return annotation != null && annotation.excludeFromXsd(); - } - } \ No newline at end of file