From ca8dc5253b9180b7ad81a3fda3fde166c735a35b Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Thu, 28 Dec 2023 20:57:11 +0100 Subject: [PATCH] chore: Improved AsyncAPI interface Improved the creation of Messages. A 'Message' is now an interface that's implemented by 'MessageObject' and 'MessageReference'. This change allows to properly define a 'Message' and use it in a more transparent way. Added also different builders --- .../asyncapi/v3/model/AsyncAPI.java | 4 +- .../v3/model/channel/message/Message.java | 119 +---- .../model/channel/message/MessageObject.java | 121 +++++ .../channel/message/MessageReference.java | 33 +- .../v3/model/schema/MultiFormatSchema.java | 5 +- .../asyncapi/v3/model/schema/Schema.java | 21 + .../v3/model/schema/SchemaFormat.java | 31 ++ .../v3/bindings/amqp/AMQPBindingTest.java | 4 +- .../googlepubsub/GooglePubSubBindingTest.java | 6 +- .../v3/bindings/jms/JMSBindingTest.java | 4 +- .../v3/bindings/kafka/KafkaBindingTest.java | 4 +- .../asyncapi/v3/model/AsyncAPITest.java | 168 +++---- .../v3/model/channel/ChannelTest.java | 10 +- .../v3/model/channel/MessageTest.java | 4 +- .../v3/model/operation/OperationTest.java | 8 +- .../asyncapi/v3/model/schema/SchemaTest.java | 441 ++++++++++++++++++ .../v3/model/operation/operation.json | 4 +- 17 files changed, 740 insertions(+), 247 deletions(-) create mode 100644 springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageObject.java create mode 100644 springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaFormat.java create mode 100644 springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaTest.java diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPI.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPI.java index de5ea9708..ac8fba487 100644 --- a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPI.java +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPI.java @@ -29,6 +29,8 @@ @EqualsAndHashCode(callSuper = true) public class AsyncAPI extends ExtendableObject { + public static final String ASYNCAPI_DEFAULT_VERSION = "3.0.0"; + /** * REQUIRED. Specifies the AsyncAPI Specification version being used. It can be used by tooling Specifications and * clients to interpret the version. The structure shall be major.minor.patch, where patch versions must be @@ -39,7 +41,7 @@ public class AsyncAPI extends ExtendableObject { @NotNull @Builder.Default @JsonProperty(value = "asyncapi") - private String asyncapi = "3.0.0"; + private String asyncapi = ASYNCAPI_DEFAULT_VERSION; /** * Identifier of the diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/Message.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/Message.java index 8f0bac5a8..7283d3ee8 100644 --- a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/Message.java +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/Message.java @@ -1,122 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding; -import io.github.stavshamir.springwolf.asyncapi.v3.model.ExtendableObject; -import io.github.stavshamir.springwolf.asyncapi.v3.model.ExternalDocumentation; -import io.github.stavshamir.springwolf.asyncapi.v3.model.Reference; -import io.github.stavshamir.springwolf.asyncapi.v3.model.Tag; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.CorrelationID; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; - -import java.util.List; -import java.util.Map; - /** - * Describes a message received on a given channel and operation. + * Represents a Message. A Message can be a MessageObject or a MessageReference. */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class Message extends ExtendableObject implements Reference { - - /** - * Schema definition of the application headers. Schema MUST be a map of key-value pairs. It MUST NOT define the - * protocol headers. If this is a Schema Object, then the schemaFormat will be assumed to be - * "application/vnd.aai.asyncapi+json;version=asyncapi" where the version is equal to the AsyncAPI Version String. - */ - @JsonProperty(value = "headers") - private MessageHeaders headers; - - /** - * Definition of the message payload. If this is a Schema Object, then the schemaFormat will be assumed to be - * "application/vnd.aai.asyncapi+json;version=asyncapi" where the version is equal to the AsyncAPI Version String. - */ - @JsonProperty(value = "payload") - private MessagePayload payload; - - /** - * Definition of the correlation ID used for message tracing or matching. - */ - @JsonProperty(value = "correlationId") - private CorrelationID correlationId; - - /** - * The content type to use when encoding/decoding a message's payload. The value MUST be a specific media type - * (e.g. application/json). When omitted, the value MUST be the one specified on the defaultContentType field. - */ - @JsonProperty(value = "contentType") - private String contentType; - - /** - * A machine-friendly name for the message. - */ - @JsonProperty(value = "name") - private String name; - - /** - * A human-friendly title for the message. - */ - @JsonProperty(value = "title") - private String title; - - /** - * A short summary of what the message is about. - */ - @JsonProperty(value = "summary") - private String summary; - - /** - * A verbose explanation of the message. CommonMark syntax can be used - * for rich text representation. - */ - @JsonProperty(value = "description") - private String description; - - /** - * A list of tags for logical grouping and categorization of messages. - */ - @JsonProperty(value = "tags") - private List tags; - - /** - * Additional external documentation for this message. - */ - @JsonProperty(value = "externalDocs") - private ExternalDocumentation externalDocs; - - /** - * A map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the message. - */ - @JsonProperty(value = "bindings") - private Map bindings; - - /** - * List of examples. - */ - @JsonProperty(value = "examples") - private List examples; - - /** - * A list of traits to apply to the message object. Traits MUST be merged using traits merge mechanism. - * The resulting object MUST be a valid Message Object. - */ - @JsonProperty(value = "traits") - private List traits; - - @JsonIgnore - private String ref; - - @Override - public String getRef() { - return ref; - } -} +public interface Message {} diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageObject.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageObject.java new file mode 100644 index 000000000..b50246464 --- /dev/null +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageObject.java @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding; +import io.github.stavshamir.springwolf.asyncapi.v3.model.ExtendableObject; +import io.github.stavshamir.springwolf.asyncapi.v3.model.ExternalDocumentation; +import io.github.stavshamir.springwolf.asyncapi.v3.model.Tag; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.CorrelationID; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * Describes a message received on a given channel and operation. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class MessageObject extends ExtendableObject implements Message { + + /** + * The key represents the message identifier. The messageId value is case-sensitive. Tools and libraries MAY use + * the messageId value to uniquely identify a message, therefore, it is RECOMMENDED to follow common programming + * naming conventions. + */ + @JsonIgnore + private String messageId; + + /** + * Schema definition of the application headers. Schema MUST be a map of key-value pairs. It MUST NOT define the + * protocol headers. If this is a Schema Object, then the schemaFormat will be assumed to be + * "application/vnd.aai.asyncapi+json;version=asyncapi" where the version is equal to the AsyncAPI Version String. + */ + @JsonProperty(value = "headers") + private MessageHeaders headers; + + /** + * Definition of the message payload. If this is a Schema Object, then the schemaFormat will be assumed to be + * "application/vnd.aai.asyncapi+json;version=asyncapi" where the version is equal to the AsyncAPI Version String. + */ + @JsonProperty(value = "payload") + private MessagePayload payload; + + /** + * Definition of the correlation ID used for message tracing or matching. + */ + @JsonProperty(value = "correlationId") + private CorrelationID correlationId; + + /** + * The content type to use when encoding/decoding a message's payload. The value MUST be a specific media type + * (e.g. application/json). When omitted, the value MUST be the one specified on the defaultContentType field. + */ + @JsonProperty(value = "contentType") + private String contentType; + + /** + * A machine-friendly name for the message. + */ + @JsonProperty(value = "name") + private String name; + + /** + * A human-friendly title for the message. + */ + @JsonProperty(value = "title") + private String title; + + /** + * A short summary of what the message is about. + */ + @JsonProperty(value = "summary") + private String summary; + + /** + * A verbose explanation of the message. CommonMark syntax can be used + * for rich text representation. + */ + @JsonProperty(value = "description") + private String description; + + /** + * A list of tags for logical grouping and categorization of messages. + */ + @JsonProperty(value = "tags") + private List tags; + + /** + * Additional external documentation for this message. + */ + @JsonProperty(value = "externalDocs") + private ExternalDocumentation externalDocs; + + /** + * A map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the message. + */ + @JsonProperty(value = "bindings") + private Map bindings; + + /** + * List of examples. + */ + @JsonProperty(value = "examples") + private List examples; + + /** + * A list of traits to apply to the message object. Traits MUST be merged using traits merge mechanism. + * The resulting object MUST be a valid Message Object. + */ + @JsonProperty(value = "traits") + private List traits; +} diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageReference.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageReference.java index fa996dcf3..e4e299b7b 100644 --- a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageReference.java +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/message/MessageReference.java @@ -4,15 +4,17 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import io.github.stavshamir.springwolf.asyncapi.v3.model.Reference; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; -@Data -@Builder +@EqualsAndHashCode @NoArgsConstructor @AllArgsConstructor -public class MessageReference implements Reference { +public class MessageReference implements Message, Reference { + + @EqualsAndHashCode.Include + @ToString.Include @JsonIgnore private String ref; @@ -20,4 +22,25 @@ public class MessageReference implements Reference { public String getRef() { return ref; } + + /** + * Convenient Builder to create a Message reference to an existing Message + * @param message Message to create the reference to. This Message MUST have a 'messageId' field + * @return a Message with the 'ref' field pointing to "#/components/messages/{messageId" + */ + public static MessageReference fromMessage(MessageObject message) { + var messageId = message.getMessageId(); + if (messageId == null) { + throw new IllegalArgumentException("The message must have a 'messageId' defined"); + } + return new MessageReference("#/components/messages/" + messageId); + } + + public static MessageReference fromMessage(String messageName) { + return new MessageReference("#/components/messages/" + messageName); + } + + public static MessageReference fromSchema(String schemaName) { + return new MessageReference("#/components/schemas/" + schemaName); + } } diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java index 0a9c927cf..292e1a694 100644 --- a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.github.stavshamir.springwolf.asyncapi.v3.model.ExtendableObject; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -35,8 +36,10 @@ public class MultiFormatSchema extends ExtendableObject { * When using Reference Objects within the schema, the schemaFormat of the referenced resource MUST match the * schemaFormat of the schema containing the reference. */ + @Builder.Default + @NotNull @JsonProperty(value = "schemaFormat") - private String schemaFormat; + private String schemaFormat = SchemaFormat.DEFAULT.toString(); /** * Required. Definition of the message payload. It can be of any type but defaults to Schema Object. It MUST match diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/Schema.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/Schema.java index 32d5b813d..85e85cc72 100644 --- a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/Schema.java +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/Schema.java @@ -84,6 +84,27 @@ public class Schema extends ExtendableObject implements Reference { @JsonProperty("enum") private List enumValues; + @JsonProperty(value = "examples") + public List examples; + + @JsonProperty(value = "additionalProperties") + public Schema additionalProperties; + + @JsonProperty(value = "required") + public List required; + + @JsonProperty(value = "allOf") + public List allOf; + + @JsonProperty(value = "oneOf") + public List oneOf; + + @JsonProperty(value = "anyOf") + public List anyOf; + + @JsonProperty(value = "const") + public Object constValue; + @JsonIgnore private String ref; diff --git a/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaFormat.java b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaFormat.java new file mode 100644 index 000000000..40e79008c --- /dev/null +++ b/springwolf-asyncapi/src/main/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaFormat.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.stavshamir.springwolf.asyncapi.v3.model.schema; + +import static io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI.ASYNCAPI_DEFAULT_VERSION; + +public enum SchemaFormat { + /** + * This is the default when a schemaFormat is not provided. + */ + DEFAULT("application/vnd.aai.asyncapi+json;version=" + ASYNCAPI_DEFAULT_VERSION), + ASYNCAPI_V3("application/vnd.aai.asyncapi;version=" + ASYNCAPI_DEFAULT_VERSION), + ASYNCAPI_V3_JSON("application/vnd.aai.asyncapi+json;version=" + ASYNCAPI_DEFAULT_VERSION), + ASYNCAPI_V3_YAML("application/vnd.aai.asyncapi+yaml;version=" + ASYNCAPI_DEFAULT_VERSION), + JSON_SCHEMA_JSON("application/schema+json;version=draft-07"), + JSON_SCHEMA_YAML("application/schema+yaml;version=draft-07"), + AVRO_V1_9_0("application/vnd.apache.avro;version=1.9.0"), + RAML_V1("application/raml+yaml;version=1.0"), + PROTOBUF_V2("application/vnd.google.protobuf;version=2"), + PROTOBUF_V3("application/vnd.google.protobuf;version=3"); + + public final String value; + + private SchemaFormat(String schemaFormat) { + this.value = schemaFormat; + } + + @Override + public String toString() { + return this.value; + } +} diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/amqp/AMQPBindingTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/amqp/AMQPBindingTest.java index 0d4d4fc3b..2c9c66424 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/amqp/AMQPBindingTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/amqp/AMQPBindingTest.java @@ -6,7 +6,7 @@ import io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation; import org.junit.jupiter.api.Test; @@ -96,7 +96,7 @@ void shouldSerializeAMQPMessageBinding() throws IOException { .address("user/signup") .messages(Map.of( "userSignupMessage", - Message.builder() + MessageObject.builder() .bindings(Map.of( "amqp", AMQPMessageBinding.builder() diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/googlepubsub/GooglePubSubBindingTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/googlepubsub/GooglePubSubBindingTest.java index 9830fa523..a0fc88ae5 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/googlepubsub/GooglePubSubBindingTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/googlepubsub/GooglePubSubBindingTest.java @@ -5,7 +5,7 @@ import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer; import io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload; import io.github.stavshamir.springwolf.asyncapi.v3.model.components.Components; import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; @@ -26,7 +26,7 @@ void shouldSerializeGooglePubSubMessage() throws IOException { .components(Components.builder() .messages(Map.of( "messageAvro", - Message.builder() + MessageObject.builder() .bindings(Map.of( "googlepubsub", GooglePubSubMessageBinding.builder() @@ -48,7 +48,7 @@ void shouldSerializeGooglePubSubMessage() throws IOException { .build())) .build(), "messageProto", - Message.builder() + MessageObject.builder() .bindings(Map.of( "googlepubsub", GooglePubSubMessageBinding.builder() diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/jms/JMSBindingTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/jms/JMSBindingTest.java index 62840c0cf..76f692b6f 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/jms/JMSBindingTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/jms/JMSBindingTest.java @@ -5,8 +5,8 @@ import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer; import io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.Schema; import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server; import org.junit.jupiter.api.Disabled; @@ -79,7 +79,7 @@ void shouldSerializeJMSServerBinding() throws IOException { void shouldSerializeJMSMessageBinding() throws IOException { var message = Map.of( "message", - Message.builder() + MessageObject.builder() .bindings(Map.of( "jms", JMSMessageBinding.builder() diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/kafka/KafkaBindingTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/kafka/KafkaBindingTest.java index 8f93c3eaa..84b37d712 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/kafka/KafkaBindingTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/bindings/kafka/KafkaBindingTest.java @@ -5,7 +5,7 @@ import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer; import io.github.stavshamir.springwolf.asyncapi.v3.model.AsyncAPI; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation; import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction; import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.Schema; @@ -130,7 +130,7 @@ void shouldSerializeKafkaMessage() throws IOException { .address("test-topic") .messages(Map.of( "testMessage", - Message.builder() + MessageObject.builder() .bindings(Map.of( "kafka", KafkaMessageBinding.builder() diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPITest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPITest.java index b1c7e5a5c..06a53ba6b 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPITest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/AsyncAPITest.java @@ -7,8 +7,8 @@ import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelParameter; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageTrait; @@ -36,6 +36,25 @@ class AsyncAPITest { @Test void shouldCreateSimpleAsyncAPI() throws IOException { + var userSignUpMessage = MessageObject.builder() + .messageId("UserSignedUp") + .payload(MessagePayload.of(Schema.builder() + .type("object") + .properties(Map.of( + "displayName", + Schema.builder() + .type("string") + .description("Name of the user") + .build(), + "email", + Schema.builder() + .type("string") + .format("email") + .description("Email of the user") + .build())) + .build())) + .build(); + AsyncAPI asyncAPI = AsyncAPI.builder() .info(Info.builder() .title("Account Service") @@ -47,10 +66,8 @@ void shouldCreateSimpleAsyncAPI() throws IOException { Channel.builder() .address("user/signedup") .messages(Map.of( - "UserSignedUp", - Message.builder() - .ref("#/components/messages/UserSignedUp") - .build())) + userSignUpMessage.getMessageId(), + MessageReference.fromMessage(userSignUpMessage))) .build())) .operations(Map.of( "sendUserSignedup", @@ -59,30 +76,11 @@ void shouldCreateSimpleAsyncAPI() throws IOException { .channel(ChannelReference.builder() .ref("#/channels/userSignedup") .build()) - .messages(List.of(MessageReference.builder() - .ref("#/channels/userSignedup/messages/UserSignedUp") - .build())) + .messages( + List.of(new MessageReference("#/channels/userSignedup/messages/UserSignedUp"))) .build())) .components(Components.builder() - .messages(Map.of( - "UserSignedUp", - Message.builder() - .payload(MessagePayload.of(Schema.builder() - .type("object") - .properties(Map.of( - "displayName", - Schema.builder() - .type("string") - .description("Name of the user") - .build(), - "email", - Schema.builder() - .type("string") - .format("email") - .description("Email of the user") - .build())) - .build())) - .build())) + .messages(Map.of(userSignUpMessage.getMessageId(), userSignUpMessage)) .build()) .build(); @@ -93,6 +91,40 @@ void shouldCreateSimpleAsyncAPI() throws IOException { @Test void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { + var lightMeasuredMessage = MessageObject.builder() + .messageId("lightMeasured") + .name("lightMeasured") + .title("Light measured") + .summary("Inform about environmental lighting conditions of a particular streetlight.") + .contentType("application/json") + .traits(List.of(MessageTrait.builder() + .ref("#/components/messageTraits/commonHeaders") + .build())) + .payload(MessagePayload.of(MessageReference.fromSchema("lightMeasuredPayload"))) + .build(); + + var turnOnOffMessage = MessageObject.builder() + .messageId("turnOnOff") + .name("turnOnOff") + .title("Turn on/off") + .summary("Command a particular streetlight to turn the lights on or off.") + .traits(List.of(MessageTrait.builder() + .ref("#/components/messageTraits/commonHeaders") + .build())) + .payload(MessagePayload.of(MessageReference.fromSchema("turnOnOffPayload"))) + .build(); + + var dimLightMessage = MessageObject.builder() + .messageId("dimLight") + .name("dimLight") + .title("Dim light") + .summary("Command a particular streetlight to dim the lights.") + .traits(List.of(MessageTrait.builder() + .ref("#/components/messageTraits/commonHeaders") + .build())) + .payload(MessagePayload.of(MessageReference.fromSchema("dimLightPayload"))) + .build(); + AsyncAPI asyncAPI = AsyncAPI.builder() .info(Info.builder() .title("Streetlights Kafka API") @@ -160,11 +192,7 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { "lightingMeasured", Channel.builder() .address("smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured") - .messages(Map.of( - "lightMeasured", - Message.builder() - .ref("#/components/messages/lightMeasured") - .build())) + .messages(Map.of("lightMeasured", MessageReference.fromMessage(lightMeasuredMessage))) .description("The topic on which measured values may be produced and consumed.") .parameters(Map.of( "streetlightId", @@ -175,11 +203,7 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { "lightTurnOn", Channel.builder() .address("smartylighting.streetlights.1.0.action.{streetlightId}.turn.on") - .messages(Map.of( - "turnOn", - Message.builder() - .ref("#/components/messages/turnOnOff") - .build())) + .messages(Map.of("turnOn", MessageReference.fromMessage(turnOnOffMessage))) .parameters(Map.of( "streetlightId", ChannelParameter.builder() @@ -189,11 +213,7 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { "lightTurnOff", Channel.builder() .address("smartylighting.streetlights.1.0.action.{streetlightId}.turn.off") - .messages(Map.of( - "turnOff", - Message.builder() - .ref("#/components/messages/turnOnOff") - .build())) + .messages(Map.of("turnOff", MessageReference.fromMessage(turnOnOffMessage))) .parameters(Map.of( "streetlightId", ChannelParameter.builder() @@ -203,11 +223,7 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { "lightsDim", Channel.builder() .address("smartylighting.streetlights.1.0.action.{streetlightId}.dim") - .messages(Map.of( - "dimLight", - Message.builder() - .ref("#/components/messages/dimLight") - .build())) + .messages(Map.of("dimLight", MessageReference.fromMessage(dimLightMessage))) .parameters(Map.of( "streetlightId", ChannelParameter.builder() @@ -225,9 +241,8 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { .traits(List.of(OperationTraits.builder() .ref("#/components/operationTraits/kafka") .build())) - .messages(List.of(MessageReference.builder() - .ref("#/channels/lightingMeasured/messages/lightMeasured") - .build())) + .messages(List.of( + new MessageReference("#/channels/lightingMeasured/messages/lightMeasured"))) .build(), "turnOn", Operation.builder() @@ -238,9 +253,7 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { .traits(List.of(OperationTraits.builder() .ref("#/components/operationTraits/kafka") .build())) - .messages(List.of(MessageReference.builder() - .ref("#/channels/lightTurnOn/messages/turnOn") - .build())) + .messages(List.of(new MessageReference("#/channels/lightTurnOn/messages/turnOn"))) .build(), "turnOff", Operation.builder() @@ -251,9 +264,7 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { .traits(List.of(OperationTraits.builder() .ref("#/components/operationTraits/kafka") .build())) - .messages(List.of(MessageReference.builder() - .ref("#/channels/lightTurnOff/messages/turnOff") - .build())) + .messages(List.of(new MessageReference("#/channels/lightTurnOff/messages/turnOff"))) .build(), "dimLight", Operation.builder() @@ -264,50 +275,13 @@ void shouldCreateStreetlightsKafkaAsyncAPI() throws IOException { .traits(List.of(OperationTraits.builder() .ref("#/components/operationTraits/kafka") .build())) - .messages(List.of(MessageReference.builder() - .ref("#/channels/lightsDim/messages/dimLight") - .build())) + .messages(List.of(new MessageReference("#/channels/lightsDim/messages/dimLight"))) .build())) .components(Components.builder() .messages(Map.of( - "lightMeasured", - Message.builder() - .name("lightMeasured") - .title("Light measured") - .summary( - "Inform about environmental lighting conditions of a particular streetlight.") - .contentType("application/json") - .traits(List.of(MessageTrait.builder() - .ref("#/components/messageTraits/commonHeaders") - .build())) - .payload(MessagePayload.of(MessageReference.builder() - .ref("#/components/schemas/lightMeasuredPayload") - .build())) - .build(), - "turnOnOff", - Message.builder() - .name("turnOnOff") - .title("Turn on/off") - .summary("Command a particular streetlight to turn the lights on or off.") - .traits(List.of(MessageTrait.builder() - .ref("#/components/messageTraits/commonHeaders") - .build())) - .payload(MessagePayload.of(MessageReference.builder() - .ref("#/components/schemas/turnOnOffPayload") - .build())) - .build(), - "dimLight", - Message.builder() - .name("dimLight") - .title("Dim light") - .summary("Command a particular streetlight to dim the lights.") - .traits(List.of(MessageTrait.builder() - .ref("#/components/messageTraits/commonHeaders") - .build())) - .payload(MessagePayload.of(MessageReference.builder() - .ref("#/components/schemas/dimLightPayload") - .build())) - .build())) + lightMeasuredMessage.getMessageId(), lightMeasuredMessage, + turnOnOffMessage.getMessageId(), turnOnOffMessage, + dimLightMessage.getMessageId(), dimLightMessage)) .schemas(Map.of( "lightMeasuredPayload", ComponentSchema.of(Schema.builder() diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/ChannelTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/ChannelTest.java index ef1f18ba7..e312d7c59 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/ChannelTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/ChannelTest.java @@ -8,7 +8,7 @@ import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer; import io.github.stavshamir.springwolf.asyncapi.v3.model.ExternalDocumentation; import io.github.stavshamir.springwolf.asyncapi.v3.model.Tag; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -28,13 +28,9 @@ void shouldSerializeChannelObject() throws IOException { .description("This channel is used to exchange messages about user events.") .messages(Map.of( "userSignedUp", - Message.builder() - .ref("#/components/messages/userSignedUp") - .build(), + MessageReference.fromMessage("userSignedUp"), "userCompletedOrder", - Message.builder() - .ref("#/components/messages/userCompletedOrder") - .build())) + MessageReference.fromMessage("userCompletedOrder"))) .parameters(Map.of( "userId", ChannelParameter.builder() diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/MessageTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/MessageTest.java index 34b8c4816..a0f3044fe 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/MessageTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/channel/MessageTest.java @@ -4,9 +4,9 @@ import io.github.stavshamir.springwolf.asyncapi.v3.ClasspathUtil; import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer; import io.github.stavshamir.springwolf.asyncapi.v3.model.Tag; -import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageExample; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders; +import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload; import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageTrait; import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.Schema; @@ -23,7 +23,7 @@ class MessageTest { @Test void shouldSerializeMessage() throws IOException { - Message message = Message.builder() + MessageObject message = MessageObject.builder() .name("UserSignup") .title("User signup") .summary("Action to sign a user up.") diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/operation/OperationTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/operation/OperationTest.java index 75bb28f3b..20b30b29f 100644 --- a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/operation/OperationTest.java +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/operation/OperationTest.java @@ -37,9 +37,7 @@ void shouldSerializeOperation() throws IOException { .traits(List.of(OperationTraits.builder() .ref("#/components/operationTraits/kafka") .build())) - .messages(List.of(MessageReference.builder() - .ref("/components/messages/userSignedUp") - .build())) + .messages(List.of(MessageReference.fromMessage("userSignedUp"))) .reply(OperationReply.builder() .address(OperationReplyAddress.builder() .location("$message.header#/replyTo") @@ -47,9 +45,7 @@ void shouldSerializeOperation() throws IOException { .channel(ChannelReference.builder() .ref("#/channels/userSignupReply") .build()) - .messages(List.of(MessageReference.builder() - .ref("/components/messages/userSignedUpReply") - .build())) + .messages(List.of(MessageReference.fromMessage("userSignedUpReply"))) .build()) .build(); diff --git a/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaTest.java b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaTest.java new file mode 100644 index 000000000..57f7e4610 --- /dev/null +++ b/springwolf-asyncapi/src/test/java/io/github/stavshamir/springwolf/asyncapi/v3/model/schema/SchemaTest.java @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.stavshamir.springwolf.asyncapi.v3.model.schema; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; + +class SchemaTest { + private static final DefaultAsyncApiSerializer serializer = new DefaultAsyncApiSerializer(); + + @Test + void shouldSerializePrimitiveSample() throws JsonProcessingException { + var schema = Schema.builder().type("string").format("email").build(); + + String example = + """ + { + "type": "string", + "format": "email" + } + """; + assertThatJson(serializer.toJsonString(schema)).isEqualTo(example); + } + + @Test + void shouldSerializeSimpleModel() throws JsonProcessingException { + var schema = Schema.builder() + .type("object") + .required(List.of("name")) + .properties(Map.of( + "name", Schema.builder().type("string").build(), + "address", + Schema.builder() + .ref("#/components/schemas/Address") + .build(), + "age", + Schema.builder() + .type("integer") + .format("int32") + .minimum(0) + .build())) + .build(); + + String example = + """ + { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "address": { + "$ref": "#/components/schemas/Address" + }, + "age": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + } + """; + assertThatJson(serializer.toJsonString(schema)).isEqualTo(example); + } + + @Test + void shouldSerializeStringToStringMapping() throws JsonProcessingException { + var schema = Schema.builder() + .type("object") + .additionalProperties(Schema.builder().type("string").build()) + .build(); + + var example = + """ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + """; + assertThatJson(serializer.toJsonString(schema)).isEqualTo(example); + } + + @Test + void shouldSerializeModelMapping() throws JsonProcessingException { + var schema = Schema.builder() + .type("object") + .additionalProperties(Schema.builder() + .ref("#/components/schemas/ComplexModel") + .build()) + .build(); + + var example = + """ + { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/ComplexModel" + } + } + """; + assertThatJson(serializer.toJsonString(schema)).isEqualTo(example); + } + + @Test + void shouldSerializeModelWithExample() throws JsonProcessingException { + var schema = Schema.builder() + .type("object") + .properties(Map.of( + "id", Schema.builder().type("integer").format("int64").build(), + "name", Schema.builder().type("string").build())) + .required(List.of("name")) + .examples(List.of(Map.of("name", "Puma", "id", 1))) + .build(); + + var example = + """ + { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "examples": [ + { + "name": "Puma", + "id": 1 + } + ] + } + """; + assertThatJson(serializer.toJsonString(schema)).isEqualTo(example); + } + + @Test + @Disabled("MODEL WITH BOOLEAN SCHEMAS is not supported yet") + // FIXME: See https://www.asyncapi.com/docs/reference/specification/v3.0.0#schemaObject + void shouldSerializeModelWithBooleans() throws JsonProcessingException { + var schema = Schema.builder() + .type("object") + // .properties(Map.of( + // "anySchema", true, + // "cannotBeDefined", false)) + .required(List.of("anySchema")) + .build(); + + var example = + """ + { + "type": "object", + "required": [ + "anySchema" + ], + "properties": { + "anySchema": true, + "cannotBeDefined": false + } + } + """; + assertThatJson(serializer.toJsonString(schema)).isEqualTo(example); + } + + @Test + void shouldSerializeModelsWithComposition() throws JsonProcessingException { + var schemas = Map.of( + "schemas", + Map.of( + "ErrorModel", + Schema.builder() + .type("object") + .required(List.of("message", "code")) + .properties(Map.of( + "message", + Schema.builder().type("string").build(), + "code", + Schema.builder() + .type("integer") + .minimum(100) + .maximum(600) + .build())) + .build(), + "ExtendedErrorModel", + Schema.builder() + .allOf(List.of( + Schema.builder() + .ref("#/components/schemas/ErrorModel") + .build(), + Schema.builder() + .type("object") + .required(List.of("rootCause")) + .properties(Map.of( + "rootCause", + Schema.builder() + .type("string") + .build())) + .build())) + .build())); + + var example = + """ + { + "schemas": { + "ErrorModel": { + "type": "object", + "required": [ + "message", + "code" + ], + "properties": { + "message": { + "type": "string" + }, + "code": { + "type": "integer", + "minimum": 100, + "maximum": 600 + } + } + }, + "ExtendedErrorModel": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorModel" + }, + { + "type": "object", + "required": [ + "rootCause" + ], + "properties": { + "rootCause": { + "type": "string" + } + } + } + ] + } + } + } + """; + assertThatJson(serializer.toJsonString(schemas)).isEqualTo(example); + } + + @Test + void shouldSerializeModelsWithPolimorphismSupport() throws JsonProcessingException { + var schemas = Map.of( + "schemas", + Map.of( + "Pet", + Schema.builder() + .type("object") + .discriminator("petType") + .properties(Map.of( + "name", + Schema.builder().type("string").build(), + "petType", + Schema.builder().type("string").build())) + .required(List.of("name", "petType")) + .build(), + "Cat", + Schema.builder() + .description( + "A representation of a cat. Note that `Cat` will be used as the discriminator value.") + .allOf(List.of( + Schema.builder() + .ref("#/components/schemas/Pet") + .build(), + Schema.builder() + .type("object") + .properties(Map.of( + "huntingSkill", + Schema.builder() + .type("string") + .description("The measured skill for hunting") + .enumValues( + List.of( + "clueless", + "lazy", + "adventurous", + "aggressive")) + .build())) + .required(List.of("huntingSkill")) + .build())) + .build(), + "Dog", + Schema.builder() + .description( + "A representation of a dog. Note that `Dog` will be used as the discriminator value.") + .allOf(List.of( + Schema.builder() + .ref("#/components/schemas/Pet") + .build(), + Schema.builder() + .type("object") + .properties(Map.of( + "packSize", + Schema.builder() + .type("integer") + .format("int32") + .description("the size of the pack the dog is from") + .minimum(0) + .build())) + .required(List.of("packSize")) + .build())) + .build(), + "StickInsect", + Schema.builder() + .description( + "A representation of an Australian walking stick. Note that `StickBug` will be used as the discriminator value.") + .allOf(List.of( + Schema.builder() + .ref("#/components/schemas/Pet") + .build(), + Schema.builder() + .type("object") + .properties(Map.of( + "petType", + Schema.builder() + .constValue("StickBug") + .build(), + "color", + Schema.builder() + .type("string") + .build())) + .required(List.of("color")) + .build())) + .build())); + var example = + """ + { + "schemas": { + "Pet": { + "type": "object", + "discriminator": "petType", + "properties": { + "name": { + "type": "string" + }, + "petType": { + "type": "string" + } + }, + "required": [ + "name", + "petType" + ] + }, + "Cat": { + "description": "A representation of a cat. Note that `Cat` will be used as the discriminator value.", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "huntingSkill": { + "type": "string", + "description": "The measured skill for hunting", + "enum": [ + "clueless", + "lazy", + "adventurous", + "aggressive" + ] + } + }, + "required": [ + "huntingSkill" + ] + } + ] + }, + "Dog": { + "description": "A representation of a dog. Note that `Dog` will be used as the discriminator value.", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "packSize": { + "type": "integer", + "format": "int32", + "description": "the size of the pack the dog is from", + "minimum": 0 + } + }, + "required": [ + "packSize" + ] + } + ] + }, + "StickInsect": { + "description": "A representation of an Australian walking stick. Note that `StickBug` will be used as the discriminator value.", + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "petType": { + "const": "StickBug" + }, + "color": { + "type": "string" + } + }, + "required": [ + "color" + ] + } + ] + } + } + } + """; + assertThatJson(serializer.toJsonString(schemas)).isEqualTo(example); + } +} diff --git a/springwolf-asyncapi/src/test/resources/v3/model/operation/operation.json b/springwolf-asyncapi/src/test/resources/v3/model/operation/operation.json index 456e0c59e..3c578d71b 100644 --- a/springwolf-asyncapi/src/test/resources/v3/model/operation/operation.json +++ b/springwolf-asyncapi/src/test/resources/v3/model/operation/operation.json @@ -37,7 +37,7 @@ ], "messages": [ { - "$ref": "/components/messages/userSignedUp" + "$ref": "#/components/messages/userSignedUp" } ], "reply": { @@ -49,7 +49,7 @@ }, "messages": [ { - "$ref": "/components/messages/userSignedUpReply" + "$ref": "#/components/messages/userSignedUpReply" } ] }