From ef7c133852f927888108a6682d269ca938a77bf5 Mon Sep 17 00:00:00 2001 From: arielmirra Date: Wed, 7 Feb 2024 17:01:34 -0300 Subject: [PATCH] W-12689952: add ibmmq message binding in async21+ - add message binding model, parser and emitter - add unit validation and cycle tests - add model to APIEntities and model.md --- .../bindings/ibmmq/IBMMQMessageBinding.scala | 51 +++++++++++++++ .../bindings/ibmmq/IBMMQMessageBinding.scala | 49 +++++++++++++++ .../internal/convert/ApiBaseConverter.scala | 11 ++++ .../internal/convert/ApiRegister.scala | 4 ++ .../internal/entities/APIEntities.scala | 3 +- .../domain/bindings/IBMMQBindingModel.scala | 62 +++++++++++++++++++ .../AsyncApiMessageBindingsEmitter.scala | 26 ++++++++ .../parser/bindings/AsyncBindingsParser.scala | 1 + .../bindings/AsyncMessageBindingsParser.scala | 12 ++-- .../message/IBMMQMessageBindingParser.scala | 27 ++++++++ .../parser/context/AsyncValidBindingSet.scala | 2 +- .../parser/context/syntax/Async21Syntax.scala | 15 ++++- .../cycle/async20/bindings/ibmmq-binding.yaml | 28 +++++++++ .../validations/ibmmq-binding-extra-key.yaml | 32 ++++++++++ .../async20/ibmmq-binding-extra-key.report | 38 ++++++++++++ .../scala/amf/emit/Async20CycleTest.scala | 7 ++- ...c20UniquePlatformUnitValidationsTest.scala | 4 ++ documentation/model.md | 18 ++++++ 18 files changed, 377 insertions(+), 13 deletions(-) create mode 100644 amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala create mode 100644 amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala create mode 100644 amf-api-contract/shared/src/main/scala/amf/apicontract/internal/metamodel/domain/bindings/IBMMQBindingModel.scala create mode 100644 amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/message/IBMMQMessageBindingParser.scala create mode 100644 amf-cli/shared/src/test/resources/upanddown/cycle/async20/bindings/ibmmq-binding.yaml create mode 100644 amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml create mode 100644 amf-cli/shared/src/test/resources/validations/reports/async20/ibmmq-binding-extra-key.report diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala new file mode 100644 index 0000000000..cf4bf10ee5 --- /dev/null +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala @@ -0,0 +1,51 @@ +package amf.apicontract.client.platform.model.domain.bindings.ibmmq + +import amf.apicontract.client.platform.model.domain.bindings.{BindingVersion, MessageBinding} +import amf.apicontract.client.scala.model.domain.bindings.ibmmq.{IBMMQMessageBinding => InternalIBMMQMessageBinding} +import amf.apicontract.internal.convert.ApiClientConverters._ +import amf.core.client.platform.model +import amf.core.client.platform.model.{IntField, StrField} + +import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel} + +@JSExportAll +case class IBMMQMessageBinding(override private[amf] val _internal: InternalIBMMQMessageBinding) + extends MessageBinding + with BindingVersion { + @JSExportTopLevel("IBMMQMessageBinding") + def this() = this(InternalIBMMQMessageBinding()) + + def messageType: StrField = _internal.messageType + def headers: StrField = _internal.headers + def description: StrField = _internal.description + def expiry: IntField = _internal.expiry + + def withType(messageType: String): this.type = { + _internal.withMessageType(messageType) + this + } + + def withHeaders(headers: String): this.type = { + _internal.withHeaders(headers) + this + } + + def withDescription(description: String): this.type = { + _internal.withDescription(description) + this + } + + def withExpiry(expiry: Int): this.type = { + _internal.withExpiry(expiry) + this + } + + override protected def bindingVersion: model.StrField = _internal.bindingVersion + + override def withBindingVersion(bindingVersion: String): this.type = { + _internal.withBindingVersion(bindingVersion) + this + } + + override def linkCopy(): IBMMQMessageBinding = _internal.linkCopy() +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala new file mode 100644 index 0000000000..47dd7b58ab --- /dev/null +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/bindings/ibmmq/IBMMQMessageBinding.scala @@ -0,0 +1,49 @@ +package amf.apicontract.client.scala.model.domain.bindings.ibmmq + +import amf.apicontract.client.scala.model.domain.bindings.{BindingVersion, MessageBinding} +import amf.apicontract.internal.metamodel.domain.bindings.IBMMQMessageBindingModel +import amf.apicontract.internal.metamodel.domain.bindings.IBMMQMessageBindingModel._ +import amf.apicontract.internal.spec.async.parser.bindings.Bindings.IBMMQ +import amf.core.client.scala.model.{IntField, StrField} +import amf.core.client.scala.model.domain.{DomainElement, Linkable} +import amf.core.internal.metamodel.Field +import amf.core.internal.parser.domain.{Annotations, Fields} +import amf.shapes.client.scala.model.domain.Key + +class IBMMQMessageBinding(override val fields: Fields, override val annotations: Annotations) + extends MessageBinding + with BindingVersion + with Key { + + override protected def bindingVersionField: Field = BindingVersion + override def meta: IBMMQMessageBindingModel.type = IBMMQMessageBindingModel + + def messageType: StrField = fields.field(MessageType) + def headers: StrField = fields.field(Headers) + def description: StrField = fields.field(Description) + def expiry: IntField = fields.field(Expiry) + + def withMessageType(messageType: String): this.type = set(MessageType, messageType) + def withHeaders(headers: String): this.type = set(Headers, headers) + def withDescription(description: String): this.type = set(Description, description) + def withExpiry(expiry: Int): this.type = set(Expiry, expiry) + + override def key: StrField = fields.field(IBMMQMessageBindingModel.key) + + override def componentId: String = s"/$IBMMQ-message" + + override def linkCopy(): IBMMQMessageBinding = IBMMQMessageBinding() + + override protected def classConstructor: (Fields, Annotations) => Linkable with DomainElement = + IBMMQMessageBinding.apply +} + +object IBMMQMessageBinding { + + def apply(): IBMMQMessageBinding = apply(Annotations()) + + def apply(annotations: Annotations): IBMMQMessageBinding = apply(Fields(), annotations) + + def apply(fields: Fields, annotations: Annotations): IBMMQMessageBinding = + new IBMMQMessageBinding(fields, annotations) +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiBaseConverter.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiBaseConverter.scala index da2551f7b4..2046db8c47 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiBaseConverter.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiBaseConverter.scala @@ -30,6 +30,7 @@ import amf.apicontract.client.scala.model.domain.bindings.amqp.{ Amqp091Queue } import amf.apicontract.client.scala.model.domain.bindings.http.{HttpMessageBinding, HttpOperationBinding} +import amf.apicontract.client.scala.model.domain.bindings.ibmmq.IBMMQMessageBinding import amf.apicontract.client.scala.model.domain.bindings.kafka.{KafkaMessageBinding, KafkaOperationBinding} import amf.apicontract.client.scala.model.domain.bindings.mqtt.{ MqttMessageBinding, @@ -112,6 +113,7 @@ trait ApiBaseConverter with MqttServerLastWillConverter with Amqp091ChannelExchangeConverter with Amqp091QueueConverter + with IBBMQMessageBindingConverter with ChannelBindingConverter with OperationBindingConverter with MessageBindingConverter @@ -311,6 +313,15 @@ trait Amqp091QueueConverter extends PlatformSecrets { } } +trait IBBMQMessageBindingConverter extends PlatformSecrets { + implicit object IBBMQMessageBindingMatcher + extends BidirectionalMatcher[IBMMQMessageBinding, domain.bindings.ibmmq.IBMMQMessageBinding] { + override def asClient(from: IBMMQMessageBinding): domain.bindings.ibmmq.IBMMQMessageBinding = + platform.wrap[domain.bindings.ibmmq.IBMMQMessageBinding](from) + override def asInternal(from: domain.bindings.ibmmq.IBMMQMessageBinding): IBMMQMessageBinding = from._internal + } +} + trait EndPointConverter extends PlatformSecrets { implicit object EndPointMatcher extends BidirectionalMatcher[EndPoint, domain.EndPoint] { diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiRegister.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiRegister.scala index 7da48e594a..6f082aa61e 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiRegister.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/convert/ApiRegister.scala @@ -6,6 +6,7 @@ import amf.apicontract.client.platform.model.domain.api.{AsyncApi, WebApi} import amf.apicontract.client.platform.model.domain.bindings._ import amf.apicontract.client.platform.model.domain.bindings.amqp._ import amf.apicontract.client.platform.model.domain.bindings.http.{HttpMessageBinding, HttpOperationBinding} +import amf.apicontract.client.platform.model.domain.bindings.ibmmq.IBMMQMessageBinding import amf.apicontract.client.platform.model.domain.bindings.kafka.{KafkaMessageBinding, KafkaOperationBinding} import amf.apicontract.client.platform.model.domain.bindings.mqtt.{ MqttMessageBinding, @@ -274,6 +275,9 @@ private[amf] object ApiRegister extends UniqueInitializer with PlatformSecrets { platform.registerWrapper(EmptyBindingModel) { case s: amf.apicontract.client.scala.model.domain.bindings.EmptyBinding => EmptyBinding(s) } + platform.registerWrapper(IBMMQMessageBindingModel) { + case s: amf.apicontract.client.scala.model.domain.bindings.ibmmq.IBMMQMessageBinding => IBMMQMessageBinding(s) + } platform.registerWrapper(APIContractProcessingDataModel) { case s: amf.apicontract.client.scala.model.document.APIContractProcessingData => APIContractProcessingData(s) } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/entities/APIEntities.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/entities/APIEntities.scala index bb803a0687..3164cd7b93 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/entities/APIEntities.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/entities/APIEntities.scala @@ -94,6 +94,7 @@ private[amf] object APIEntities extends Entities { ParameterKeyMappingModel, ComponentModuleModel, ParameterFederationMetadataModel, - EndpointFederationMetadataModel + EndpointFederationMetadataModel, + IBMMQMessageBindingModel ) } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/metamodel/domain/bindings/IBMMQBindingModel.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/metamodel/domain/bindings/IBMMQBindingModel.scala new file mode 100644 index 0000000000..082a83259b --- /dev/null +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/metamodel/domain/bindings/IBMMQBindingModel.scala @@ -0,0 +1,62 @@ +package amf.apicontract.internal.metamodel.domain.bindings + +import amf.apicontract.client.scala.model.domain.bindings.ibmmq.IBMMQMessageBinding +import amf.core.client.scala.model.domain.AmfObject +import amf.core.client.scala.vocabulary.Namespace.ApiBinding +import amf.core.client.scala.vocabulary.ValueType +import amf.core.internal.metamodel.Field +import amf.core.internal.metamodel.Type.{Int, Str} +import amf.core.internal.metamodel.domain.{ModelDoc, ModelVocabularies} + +object IBMMQMessageBindingModel extends MessageBindingModel with BindingVersion { + val MessageType: Field = + Field( + Str, + ApiBinding + "messageType", + ModelDoc(ModelVocabularies.ApiBinding, "type", "The type of the message") + ) + + val Headers: Field = + Field( + Str, + ApiBinding + "headers", + ModelDoc( + ModelVocabularies.ApiBinding, + "headers", + "Defines the IBM MQ message headers to include with this message. More than one header can be specified as a comma separated list." + ) + ) + + val Description: Field = + Field( + Str, + ApiBinding + "description", + ModelDoc( + ModelVocabularies.ApiBinding, + "description", + "Provides additional information for application developers: describes the message type or format." + ) + ) + + val Expiry: Field = + Field( + Int, + ApiBinding + "expiry", + ModelDoc( + ModelVocabularies.ApiBinding, + "expiry", + "This is a period of time expressed in milliseconds and set by the application that puts the message." + ) + ) + + override def modelInstance: AmfObject = IBMMQMessageBinding() + + override def fields: List[Field] = + List(MessageType, Headers, Description, Expiry, BindingVersion) ++ MessageBindingModel.fields + + override val `type`: List[ValueType] = ApiBinding + "IBMMQMessageBinding" :: MessageBindingModel.`type` + + override val key: Field = Type + + override val doc: ModelDoc = ModelDoc(ModelVocabularies.ApiBinding, "IBMMQMessageBinding") +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/bindings/AsyncApiMessageBindingsEmitter.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/bindings/AsyncApiMessageBindingsEmitter.scala index 88d20d5711..810b864d90 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/bindings/AsyncApiMessageBindingsEmitter.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/bindings/AsyncApiMessageBindingsEmitter.scala @@ -3,11 +3,13 @@ package amf.apicontract.internal.spec.async.emitters.bindings import amf.apicontract.client.scala.model.domain.bindings.MessageBinding import amf.apicontract.client.scala.model.domain.bindings.amqp.Amqp091MessageBinding import amf.apicontract.client.scala.model.domain.bindings.http.HttpMessageBinding +import amf.apicontract.client.scala.model.domain.bindings.ibmmq.IBMMQMessageBinding import amf.apicontract.client.scala.model.domain.bindings.kafka.KafkaMessageBinding import amf.apicontract.client.scala.model.domain.bindings.mqtt.MqttMessageBinding import amf.apicontract.internal.metamodel.domain.bindings.{ Amqp091MessageBindingModel, HttpMessageBindingModel, + IBMMQMessageBindingModel, KafkaMessageBindingModel } import amf.apicontract.internal.spec.async.emitters.domain @@ -35,6 +37,7 @@ class AsyncApiMessageBindingsEmitter(binding: MessageBinding, ordering: SpecOrde case binding: HttpMessageBinding => Some(new HttpMessageEmitter(binding, ordering)) case binding: KafkaMessageBinding => Some(new KafkaMessageEmitter(binding, ordering)) case binding: MqttMessageBinding => Some(new MqttMessageEmitter(binding, ordering)) + case binding: IBMMQMessageBinding => Some(new IBMMQMessageEmitter(binding, ordering)) case _ => None } @@ -121,3 +124,26 @@ class Amqp091MessageEmitter(binding: Amqp091MessageBinding, ordering: SpecOrderi override def position(): Position = pos(binding.annotations) } + +class IBMMQMessageEmitter(binding: IBMMQMessageBinding, ordering: SpecOrdering)(implicit + val spec: OasLikeSpecEmitterContext +) extends AsyncApiCommonBindingEmitter { + override def emit(b: EntryBuilder): Unit = { + b.entry( + YNode("ibmmq"), + _.obj { emitter => + val result = ListBuffer[EntryEmitter]() + val fs = binding.fields + + fs.entry(IBMMQMessageBindingModel.MessageType).foreach(f => result += ValueEmitter("type", f)) + fs.entry(IBMMQMessageBindingModel.Headers).foreach(f => result += ValueEmitter("headers", f)) + fs.entry(IBMMQMessageBindingModel.Description).foreach(f => result += ValueEmitter("description", f)) + fs.entry(IBMMQMessageBindingModel.Expiry).foreach(f => result += ValueEmitter("expiry", f)) + emitBindingVersion(fs, result) + traverse(ordering.sorted(result), emitter) + } + ) + } + + override def position(): Position = pos(binding.annotations) +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncBindingsParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncBindingsParser.scala index e430d9eb34..22fe416aab 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncBindingsParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncBindingsParser.scala @@ -117,4 +117,5 @@ object Bindings { val Stomp = "stomp" val Redis = "redis" val Mercure = "mercure" + val IBMMQ = "ibmmq" } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncMessageBindingsParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncMessageBindingsParser.scala index 9614677c10..5102784978 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncMessageBindingsParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncMessageBindingsParser.scala @@ -2,13 +2,8 @@ package amf.apicontract.internal.spec.async.parser.bindings import amf.apicontract.client.scala.model.domain.bindings.{MessageBinding, MessageBindings} import amf.apicontract.internal.metamodel.domain.bindings._ -import amf.apicontract.internal.spec.async.parser.bindings.Bindings.{Amqp, Http, Kafka, Mqtt} -import amf.apicontract.internal.spec.async.parser.bindings.message.{ - Amqp091MessageBindingParser, - HttpMessageBindingParser, - KafkaMessageBindingParser, - MqttMessageBindingParser -} +import amf.apicontract.internal.spec.async.parser.bindings.Bindings._ +import amf.apicontract.internal.spec.async.parser.bindings.message._ import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext import amf.apicontract.internal.spec.common.WebApiDeclarations.ErrorMessageBindings import amf.apicontract.internal.spec.spec.OasDefinitions @@ -22,7 +17,8 @@ object AsyncMessageBindingsParser { Amqp -> Amqp091MessageBindingParser, Http -> HttpMessageBindingParser, Kafka -> KafkaMessageBindingParser, - Mqtt -> MqttMessageBindingParser + Mqtt -> MqttMessageBindingParser, + IBMMQ -> IBMMQMessageBindingParser ) } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/message/IBMMQMessageBindingParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/message/IBMMQMessageBindingParser.scala new file mode 100644 index 0000000000..80273026ab --- /dev/null +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/message/IBMMQMessageBindingParser.scala @@ -0,0 +1,27 @@ +package amf.apicontract.internal.spec.async.parser.bindings.message + +import amf.apicontract.client.scala.model.domain.bindings.ibmmq.IBMMQMessageBinding +import amf.apicontract.internal.metamodel.domain.bindings.IBMMQMessageBindingModel +import amf.apicontract.internal.spec.async.parser.bindings.BindingParser +import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext +import amf.core.internal.parser.YMapOps +import amf.core.internal.parser.domain.Annotations +import org.yaml.model.{YMap, YMapEntry} + +object IBMMQMessageBindingParser extends BindingParser[IBMMQMessageBinding] { + override def parse(entry: YMapEntry, parent: String)(implicit ctx: AsyncWebApiContext): IBMMQMessageBinding = { + val binding = IBMMQMessageBinding(Annotations(entry)) + val map = entry.value.as[YMap] + + map.key("type", IBMMQMessageBindingModel.MessageType in binding) + map.key("headers", IBMMQMessageBindingModel.Headers in binding) + map.key("description", IBMMQMessageBindingModel.Description in binding) + map.key("expiry", IBMMQMessageBindingModel.Expiry in binding) + + parseBindingVersion(binding, IBMMQMessageBindingModel.BindingVersion, map) + + ctx.closedShape(binding, map, "IBMMQMessageBinding") + + binding + } +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncValidBindingSet.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncValidBindingSet.scala index 00323de96a..c5e1623083 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncValidBindingSet.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncValidBindingSet.scala @@ -11,7 +11,7 @@ object AsyncValidBindingSet { private val basic = Set(Http, WebSockets, Kafka, Amqp, Amqp1, Mqtt, Mqtt5, Nats, Jms, Sns, Sqs, Stomp, Redis) val async20: AsyncValidBindingSet = AsyncValidBindingSet(basic) - val async21: AsyncValidBindingSet = async20.add(Mercure) + val async21: AsyncValidBindingSet = async20.add(Mercure, IBMMQ) val async22: AsyncValidBindingSet = async21 val async23: AsyncValidBindingSet = async22 val async24: AsyncValidBindingSet = async23 diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/syntax/Async21Syntax.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/syntax/Async21Syntax.scala index 9977ef0caa..9fb1fa40ae 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/syntax/Async21Syntax.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/syntax/Async21Syntax.scala @@ -1,8 +1,19 @@ package amf.apicontract.internal.spec.async.parser.context.syntax -import amf.apicontract.internal.spec.async.parser.bindings.Bindings.Mercure +import amf.apicontract.internal.spec.async.parser.bindings.Bindings.{IBMMQ, Mercure} import amf.shapes.internal.spec.common.parser.SpecSyntax object Async21Syntax extends SpecSyntax { - override val nodes: Map[String, Set[String]] = add(Async20Syntax.nodes, "bindings", Set(Mercure)) + override val nodes: Map[String, Set[String]] = + add( + Async20Syntax.nodes, + "bindings" -> Set(Mercure, IBMMQ), + "IBMMQMessageBinding" -> Set( + "type", + "headers", + "description", + "expiry", + "bindingVersion" + ) + ) } diff --git a/amf-cli/shared/src/test/resources/upanddown/cycle/async20/bindings/ibmmq-binding.yaml b/amf-cli/shared/src/test/resources/upanddown/cycle/async20/bindings/ibmmq-binding.yaml new file mode 100644 index 0000000000..40c2025ecf --- /dev/null +++ b/amf-cli/shared/src/test/resources/upanddown/cycle/async20/bindings/ibmmq-binding.yaml @@ -0,0 +1,28 @@ +asyncapi: 2.1.0 +info: + title: test mercure binding + version: 1.0.0 +servers: + theName: + url: some.com + protocol: ibmmq + bindings: + ibmmq: {} +channels: + some-channel: + bindings: + ibmmq: {} + description: some channel + publish: + bindings: + ibmmq: {} + message: + bindings: + ibmmq: + type: test + headers: test + description: test + expiry: 123 + bindingVersion: test + payload: + type: string diff --git a/amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml b/amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml new file mode 100644 index 0000000000..d58478ba38 --- /dev/null +++ b/amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml @@ -0,0 +1,32 @@ +asyncapi: 2.1.0 +info: + title: test mercure binding + version: 1.0.0 +servers: + theName: + url: some.com + protocol: mercure + bindings: + ibmmq: + key: value +channels: + some-channel: + bindings: + ibmmq: + key: value + description: some channel + publish: + bindings: + ibmmq: + key: value + message: + bindings: + ibmmq: + type: test + headers: test + description: test + expiry: 123 + bindingVersion: test + thisKeyIsNotAllowed: should throw validation error + payload: + type: string diff --git a/amf-cli/shared/src/test/resources/validations/reports/async20/ibmmq-binding-extra-key.report b/amf-cli/shared/src/test/resources/validations/reports/async20/ibmmq-binding-extra-key.report new file mode 100644 index 0000000000..539dc3a171 --- /dev/null +++ b/amf-cli/shared/src/test/resources/validations/reports/async20/ibmmq-binding-extra-key.report @@ -0,0 +1,38 @@ +ModelId: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml +Profile: +Conforms: false +Number of results: 4 + +Level: Violation + +- Constraint: http://a.ml/vocabularies/amf/parser#non-empty-binding-map + Message: Reserved name binding 'ibmmq' must have an empty map + Severity: Violation + Target: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml#/async-api/server/some.com/server-bindings/bindings/ibmmq + Property: + Range: [(10,12)-(12,0)] + Location: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml + +- Constraint: http://a.ml/vocabularies/amf/parser#non-empty-binding-map + Message: Reserved name binding 'ibmmq' must have an empty map + Severity: Violation + Target: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml#/async-api/endpoint/some-channel/channel-bindings/bindings/ibmmq + Property: + Range: [(15,12)-(17,0)] + Location: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml + +- Constraint: http://a.ml/vocabularies/amf/parser#non-empty-binding-map + Message: Reserved name binding 'ibmmq' must have an empty map + Severity: Violation + Target: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml#/async-api/endpoint/some-channel/supportedOperation/publish/operation-bindings/bindings/ibmmq + Property: + Range: [(20,14)-(22,0)] + Location: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml + +- Constraint: http://a.ml/vocabularies/amf/parser#closed-shape + Message: Property 'thisKeyIsNotAllowed' not supported in a ASYNC 2.1 IBMMQMessageBinding node + Severity: Violation + Target: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml#/async-api/endpoint/some-channel/supportedOperation/publish/expects/request/message-bindings/bindings/ibmmq-message + Property: + Range: [(30,12)-(31,0)] + Location: file://amf-cli/shared/src/test/resources/validations/async20/validations/ibmmq-binding-extra-key.yaml diff --git a/amf-cli/shared/src/test/scala/amf/emit/Async20CycleTest.scala b/amf-cli/shared/src/test/scala/amf/emit/Async20CycleTest.scala index fb3b7c0cdf..73561e5ea0 100644 --- a/amf-cli/shared/src/test/scala/amf/emit/Async20CycleTest.scala +++ b/amf-cli/shared/src/test/scala/amf/emit/Async20CycleTest.scala @@ -137,9 +137,14 @@ class Async20CycleTest extends FunSuiteCycleTests { "mercure binding", "bindings/mercure-binding.yaml", "bindings/mercure-binding.yaml" + ), + FixtureData( + "ibmmq binding", + "bindings/ibmmq-binding.yaml", + "bindings/ibmmq-binding.yaml" ) -// TODO: figure out why this test is commented out + // TODO: figure out why this test is commented out // FixtureData("Channel params with refs", "references/channel-params.yaml", "references/channel-params.yaml"), ) } diff --git a/amf-cli/shared/src/test/scala/amf/validation/Async20UniquePlatformUnitValidationsTest.scala b/amf-cli/shared/src/test/scala/amf/validation/Async20UniquePlatformUnitValidationsTest.scala index fe0611b8e3..90a4d655c5 100644 --- a/amf-cli/shared/src/test/scala/amf/validation/Async20UniquePlatformUnitValidationsTest.scala +++ b/amf-cli/shared/src/test/scala/amf/validation/Async20UniquePlatformUnitValidationsTest.scala @@ -332,4 +332,8 @@ class Async20UniquePlatformUnitValidationsTest extends UniquePlatformReportGenTe test("Mercure binding should be empty") { validate("mercure-binding-nonempty.yaml", Some("mercure-binding-nonempty.report")) } + + test("IBMMQ Closed Shape validation") { + validate("ibmmq-binding-extra-key.yaml", Some("ibmmq-binding-extra-key.report")) + } } diff --git a/documentation/model.md b/documentation/model.md index bdd88a06ad..278fdce626 100644 --- a/documentation/model.md +++ b/documentation/model.md @@ -76,6 +76,7 @@ AMF Model Documentation * [HttpMessageBinding](#httpmessagebinding) * [HttpOperationBinding](#httpoperationbinding) * [HttpSettings](#httpsettings) +* [IBMMQMessageBinding](#ibmmqmessagebinding) * [IriTemplateMapping](#iritemplatemapping) * [JsonLDElement](#jsonldelement) * [JsonLDInstanceDocument](#jsonldinstancedocument) @@ -1500,6 +1501,23 @@ Types: | additionalProperties | [DataNode](#datanode) | - | | `http://a.ml/vocabularies/security#additionalProperties` | | extends | [[DomainElement](#domainelement)] | false | Entity that is going to be extended overlaying or adding additional information The type of the relationship provide the semantics about thow the referenced and referencer elements must be combined when generating the domain model from the document model. | `http://a.ml/vocabularies/document#extends` | +## IBMMQMessageBinding + +Types: +* `http://a.ml/vocabularies/apiBinding#IBMMQMessageBinding` +* `http://a.ml/vocabularies/apiBinding#MessageBinding` +* `http://a.ml/vocabularies/document#DomainElement` + + | Name | Value | Sorted | Documentation | Namespace | + | ------ | ------ | ------ | ------ | ------ | + | messageType | string | - | The type of the message | `http://a.ml/vocabularies/apiBinding#messageType` | + | headers | string | - | Defines the IBM MQ message headers to include with this message. More than one header can be specified as a comma separated list. | `http://a.ml/vocabularies/apiBinding#headers` | + | description | string | - | Provides additional information for application developers: describes the message type or format. | `http://a.ml/vocabularies/apiBinding#description` | + | expiry | int | - | This is a period of time expressed in milliseconds and set by the application that puts the message. | `http://a.ml/vocabularies/apiBinding#expiry` | + | bindingVersion | string | - | The version of this binding | `http://a.ml/vocabularies/apiBinding#bindingVersion` | + | type | string | - | Binding for a corresponding known type | `http://a.ml/vocabularies/apiBinding#type` | + | extends | [[DomainElement](#domainelement)] | false | Entity that is going to be extended overlaying or adding additional information The type of the relationship provide the semantics about thow the referenced and referencer elements must be combined when generating the domain model from the document model. | `http://a.ml/vocabularies/document#extends` | + ## IriTemplateMapping Types: