From aa9633d02504dc5df9fac8552192dd417451f15c Mon Sep 17 00:00:00 2001 From: Loose Date: Mon, 5 Feb 2024 12:10:40 -0300 Subject: [PATCH] W-12689957 - Added new components for Async 2.3 --- .../platform/model/domain/EndPoint.scala | 10 +- .../client/platform/model/domain/Server.scala | 6 +- .../client/scala/model/domain/EndPoint.scala | 12 +- .../client/scala/model/domain/Server.scala | 21 +- .../document/AsyncDeclarationsEmitters.scala | 24 +- .../domain/AsyncApiEndpointsEmitter.scala | 49 +++- .../domain/AsyncApiServersEmitter.scala | 58 +++-- .../AsyncOperationBindingsParser.scala | 6 - .../parser/context/Async2WebApiContext.scala | 20 +- .../context/AsyncSpecAwareContext.scala | 38 ++-- .../document/AsyncApiDocumentParser.scala | 5 +- .../parser/domain/Async20EndpointParser.scala | 132 +++++++++++ .../parser/domain/AsyncServerParser.scala | 91 +++++++- .../Async20DeclarationParser.scala | 10 +- .../Async23DeclarationParser.scala | 39 +++- .../declarations/AsyncDeclarationParser.scala | 7 +- .../spec/common/WebApiDeclarations.scala | 39 +++- .../domain/OasTagToReferenceEmitter.scala | 2 + .../parser/domain/AsyncEndpointParser.scala | 63 ------ .../parser/domain/OasLikeEndpointParser.scala | 2 +- .../WebApiReferenceResolutionStage.scala | 16 +- .../async-2.3-components.expanded.jsonld | 214 ++++++++++++++++++ .../async-2.3-components.flattened.jsonld | 162 +++++++++++++ .../async20/async-2.3-components.yaml | 18 ++ .../async20/components/components-2.3.yaml | 18 ++ .../{ => components}/components-cycle.yaml | 0 .../scala/amf/emit/Async20CycleTest.scala | 7 +- .../resolution/Async20ResolutionTest.scala | 10 + 28 files changed, 906 insertions(+), 173 deletions(-) create mode 100644 amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/Async20EndpointParser.scala delete mode 100644 amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/AsyncEndpointParser.scala create mode 100644 amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.expanded.jsonld create mode 100644 amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.flattened.jsonld create mode 100644 amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.yaml create mode 100644 amf-cli/shared/src/test/resources/upanddown/cycle/async20/components/components-2.3.yaml rename amf-cli/shared/src/test/resources/upanddown/cycle/async20/{ => components}/components-cycle.yaml (100%) diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/EndPoint.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/EndPoint.scala index 6b8f3dea71..744c435ccb 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/EndPoint.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/EndPoint.scala @@ -3,11 +3,10 @@ package amf.apicontract.client.platform.model.domain import amf.apicontract.client.platform.model.domain.bindings.ChannelBindings import amf.apicontract.client.platform.model.domain.federation.EndPointFederationMetadata import amf.apicontract.client.platform.model.domain.security.SecurityRequirement +import amf.apicontract.client.scala.model.domain.{EndPoint => InternalEndPoint} import amf.apicontract.internal.convert.ApiClientConverters._ import amf.core.client.platform.model.StrField -import amf.core.client.platform.model.domain.{DomainElement, NamedDomainElement} -import amf.apicontract.client.scala.model.domain.{EndPoint => InternalEndPoint} -import amf.core.client.platform.model.domain.federation.ShapeFederationMetadata +import amf.core.client.platform.model.domain.{DomainElement, Linkable, NamedDomainElement} import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel} @@ -16,7 +15,8 @@ import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel} @JSExportAll case class EndPoint(override private[amf] val _internal: InternalEndPoint) extends DomainElement - with NamedDomainElement { + with NamedDomainElement + with Linkable { @JSExportTopLevel("EndPoint") def this() = this(InternalEndPoint()) @@ -117,4 +117,6 @@ case class EndPoint(override private[amf] val _internal: InternalEndPoint) _internal.withBindings(bindings) this } + + override def linkCopy(): EndPoint = _internal.linkCopy() } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/Server.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/Server.scala index 2a9e23ced2..1f933fccee 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/Server.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/platform/model/domain/Server.scala @@ -4,7 +4,7 @@ import amf.apicontract.client.platform.model.domain.bindings.ServerBindings import amf.apicontract.client.platform.model.domain.security.SecurityRequirement import amf.apicontract.internal.convert.ApiClientConverters.ClientList import amf.core.client.platform.model.StrField -import amf.core.client.platform.model.domain.DomainElement +import amf.core.client.platform.model.domain.{DomainElement, Linkable} import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel} import amf.apicontract.client.scala.model.domain.{Server => InternalServer} @@ -13,7 +13,7 @@ import amf.apicontract.internal.convert.ApiClientConverters._ /** Server model class. */ @JSExportAll -case class Server(override private[amf] val _internal: InternalServer) extends DomainElement { +case class Server(override private[amf] val _internal: InternalServer) extends DomainElement with Linkable { @JSExportTopLevel("Server") def this() = this(InternalServer()) @@ -69,4 +69,6 @@ case class Server(override private[amf] val _internal: InternalServer) extends D * Parameter is required. */ def withVariable(name: String): Parameter = _internal.withVariable(name) + + override def linkCopy(): Server = _internal.linkCopy() } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/EndPoint.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/EndPoint.scala index f39c848a3b..28c3d26909 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/EndPoint.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/EndPoint.scala @@ -7,8 +7,8 @@ import amf.apicontract.internal.annotations.ParentEndPoint import amf.apicontract.internal.metamodel.domain.EndPointModel import amf.apicontract.internal.metamodel.domain.EndPointModel._ import amf.core.client.scala.model.StrField -import amf.core.client.scala.model.domain.NamedDomainElement -import amf.core.client.scala.model.domain.federation.{FederationMetadata, HasFederationMetadata} +import amf.core.client.scala.model.domain.federation.HasFederationMetadata +import amf.core.client.scala.model.domain.{DomainElement, Linkable, NamedDomainElement} import amf.core.internal.metamodel.Field import amf.core.internal.parser.domain.{Annotations, Fields} import amf.core.internal.utils.AmfStrings @@ -20,7 +20,8 @@ class EndPoint(override val fields: Fields, override val annotations: Annotation with SecuredElement with ExtensibleWebApiDomainElement with ServerContainer - with HasFederationMetadata[EndPointFederationMetadata] { + with HasFederationMetadata[EndPointFederationMetadata] + with Linkable { def description: StrField = fields.field(Description) def summary: StrField = fields.field(Summary) @@ -85,6 +86,11 @@ class EndPoint(override val fields: Fields, override val annotations: Annotation } } override def nameField: Field = Name + + override def linkCopy(): EndPoint = EndPoint() + + /** apply method for create a new instance with fields and annotations. Aux method for copy */ + override protected def classConstructor: (Fields, Annotations) => Linkable with DomainElement = EndPoint.apply } object EndPoint { diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/Server.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/Server.scala index 5fbe4ce63f..4f428158e7 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/Server.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/client/scala/model/domain/Server.scala @@ -4,6 +4,8 @@ import amf.apicontract.client.scala.model.domain.bindings.ServerBindings import amf.apicontract.internal.metamodel.domain.ServerModel import amf.apicontract.internal.metamodel.domain.ServerModel._ import amf.core.client.scala.model.StrField +import amf.core.client.scala.model.domain.{DomainElement, Linkable, NamedDomainElement} +import amf.core.internal.metamodel.Field import amf.core.internal.metamodel.domain.DomainElementModel import amf.core.internal.parser.domain.{Annotations, Fields} import amf.core.internal.utils.AmfStrings @@ -11,9 +13,13 @@ import org.yaml.model.YMap /** Server internal model */ -case class Server(fields: Fields, annotations: Annotations) extends SecuredElement { +class Server(override val fields: Fields, override val annotations: Annotations) + extends SecuredElement + with NamedDomainElement + with Linkable { + + override protected def nameField: Field = Name - def name: StrField = fields.field(Name) def url: StrField = fields.field(Url) def description: StrField = fields.field(Description) def variables: Seq[Parameter] = fields.field(Variables) @@ -21,7 +27,6 @@ case class Server(fields: Fields, annotations: Annotations) extends SecuredEleme def protocolVersion: StrField = fields.field(ProtocolVersion) def bindings: ServerBindings = fields.field(Bindings) - def withName(name: String): this.type = set(Name, name) def withUrl(url: String): this.type = set(Url, url) def withDescription(description: String): this.type = set(Description, description) def withVariables(variables: Seq[Parameter]): this.type = setArray(Variables, variables) @@ -39,6 +44,12 @@ case class Server(fields: Fields, annotations: Annotations) extends SecuredEleme /** Value , path + field value that is used to compose the id when the object its adopted */ override def componentId: String = "/" + url.option().orNull.urlComponentEncoded + + override def linkCopy(): Server = Server().withId(id) + + /** apply method for create a new instance with fields and annotations. Aux method for copy */ + override protected def classConstructor: (Fields, Annotations) => Linkable with DomainElement = Server.apply + } object Server { @@ -47,5 +58,7 @@ object Server { def apply(ast: YMap): Server = apply(Annotations(ast)) - def apply(annotations: Annotations): Server = Server(Fields(), annotations) + def apply(annotations: Annotations): Server = apply(Fields(), annotations) + + def apply(fields: Fields, annotations: Annotations): Server = new Server(fields, annotations) } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/document/AsyncDeclarationsEmitters.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/document/AsyncDeclarationsEmitters.scala index 253d1b971e..abeb5ea7d9 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/document/AsyncDeclarationsEmitters.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/document/AsyncDeclarationsEmitters.scala @@ -1,14 +1,6 @@ package amf.apicontract.internal.spec.async.emitters.document -import amf.apicontract.internal.spec.async.emitters._ -import amf.apicontract.internal.spec.async.emitters.domain.{ - AsyncApiBindingsDeclarationEmitter, - AsyncApiParametersEmitter, - AsyncCorrelationIdDeclarationsEmitter, - AsyncMessageDeclarationsEmitter, - AsyncOperationTraitsDeclarationEmitter, - AsyncSecuritySchemesEmitter -} +import amf.apicontract.internal.spec.async.emitters.domain._ import amf.apicontract.internal.spec.common.WebApiDeclarations import amf.apicontract.internal.spec.oas.emitter.context.OasLikeSpecEmitterContext import amf.core.client.scala.errorhandling.UnhandledErrorHandler @@ -77,6 +69,20 @@ case class AsyncDeclarationsEmitters(declares: Seq[DomainElement], ordering: Spe ordering ) + if (declarations.servers.nonEmpty) + result += AsyncApiServersDeclarationEmitter( + "servers", + declarations.servers.values.toSeq, + ordering + ) + + if (declarations.channels.nonEmpty) + result += AsyncApiChannelsDeclarationEmitter( + "channels", + declarations.channels.values.toSeq, + ordering + ) + result } } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiEndpointsEmitter.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiEndpointsEmitter.scala index d45a7eef66..9f5775a3ae 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiEndpointsEmitter.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiEndpointsEmitter.scala @@ -3,12 +3,15 @@ package amf.apicontract.internal.spec.async.emitters.domain import amf.apicontract.client.scala.model.domain.{EndPoint, Operation, Parameter} import amf.apicontract.internal.metamodel.domain.EndPointModel import amf.apicontract.internal.spec.oas.emitter.context.OasLikeSpecEmitterContext +import amf.apicontract.internal.spec.oas.emitter.domain.OasTagToReferenceEmitter import org.mulesoft.common.client.lexical.Position import amf.core.internal.parser.domain.FieldEntry import amf.core.internal.render.BaseEmitters.{ValueEmitter, pos, traverse} import amf.core.internal.render.SpecOrdering import amf.core.internal.render.emitters.{EntryEmitter, PartEmitter} import amf.shapes.internal.annotations.OrphanOasExtension +import org.mulesoft.common.client.lexical.Position.ZERO +import org.yaml.model.YDocument.{EntryBuilder, PartBuilder} import org.yaml.model.{YDocument, YNode} import scala.collection.mutable.ListBuffer @@ -44,17 +47,21 @@ class AsyncApiSingleEndpointEmitter(channel: EndPoint, ordering: SpecOrdering)(i } override def emit(b: YDocument.PartBuilder): Unit = { - val result = ListBuffer[EntryEmitter]() - val fs = channel.fields - val bindingOrphanAnnotations = - channel.customDomainProperties.filter(_.extension.annotations.contains(classOf[OrphanOasExtension])) - fs.entry(EndPointModel.Description).foreach(f => result += ValueEmitter("description", f)) - fs.entry(EndPointModel.Operations).foreach(f => result ++= operations(f)) - fs.entry(EndPointModel.Parameters) - .foreach(f => result += new AsyncApiParametersEmitter(f.arrayValues[Parameter], ordering)) - fs.entry(EndPointModel.Bindings) - .foreach(f => result += AsyncApiBindingsEmitter(f.value.value, ordering, bindingOrphanAnnotations)) - b.obj(traverse(ordering.sorted(result), _)) + if (channel.isLink) { + emitLink(b) + } else { + val result = ListBuffer[EntryEmitter]() + val fs = channel.fields + val bindingOrphanAnnotations = + channel.customDomainProperties.filter(_.extension.annotations.contains(classOf[OrphanOasExtension])) + fs.entry(EndPointModel.Description).foreach(f => result += ValueEmitter("description", f)) + fs.entry(EndPointModel.Operations).foreach(f => result ++= operations(f)) + fs.entry(EndPointModel.Parameters) + .foreach(f => result += new AsyncApiParametersEmitter(f.arrayValues[Parameter], ordering)) + fs.entry(EndPointModel.Bindings) + .foreach(f => result += AsyncApiBindingsEmitter(f.value.value, ordering, bindingOrphanAnnotations)) + b.obj(traverse(ordering.sorted(result), _)) + } } def operations(f: FieldEntry): Seq[AsyncApiOperationEmitter] = @@ -62,5 +69,25 @@ class AsyncApiSingleEndpointEmitter(channel: EndPoint, ordering: SpecOrdering)(i .filter(e => e.method.value().matches("subscribe|publish")) .map(o => new AsyncApiOperationEmitter(o, ordering)(spec)) + def emitLink(b: PartBuilder): Unit = { + OasTagToReferenceEmitter(channel).emit(b) + } + override def position(): Position = pos(channel.annotations) } + +case class AsyncApiChannelsDeclarationEmitter(key: String, channels: Seq[EndPoint], ordering: SpecOrdering)(implicit + val spec: OasLikeSpecEmitterContext +) extends EntryEmitter { + + override def emit(b: EntryBuilder): Unit = { + val namedChannelsEmitters = + channels.map(c => new AsyncApiSingleEndpointEmitter(c, ordering)) + b.entry( + key, + _.obj(pb => namedChannelsEmitters.foreach(e => e.emit(pb))) + ) + } + + override def position(): Position = channels.headOption.map(b => pos(b.annotations)).getOrElse(ZERO) +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiServersEmitter.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiServersEmitter.scala index 08376418bc..99535c452e 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiServersEmitter.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/emitters/domain/AsyncApiServersEmitter.scala @@ -8,13 +8,16 @@ import amf.apicontract.internal.spec.common.emitter.{ SecurityRequirementsEmitter } import amf.apicontract.internal.spec.oas.emitter.context.OasLikeSpecEmitterContext -import org.mulesoft.common.client.lexical.Position +import amf.apicontract.internal.spec.oas.emitter.domain.OasTagToReferenceEmitter import amf.core.internal.parser.domain.FieldEntry import amf.core.internal.render.BaseEmitters.{ValueEmitter, pos, traverse} import amf.core.internal.render.SpecOrdering import amf.core.internal.render.emitters.{EntryEmitter, PartEmitter} import amf.shapes.internal.annotations.OrphanOasExtension import amf.shapes.internal.spec.common.emitter.annotations.AnnotationsEmitter +import org.mulesoft.common.client.lexical.Position +import org.mulesoft.common.client.lexical.Position.ZERO +import org.yaml.model.YDocument.{EntryBuilder, PartBuilder} import org.yaml.model.{YDocument, YNode} import scala.collection.mutable.ListBuffer @@ -50,25 +53,50 @@ class AsyncApiServerPartEmitter(server: Server, ordering: SpecOrdering)(implicit extends PartEmitter { protected implicit val shapeCtx = AgnosticShapeEmitterContextAdapter(spec) override def emit(b: YDocument.PartBuilder): Unit = { - val result = ListBuffer[EntryEmitter]() - val fs = server.fields - val bindingOrphanAnnotations = - server.customDomainProperties.filter(_.extension.annotations.contains(classOf[OrphanOasExtension])) + if (server.isLink) { + emitLink(b) + } else { + val result = ListBuffer[EntryEmitter]() + val fs = server.fields + val bindingOrphanAnnotations = + server.customDomainProperties.filter(_.extension.annotations.contains(classOf[OrphanOasExtension])) - fs.entry(ServerModel.Url).foreach(f => result += ValueEmitter("url", f)) - fs.entry(ServerModel.Protocol).foreach(f => result += ValueEmitter("protocol", f)) - fs.entry(ServerModel.ProtocolVersion).foreach(f => result += ValueEmitter("protocolVersion", f)) - fs.entry(ServerModel.Description).foreach(f => result += ValueEmitter("description", f)) - fs.entry(ServerModel.Variables).foreach(f => result += OasServerVariablesEmitter(f, ordering)) - fs.entry(ServerModel.Security).foreach(f => result += SecurityRequirementsEmitter("security", f, ordering)) - fs.entry(ServerModel.Bindings) - .foreach(f => result += AsyncApiBindingsEmitter(f.value.value, ordering, bindingOrphanAnnotations)) + fs.entry(ServerModel.Url).foreach(f => result += ValueEmitter("url", f)) + fs.entry(ServerModel.Protocol).foreach(f => result += ValueEmitter("protocol", f)) + fs.entry(ServerModel.ProtocolVersion).foreach(f => result += ValueEmitter("protocolVersion", f)) + fs.entry(ServerModel.Description).foreach(f => result += ValueEmitter("description", f)) + fs.entry(ServerModel.Variables).foreach(f => result += OasServerVariablesEmitter(f, ordering)) + fs.entry(ServerModel.Security).foreach(f => result += SecurityRequirementsEmitter("security", f, ordering)) + fs.entry(ServerModel.Bindings) + .foreach(f => result += AsyncApiBindingsEmitter(f.value.value, ordering, bindingOrphanAnnotations)) - result ++= AnnotationsEmitter(server, ordering).emitters + result ++= AnnotationsEmitter(server, ordering).emitters + + b.obj(traverse(ordering.sorted(result), _)) + } - b.obj(traverse(ordering.sorted(result), _)) + } + + def emitLink(b: PartBuilder): Unit = { + OasTagToReferenceEmitter(server).emit(b) } override def position(): Position = pos(server.annotations) } + +case class AsyncApiServersDeclarationEmitter(key: String, servers: Seq[Server], ordering: SpecOrdering)(implicit + val spec: OasLikeSpecEmitterContext +) extends EntryEmitter { + + override def emit(b: EntryBuilder): Unit = { + val namedServersEmitters = + servers.map(s => new AsyncApiSingleServerEmitter(s, ordering)) + b.entry( + key, + _.obj(pb => namedServersEmitters.foreach(e => e.emit(pb))) + ) + } + + override def position(): Position = servers.headOption.map(b => pos(b.annotations)).getOrElse(ZERO) +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncOperationBindingsParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncOperationBindingsParser.scala index 6a4d4b6f5e..4139ae41fd 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncOperationBindingsParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/bindings/AsyncOperationBindingsParser.scala @@ -1,9 +1,5 @@ package amf.apicontract.internal.spec.async.parser.bindings -import amf.apicontract.client.scala.model.domain.bindings.amqp.Amqp091OperationBinding -import amf.apicontract.client.scala.model.domain.bindings.http.HttpOperationBinding -import amf.apicontract.client.scala.model.domain.bindings.kafka.KafkaOperationBinding -import amf.apicontract.client.scala.model.domain.bindings.mqtt.MqttOperationBinding import amf.apicontract.client.scala.model.domain.bindings.{OperationBinding, OperationBindings} import amf.apicontract.internal.metamodel.domain.bindings._ import amf.apicontract.internal.spec.async.parser.bindings.AsyncOperationBindingsParser.parserMap @@ -19,10 +15,8 @@ import amf.apicontract.internal.spec.common.WebApiDeclarations.ErrorOperationBin import amf.apicontract.internal.spec.spec.OasDefinitions import amf.core.client.scala.model.domain.AmfScalar import amf.core.internal.metamodel.Field -import amf.core.internal.parser.YMapOps import amf.core.internal.parser.domain.{Annotations, SearchScope} import amf.shapes.internal.spec.common.parser.YMapEntryLike -import org.yaml.model.{YMap, YMapEntry} object AsyncOperationBindingsParser { private val parserMap: Map[String, BindingParser[OperationBinding]] = Map( diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/Async2WebApiContext.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/Async2WebApiContext.scala index 02eabd85b5..bb6db5c49e 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/Async2WebApiContext.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/Async2WebApiContext.scala @@ -1,20 +1,10 @@ package amf.apicontract.internal.spec.async.parser.context -import amf.apicontract.internal.spec.async._ import amf.apicontract.internal.spec.async.parser.context.syntax._ import amf.apicontract.internal.spec.common.AsyncWebApiDeclarations import amf.core.client.scala.config.ParsingOptions import amf.core.client.scala.parse.document.{ParsedReference, ParserContext} -import amf.core.internal.remote.{ - AsyncApi20, - AsyncApi21, - AsyncApi22, - AsyncApi23, - AsyncApi24, - AsyncApi25, - AsyncApi26, - Spec -} +import amf.core.internal.remote._ import amf.shapes.internal.spec.async.parser.Async2Settings import scala.collection.mutable @@ -142,10 +132,10 @@ object Async2WebApiContext { case AsyncApi20 => ctx => Async20VersionFactory()(ctx) case AsyncApi21 => ctx => Async20VersionFactory()(ctx) case AsyncApi22 => ctx => Async20VersionFactory()(ctx) - case AsyncApi23 => ctx => Async20VersionFactory()(ctx) - case AsyncApi24 => ctx => Async20VersionFactory()(ctx) - case AsyncApi25 => ctx => Async20VersionFactory()(ctx) - case AsyncApi26 => ctx => Async20VersionFactory()(ctx) + case AsyncApi23 => ctx => Async23VersionFactory()(ctx) + case AsyncApi24 => ctx => Async23VersionFactory()(ctx) + case AsyncApi25 => ctx => Async23VersionFactory()(ctx) + case AsyncApi26 => ctx => Async23VersionFactory()(ctx) } private def bindingSet(spec: Spec): AsyncValidBindingSet = spec match { diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncSpecAwareContext.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncSpecAwareContext.scala index 3aec993ac3..b43712c465 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncSpecAwareContext.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/context/AsyncSpecAwareContext.scala @@ -1,44 +1,52 @@ package amf.apicontract.internal.spec.async.parser.context +import amf.apicontract.client.scala.model.domain.api.AsyncApi import amf.apicontract.client.scala.model.domain.security.SecurityScheme import amf.apicontract.client.scala.model.domain.{EndPoint, Operation} -import amf.apicontract.internal.spec.async.parser.domain.{ - Async2SecuritySchemeParser, - Async2SecuritySettingsParser, - AsyncOperationParser, - AsyncServerVariableParser -} +import amf.apicontract.internal.spec.async.parser.domain._ import amf.apicontract.internal.spec.common.emitter.SpecAwareContext import amf.apicontract.internal.spec.common.parser.SecuritySchemeParser -import amf.apicontract.internal.spec.oas.parser._ import amf.apicontract.internal.spec.oas.parser.context.OasLikeSpecVersionFactory import amf.apicontract.internal.spec.oas.parser.domain.{ - AsyncEndpointParser, OasLikeEndpointParser, OasLikeOperationParser, OasLikeSecuritySettingsParser, OasLikeServerVariableParser } import amf.shapes.internal.spec.common.parser.YMapEntryLike -import org.yaml.model.{YMap, YMapEntry, YPart} +import org.yaml.model.{YMap, YMapEntry} // TODO ASYNC complete all this trait AsyncSpecAwareContext extends SpecAwareContext {} -trait AsyncSpecVersionFactory extends OasLikeSpecVersionFactory {} +trait AsyncSpecVersionFactory extends OasLikeSpecVersionFactory { + def serversParser(map: YMap, api: AsyncApi): AsyncServersParser +} -case class Async20VersionFactory()(implicit ctx: AsyncWebApiContext) extends AsyncSpecVersionFactory { +class Async20VersionFactory()(implicit ctx: AsyncWebApiContext) extends AsyncSpecVersionFactory { override def serverVariableParser(entry: YMapEntry, parent: String): OasLikeServerVariableParser = AsyncServerVariableParser(entry, parent)(ctx) - override def operationParser(entry: YMapEntry, adopt: Operation => Operation): OasLikeOperationParser = AsyncOperationParser(entry, adopt)(ctx) override def endPointParser(entry: YMapEntry, parentId: String, collector: List[EndPoint]): OasLikeEndpointParser = - AsyncEndpointParser(entry, parentId, collector)(ctx) - + new Async20EndpointParser(entry, parentId, collector)(ctx) override def securitySchemeParser: (YMapEntryLike, SecurityScheme => SecurityScheme) => SecuritySchemeParser = Async2SecuritySchemeParser.apply - override def securitySettingsParser(map: YMap, scheme: SecurityScheme): OasLikeSecuritySettingsParser = new Async2SecuritySettingsParser(map, scheme) + override def serversParser(map: YMap, api: AsyncApi): AsyncServersParser = new Async20ServersParser(map, api) } + +object Async20VersionFactory { + def apply()(implicit ctx: AsyncWebApiContext): Async20VersionFactory = new Async20VersionFactory()(ctx) +} + +class Async23VersionFactory()(implicit ctx: AsyncWebApiContext) extends Async20VersionFactory { + override def endPointParser(entry: YMapEntry, parentId: String, collector: List[EndPoint]): OasLikeEndpointParser = + new Async23EndpointParser(entry, parentId, collector)(ctx) + override def serversParser(map: YMap, api: AsyncApi): AsyncServersParser = new Async23ServersParser(map, api) +} + +object Async23VersionFactory { + def apply()(implicit ctx: AsyncWebApiContext): Async23VersionFactory = new Async23VersionFactory()(ctx) +} \ No newline at end of file diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/document/AsyncApiDocumentParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/document/AsyncApiDocumentParser.scala index ae5a4cf792..37de9c69ac 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/document/AsyncApiDocumentParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/document/AsyncApiDocumentParser.scala @@ -1,11 +1,10 @@ package amf.apicontract.internal.spec.async.parser.document import amf.apicontract.client.scala.model.document.APIContractProcessingData -import amf.apicontract.client.scala.model.domain.api.AsyncApi import amf.apicontract.client.scala.model.domain.EndPoint +import amf.apicontract.client.scala.model.domain.api.AsyncApi import amf.apicontract.internal.metamodel.domain.api.WebApiModel import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext -import amf.apicontract.internal.spec.async.parser.domain._ import amf.apicontract.internal.spec.async.parser.domain.declarations.AsyncDeclarationParser import amf.apicontract.internal.spec.common.parser._ import amf.apicontract.internal.spec.oas.parser.document.OasLikeDeclarationsHelper @@ -86,7 +85,7 @@ abstract class AsyncApiDocumentParser(root: Root, spec: Spec, declarationParser: map.key( "servers", entry => { - val servers = AsyncServersParser(entry.value.as[YMap], api).parse() + val servers = ctx.factory.serversParser(entry.value.as[YMap], api).parse() api.setWithoutId(WebApiModel.Servers, AmfArray(servers, Annotations(entry.value)), Annotations(entry)) } ) diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/Async20EndpointParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/Async20EndpointParser.scala new file mode 100644 index 0000000000..c0c989c364 --- /dev/null +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/Async20EndpointParser.scala @@ -0,0 +1,132 @@ +package amf.apicontract.internal.spec.async.parser.domain + +import amf.apicontract.client.scala.model.domain.{EndPoint, Operation} +import amf.apicontract.internal.metamodel.domain.EndPointModel +import amf.apicontract.internal.spec.async.parser.bindings.AsyncChannelBindingsParser +import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext +import amf.apicontract.internal.spec.common.WebApiDeclarations.ErrorChannel +import amf.apicontract.internal.spec.oas.parser.domain.OasLikeEndpointParser +import amf.apicontract.internal.spec.spec.OasDefinitions +import amf.core.client.scala.model.domain.{AmfArray, AmfScalar} +import amf.core.internal.parser.YMapOps +import amf.core.internal.parser.domain.{Annotations, ScalarNode, SearchScope} +import amf.core.internal.validation.CoreValidations +import amf.shapes.internal.spec.common.parser.{AnnotationParser, YMapEntryLike} +import org.yaml.model.{YMap, YMapEntry, YNode} + +import scala.collection.mutable + +class Async20EndpointParser(entry: YMapEntry, parentId: String, collector: List[EndPoint])( + override implicit val ctx: AsyncWebApiContext +) extends OasLikeEndpointParser(entry, parentId, collector) { + + override type ConcreteContext = AsyncWebApiContext + + override def apply(entry: YMapEntry, parentId: String, collector: List[EndPoint])( + ctx: ConcreteContext + ): Async20EndpointParser = new Async20EndpointParser(entry, parentId, collector)(ctx) + + override protected def parseEndpointMap(endpoint: EndPoint, map: YMap): EndPoint = { + + super.parseEndpointMap(endpoint, map) + + map.key("bindings").foreach { entry => + val bindings = AsyncChannelBindingsParser(YMapEntryLike(entry.value)).parse() + endpoint.setWithoutId(EndPointModel.Bindings, bindings, Annotations(entry)) + + AnnotationParser(endpoint, map).parseOrphanNode("bindings") + } + + map.key("description", EndPointModel.Description in endpoint) + map.key( + "parameters", + entry => { + val parameters = AsyncParametersParser(endpoint.id, entry.value.as[YMap]).parse() + endpoint.fields + .setWithoutId(EndPointModel.Parameters, AmfArray(parameters, Annotations(entry.value)), Annotations(entry)) + } + ) + + map.regex( + "subscribe|publish", + entries => { + val operations = mutable.ListBuffer[Operation]() + entries.foreach { entry => + val operationParser = ctx.factory.operationParser(entry, (o: Operation) => o) + operations += operationParser.parse() + } + endpoint.setWithoutId(EndPointModel.Operations, AmfArray(operations, Annotations(map)), Annotations(map)) + } + ) + + endpoint + } +} + +class Async23EndpointParser( + entry: YMapEntry, + parentId: String, + collector: List[EndPoint] +)( + override implicit val ctx: AsyncWebApiContext +) extends Async20EndpointParser(entry, parentId, collector) { + + override protected def parseEndpoint(endpoint: EndPoint): Option[EndPoint] = { + Some(parseEndpointMap(endpoint, entry.value.as[YMap])) // this is to avoid general ref detection of OasLikeParser. I want to use custom link detection for declarations for Async 2.3 + } + + override protected def parseEndpointMap(endpoint: EndPoint, map: YMap): EndPoint = { + ctx.link(map) match { + case Left(fullRef) => handleRef(fullRef, map, endpoint) + case Right(_) => super.parseEndpointMap(endpoint, map) + } + } + + private def handleRef(fullRef: String, map: YMap, endpoint: EndPoint): EndPoint = { + val entryLike = YMapEntryLike(map) + val label = OasDefinitions.stripOas3ComponentsPrefix(fullRef, "channels") + ctx.declarations + .findChannel(label, SearchScope.Named) + .map(channel => { + nameAndAdopt(generateLink(label, channel, entryLike), entryLike.key).withPath(endpoint.path.value()) + }) + .getOrElse(remote(fullRef, entryLike, endpoint)) + } + + private def remote(fullRef: String, entryLike: YMapEntryLike, endpoint: EndPoint)(implicit ctx: AsyncWebApiContext): EndPoint = { + ctx.navigateToRemoteYNode(fullRef) match { + case Some(result) => + val serverNode = result.remoteNode + val external = parseEndpointMap(endpoint, serverNode.as[YMap]) + nameAndAdopt( + external.link(AmfScalar(fullRef), entryLike.annotations, Annotations.synthesized()), + entryLike.key + ) // check if this link should be trimmed to just the label + case None => + ctx.eh.violation( + CoreValidations.UnresolvedReference, + "", + s"Cannot find link reference $fullRef", + entryLike.asMap.location + ) + val errorChannel = ErrorChannel(fullRef, entryLike.asMap) + nameAndAdopt(errorChannel.link(fullRef, errorChannel.annotations), entryLike.key) + } + } + + private def generateLink(label: String, effectiveTarget: EndPoint, entryLike: YMapEntryLike): EndPoint = { + val endPoint = EndPoint(entryLike.annotations) + val hash = s"${endPoint.id}$label".hashCode + endPoint + .withId(s"${endPoint.id}/link-$hash") + .withLinkTarget(effectiveTarget) + .withLinkLabel(label, Annotations(entryLike.value)) + } + + def nameAndAdopt(s: EndPoint, key: Option[YNode]): EndPoint = { + key foreach { k => + s.setWithoutId(EndPointModel.Name, ScalarNode(k).string(), Annotations(k)) + } + s + } +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/AsyncServerParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/AsyncServerParser.scala index 9c32cdc255..05028e3bf1 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/AsyncServerParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/AsyncServerParser.scala @@ -6,20 +6,25 @@ import amf.apicontract.client.scala.model.domain.security.SecurityRequirement import amf.apicontract.internal.metamodel.domain.ServerModel import amf.apicontract.internal.spec.async.parser.bindings.AsyncServerBindingsParser import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext +import amf.apicontract.internal.spec.common.WebApiDeclarations.ErrorServer import amf.apicontract.internal.spec.common.parser.OasLikeSecurityRequirementParser import amf.apicontract.internal.spec.oas.parser.domain.OasLikeServerParser +import amf.apicontract.internal.spec.spec.OasDefinitions import amf.core.client.scala.model.domain.{AmfArray, AmfScalar} import amf.core.internal.parser.YMapOps -import amf.core.internal.parser.domain.Annotations +import amf.core.internal.parser.domain.{Annotations, ScalarNode, SearchScope} import amf.core.internal.utils.IdCounter +import amf.core.internal.validation.CoreValidations import amf.shapes.internal.spec.common.parser.{AnnotationParser, YMapEntryLike} -import org.yaml.model.{YMap, YMapEntry, YNode} +import org.yaml.model.{YMap, YNode} -case class AsyncServersParser(map: YMap, api: AsyncApi)(implicit val ctx: AsyncWebApiContext) { +abstract class AsyncServersParser(map: YMap, api: AsyncApi)(implicit val ctx: AsyncWebApiContext) { + + protected def serverParser(entryLike: YMapEntryLike): OasLikeServerParser def parse(): Seq[Server] = { map.entries.map { entry => - AsyncServerParser(api.id, entry) + serverParser(YMapEntryLike(entry)) .parse() .setWithoutId( ServerModel.Name, @@ -30,8 +35,23 @@ case class AsyncServersParser(map: YMap, api: AsyncApi)(implicit val ctx: AsyncW } } -private case class AsyncServerParser(parent: String, entry: YMapEntry)(implicit override val ctx: AsyncWebApiContext) - extends OasLikeServerParser(parent, YMapEntryLike(entry)) { +class Async20ServersParser(map: YMap, api: AsyncApi)( + override implicit val ctx: AsyncWebApiContext +) extends AsyncServersParser(map, api) { + override protected def serverParser(entryLike: YMapEntryLike): OasLikeServerParser = + new Async20ServerParser(api.id, entryLike) +} + +class Async23ServersParser(map: YMap, api: AsyncApi)( + override implicit val ctx: AsyncWebApiContext +) extends AsyncServersParser(map, api) { + override protected def serverParser(entryLike: YMapEntryLike): OasLikeServerParser = + new Async23ServerParser(api.id, entryLike) +} + +class Async20ServerParser(parent: String, entryLike: YMapEntryLike)(implicit + override val ctx: AsyncWebApiContext +) extends OasLikeServerParser(parent, entryLike) { override def parse(): Server = { val server = super.parse() @@ -50,7 +70,7 @@ private case class AsyncServerParser(parent: String, entry: YMapEntry)(implicit val idCounter = new IdCounter() val securedBy = entry.value .as[Seq[YNode]] - .flatMap(s => OasLikeSecurityRequirementParser(s, (se: SecurityRequirement) => Unit, idCounter).parse()) + .flatMap(s => OasLikeSecurityRequirementParser(s, (_: SecurityRequirement) => Unit, idCounter).parse()) server.setWithoutId(ServerModel.Security, AmfArray(securedBy, Annotations(entry.value)), Annotations(entry)) } @@ -59,3 +79,60 @@ private case class AsyncServerParser(parent: String, entry: YMapEntry)(implicit server } } + +class Async23ServerParser(parent: String, entryLike: YMapEntryLike)(implicit override val ctx: AsyncWebApiContext) + extends Async20ServerParser(parent, entryLike) { + + override def parse(): Server = { + val map: YMap = entryLike.asMap + ctx.link(map) match { + case Left(fullRef) => handleRef(fullRef) + case Right(_) => super.parse() + } + } + + private def handleRef(fullRef: String): Server = { + val label = OasDefinitions.stripOas3ComponentsPrefix(fullRef, "servers") + ctx.declarations + .findServer(label, SearchScope.Named) + .map(server => nameAndAdopt(generateLink(label, server, entryLike), entryLike.key)) + .getOrElse(remote(fullRef, entryLike)) + } + + private def remote(fullRef: String, entryLike: YMapEntryLike)(implicit ctx: AsyncWebApiContext): Server = { + ctx.navigateToRemoteYNode(fullRef) match { + case Some(result) => + val serverNode = result.remoteNode + val external = new Async23ServerParser(parent, YMapEntryLike(serverNode))(result.context).parse() + nameAndAdopt( + external.link(AmfScalar(fullRef), entryLike.annotations, Annotations.synthesized()), + entryLike.key + ) // check if this link should be trimmed to just the label + case None => + ctx.eh.violation( + CoreValidations.UnresolvedReference, + "", + s"Cannot find link reference $fullRef", + entryLike.asMap.location + ) + val errorServer = ErrorServer(fullRef, entryLike.asMap) + nameAndAdopt(errorServer.link(fullRef, errorServer.annotations), entryLike.key) + } + } + + private def generateLink(label: String, effectiveTarget: Server, entryLike: YMapEntryLike): Server = { + val server = Server(entryLike.annotations) + val hash = s"${server.id}$label".hashCode + server + .withId(s"${server.id}/link-$hash") + .withLinkTarget(effectiveTarget) + .withLinkLabel(label, Annotations(entryLike.value)) + } + + def nameAndAdopt(s: Server, key: Option[YNode]): Server = { + key foreach { k => + s.setWithoutId(ServerModel.Name, ScalarNode(k).string(), Annotations(k)) + } + s + } +} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async20DeclarationParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async20DeclarationParser.scala index 6c0911ddad..c62564f691 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async20DeclarationParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async20DeclarationParser.scala @@ -1,6 +1,6 @@ package amf.apicontract.internal.spec.async.parser.domain.declarations -import amf.aml.internal.parse.common.{DeclarationKey, DeclarationKeyCollector} +import amf.aml.internal.parse.common.DeclarationKey import amf.apicontract.client.scala.model.domain.bindings.{ ChannelBindings, MessageBindings, @@ -38,10 +38,7 @@ import amf.core.internal.parser.domain.Annotations import amf.shapes.internal.spec.common.parser.YMapEntryLike import org.yaml.model.{YMap, YMapEntry} -case class Async20DeclarationParser() - extends AsyncDeclarationParser - with DeclarationKeyCollector - with OasLikeDeclarationsHelper { +case class Async20DeclarationParser() extends AsyncDeclarationParser with OasLikeDeclarationsHelper { protected val definitionsKey = "schemas" @@ -61,7 +58,8 @@ case class Async20DeclarationParser() parseMessageTraits(map, parent + "/messageTraits") parseMessageDeclarations(map, parent + "/messages") - addDeclarationsToModel(document) + + super.parseDeclarations(map, parent, document) } private def parseMessageDeclarations(componentsMap: YMap, parent: String)(implicit ctx: AsyncWebApiContext): Unit = diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async23DeclarationParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async23DeclarationParser.scala index 75e3989230..e1d33af47c 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async23DeclarationParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/Async23DeclarationParser.scala @@ -1,5 +1,11 @@ package amf.apicontract.internal.spec.async.parser.domain.declarations + +import amf.aml.internal.parse.common.DeclarationKey import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext +import amf.apicontract.internal.spec.async.parser.domain.{Async23EndpointParser, Async23ServerParser} +import amf.core.internal.annotations.DeclaredElement +import amf.core.internal.parser.YMapOps +import amf.shapes.internal.spec.common.parser.YMapEntryLike import amf.core.client.scala.model.document.Document import org.yaml.model.YMap @@ -7,7 +13,38 @@ object Async23DeclarationParser extends AsyncDeclarationParser { override def parseDeclarations(map: YMap, parent: String, document: Document)(implicit ctx: AsyncWebApiContext ): Unit = { + + parseServerDeclarations(map, parent) + parseChannelDeclarations(map, parent) + Async20DeclarationParser().parseDeclarations(map, parent, document) - // TODO: add stuff.... } + + private def parseServerDeclarations(componentsMap: YMap, parent: String)(implicit ctx: AsyncWebApiContext): Unit = + componentsMap.key( + "servers", + e => { + addDeclarationKey(DeclarationKey(e)) + e.value.as[YMap].entries.foreach { entry => + val server = new Async23ServerParser(parent, YMapEntryLike(entry)).parse() + server.add(DeclaredElement()) + ctx.declarations += server + } + } + ) + + private def parseChannelDeclarations(componentsMap: YMap, parent: String)(implicit ctx: AsyncWebApiContext): Unit = + componentsMap.key( + "channels", + e => { + addDeclarationKey(DeclarationKey(e)) + e.value.as[YMap].entries.foreach { entry => + val channel = new Async23EndpointParser(entry, parent, Nil).parse() + channel.foreach(c => { + c.add(DeclaredElement()) + ctx.declarations += c + }) + } + } + ) } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/AsyncDeclarationParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/AsyncDeclarationParser.scala index cac3df159b..f9729932ef 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/AsyncDeclarationParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/async/parser/domain/declarations/AsyncDeclarationParser.scala @@ -1,9 +1,12 @@ package amf.apicontract.internal.spec.async.parser.domain.declarations +import amf.aml.internal.parse.common.DeclarationKeyCollector import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext import amf.core.client.scala.model.document.Document import org.yaml.model.YMap -trait AsyncDeclarationParser { - def parseDeclarations(map: YMap, parent: String, document: Document)(implicit ctx: AsyncWebApiContext): Unit +trait AsyncDeclarationParser extends DeclarationKeyCollector { + def parseDeclarations(map: YMap, parent: String, document: Document)(implicit ctx: AsyncWebApiContext): Unit = { + addDeclarationsToModel(document) + } } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/common/WebApiDeclarations.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/common/WebApiDeclarations.scala index 01a08cbeeb..0f7efd6d43 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/common/WebApiDeclarations.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/common/WebApiDeclarations.scala @@ -60,6 +60,8 @@ class WebApiDeclarations( var serverBindings: Map[String, ServerBindings] = Map() var operationTraits: Map[String, Operation] = Map() var messageTraits: Map[String, Message] = Map() + var servers: Map[String, Server] = Map() + var channels: Map[String, EndPoint] = Map() var others: Map[String, BaseUnit] = Map() override def addLibrary(alias: String, declarations: Declarations): Unit = { @@ -92,6 +94,8 @@ class WebApiDeclarations( responses.foreach { case (k, s) => merged.responses += (k -> s) } other.responses.foreach { case (k, s) => merged.responses += (k -> s) } extensions.foreach { case (k, s) => merged.extensions = merged.extensions + (k -> s) } + servers.foreach { case (k, s) => merged.servers = merged.servers + (k -> s) } + channels.foreach { case (k, s) => merged.channels = merged.channels + (k -> s) } } def merge(other: WebApiDeclarations): WebApiDeclarations = { @@ -123,6 +127,8 @@ class WebApiDeclarations( next.serverBindings = serverBindings next.operationTraits = operationTraits next.messageTraits = next.messageTraits + next.servers = next.servers + next.channels = next.channels next.others = others next } @@ -163,12 +169,15 @@ class WebApiDeclarations( channelBindings = channelBindings + (indexKey -> b) case b: OperationBindings => operationBindings = operationBindings + (indexKey -> b) - case c: Callback => callbacks.get(indexKey) match { case Some(prev) => callbacks = callbacks + (indexKey -> (c :: prev)) case None => callbacks = callbacks + (indexKey -> List(c)) } + case s: Server => + servers = servers + (indexKey -> s) + case c: EndPoint => + channels = channels + (c.path.value() -> c) case _ => super.+=(indexKey, element) } this @@ -215,7 +224,7 @@ class WebApiDeclarations( override def declarables(): Seq[DomainElement] = super .declarables() - .toList ++ (shapes.values ++ resourceTypes.values ++ traits.values ++ parameters.values ++ payloads.values ++ securitySchemes.values ++ responses.values ++ examples.values ++ requests.values ++ links.values ++ callbacks.values.flatten ++ headers.values ++ correlationIds.values ++ messageBindings.values ++ operationBindings.values ++ channelBindings.values ++ serverBindings.values ++ messages.values ++ operationTraits.values ++ messageTraits.values).toList + .toList ++ (shapes.values ++ resourceTypes.values ++ traits.values ++ parameters.values ++ payloads.values ++ securitySchemes.values ++ responses.values ++ examples.values ++ requests.values ++ links.values ++ callbacks.values.flatten ++ headers.values ++ correlationIds.values ++ messageBindings.values ++ operationBindings.values ++ channelBindings.values ++ serverBindings.values ++ messages.values ++ operationTraits.values ++ messageTraits.values ++ servers.values ++ channels.values).toList def findParameterOrError(ast: YPart)(key: String, scope: SearchScope.Scope): Parameter = findParameter(key, scope) match { @@ -311,6 +320,12 @@ class WebApiDeclarations( case _ => None } + def findServer(key: String, scope: SearchScope.Scope): Option[Server] = + findForType(key, _.asInstanceOf[WebApiDeclarations].servers, scope) collect { case s: Server => s } + + def findChannel(key: String, scope: SearchScope.Scope): Option[EndPoint] = + findForType(key, _.asInstanceOf[WebApiDeclarations].channels, scope) collect { case c: EndPoint => c } + def findResourceTypeOrError(ast: YPart)(key: String, scope: SearchScope.Scope): ResourceType = findResourceType(key, scope) match { case Some(result) => result @@ -583,4 +598,24 @@ object WebApiDeclarations { override protected def newErrorInstance: ErrorDeclaration[RequestModel.type] = ErrorRequest(idPart, ast) override val model: RequestModel.type = RequestModel } + + case class ErrorServer(idPart: String, ast: YPart) + extends Server(Fields(), Annotations(ast)) + with ErrorDeclaration[ServerModel.type] { + override val namespace: String = "http://amferror.com/#errorServer/" + withId(idPart) + + override protected def newErrorInstance: ErrorDeclaration[ServerModel.type] = ErrorServer(idPart, ast) + override val model: ServerModel.type = ServerModel + } + + case class ErrorChannel(idPart: String, ast: YPart) + extends EndPoint(Fields(), Annotations(ast)) + with ErrorDeclaration[EndPointModel.type] { + override val namespace: String = "http://amferror.com/#errorChannel/" + withId(idPart) + + override protected def newErrorInstance: ErrorDeclaration[EndPointModel.type] = ErrorChannel(idPart, ast) + override val model: EndPointModel.type = EndPointModel + } } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/emitter/domain/OasTagToReferenceEmitter.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/emitter/domain/OasTagToReferenceEmitter.scala index dbc71c7b52..a4c41dd34f 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/emitter/domain/OasTagToReferenceEmitter.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/emitter/domain/OasTagToReferenceEmitter.scala @@ -41,6 +41,8 @@ case class OasTagToReferenceEmitter(link: DomainElement)(implicit val specContex case _: OperationBindings => appendOas3ComponentsPrefix(referenceLabel, "operationBindings") case _: ChannelBindings => appendOas3ComponentsPrefix(referenceLabel, "channelBindings") case _: MessageBindings => appendOas3ComponentsPrefix(referenceLabel, "messageBindings") + case _: Server => appendOas3ComponentsPrefix(referenceLabel, "servers") + case _: EndPoint => appendOas3ComponentsPrefix(referenceLabel, "channels") case _ => super.getRefUrlFor(element, default) } diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/AsyncEndpointParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/AsyncEndpointParser.scala deleted file mode 100644 index c1fb1846e8..0000000000 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/AsyncEndpointParser.scala +++ /dev/null @@ -1,63 +0,0 @@ -package amf.apicontract.internal.spec.oas.parser.domain - -import amf.apicontract.client.scala.model.domain.{EndPoint, Operation} -import amf.apicontract.internal.metamodel.domain.EndPointModel -import amf.apicontract.internal.spec.async.parser.bindings.AsyncChannelBindingsParser -import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext -import amf.apicontract.internal.spec.async.parser.domain.AsyncParametersParser -import amf.core.client.scala.model.domain.AmfArray -import amf.core.internal.parser.YMapOps -import amf.core.internal.parser.domain.Annotations -import amf.shapes.internal.spec.common.parser.{AnnotationParser, YMapEntryLike} -import org.yaml.model.{YMap, YMapEntry} - -import scala.collection.mutable - -case class AsyncEndpointParser(entry: YMapEntry, parentId: String, collector: List[EndPoint])( - override implicit val ctx: AsyncWebApiContext -) extends OasLikeEndpointParser(entry, parentId, collector) { - - override type ConcreteContext = AsyncWebApiContext - - override def apply(entry: YMapEntry, parentId: String, collector: List[EndPoint])( - ctx: ConcreteContext - ): AsyncEndpointParser = { - AsyncEndpointParser(entry, parentId, collector)(ctx) - } - - override protected def parseEndpointMap(endpoint: EndPoint, map: YMap): EndPoint = { - - super.parseEndpointMap(endpoint, map) - - map.key("bindings").foreach { entry => - val bindings = AsyncChannelBindingsParser(YMapEntryLike(entry.value)).parse() - endpoint.setWithoutId(EndPointModel.Bindings, bindings, Annotations(entry)) - - AnnotationParser(endpoint, map).parseOrphanNode("bindings") - } - - map.key("description", EndPointModel.Description in endpoint) - map.key( - "parameters", - entry => { - val parameters = AsyncParametersParser(endpoint.id, entry.value.as[YMap]).parse() - endpoint.fields - .setWithoutId(EndPointModel.Parameters, AmfArray(parameters, Annotations(entry.value)), Annotations(entry)) - } - ) - - map.regex( - "subscribe|publish", - entries => { - val operations = mutable.ListBuffer[Operation]() - entries.foreach { entry => - val operationParser = ctx.factory.operationParser(entry, (o: Operation) => o) - operations += operationParser.parse() - } - endpoint.setWithoutId(EndPointModel.Operations, AmfArray(operations, Annotations(map)), Annotations(map)) - } - ) - - endpoint - } -} diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/OasLikeEndpointParser.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/OasLikeEndpointParser.scala index a6d402868e..20742eea42 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/OasLikeEndpointParser.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/spec/oas/parser/domain/OasLikeEndpointParser.scala @@ -31,7 +31,7 @@ abstract class OasLikeEndpointParser(entry: YMapEntry, parentId: String, collect parseEndpoint(endpoint) } - private def parseEndpoint(endpoint: EndPoint): Option[EndPoint] = + protected def parseEndpoint(endpoint: EndPoint): Option[EndPoint] = ctx.link(entry.value) match { case Left(value) => ctx.navigateToRemoteYNode(value) match { diff --git a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/transformation/stages/WebApiReferenceResolutionStage.scala b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/transformation/stages/WebApiReferenceResolutionStage.scala index f0fbb5d608..9214870121 100644 --- a/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/transformation/stages/WebApiReferenceResolutionStage.scala +++ b/amf-api-contract/shared/src/main/scala/amf/apicontract/internal/transformation/stages/WebApiReferenceResolutionStage.scala @@ -1,6 +1,6 @@ package amf.apicontract.internal.transformation.stages -import amf.apicontract.client.scala.model.domain.{Message, Parameter, Request, Response} +import amf.apicontract.client.scala.model.domain.{EndPoint, Message, Parameter, Request, Response, Server} import amf.apicontract.internal.metamodel.domain.MessageModel import amf.core.client.scala.model.domain.{DomainElement, Linkable} import amf.core.internal.transform.stages.ReferenceResolutionStage @@ -38,6 +38,20 @@ class WebApiReferenceResolutionStage(keepEditingInfo: Boolean = false) domain case _ => domain } + case sourceServer: Server => + domain match { + case server: Server => + val copy = server.copyElement().asInstanceOf[Server] + copy.withId(sourceServer.id).withName(sourceServer.name.value()) + case _ => domain + } + case sourceEndpoint: EndPoint => // asyncApi channel + domain match { + case channel: EndPoint => + val copy = channel.copyElement().asInstanceOf[EndPoint] + copy.withId(sourceEndpoint.id).withName(sourceEndpoint.name.value()).withPath(sourceEndpoint.path.value()) + case _ => domain + } case _ => domain } } diff --git a/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.expanded.jsonld b/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.expanded.jsonld new file mode 100644 index 0000000000..dae10a5905 --- /dev/null +++ b/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.expanded.jsonld @@ -0,0 +1,214 @@ +[ + { + "@id": "", + "@type": [ + "doc:Document", + "doc:Fragment", + "doc:Module", + "doc:Unit" + ], + "doc:encodes": [ + { + "@id": "#4", + "@type": [ + "apiContract:AsyncAPI", + "apiContract:API", + "doc:RootDomainElement", + "doc:DomainElement" + ], + "core:name": [ + { + "@value": "components-2.3" + } + ], + "apiContract:server": [ + { + "@id": "#6", + "@type": [ + "apiContract:Server", + "doc:DomainElement" + ], + "core:name": [ + { + "@value": "production" + } + ], + "core:urlTemplate": [ + { + "@value": "http://localhost:5000/ws" + } + ], + "apiContract:protocol": [ + { + "@value": "ws" + } + ], + "smaps": { + "resolved-link-target": { + "#6": "amf://id#1" + }, + "declared-element": { + "#6": "" + }, + "lexical": { + "apiContract:protocol": "[(9,6)-(10,0)]", + "#6": "[(7,4)-(10,0)]", + "core:urlTemplate": "[(8,6)-(9,0)]" + }, + "resolved-link": { + "#6": "amf://id#6" + } + } + } + ], + "core:version": [ + { + "@value": "1.0.0" + } + ], + "apiContract:endpoint": [ + { + "@id": "#5", + "@type": [ + "apiContract:EndPoint", + "doc:DomainElement" + ], + "apiContract:path": [ + { + "@value": "some/events" + } + ], + "core:name": [ + { + "@value": "null" + } + ], + "core:description": [ + { + "@value": "mychannel" + } + ], + "smaps": { + "resolved-link-target": { + "#5": "amf://id#2" + }, + "declared-element": { + "#5": "" + }, + "lexical": { + "core:description": "[(12,6)-(13,0)]", + "#5": "[(11,4)-(13,0)]" + }, + "resolved-link": { + "#5": "amf://id#5" + } + } + } + ], + "smaps": { + "lexical": { + "apiContract:endpoint": "[(16,0)-(18,43)]", + "apiContract:server": "[(13,0)-(16,0)]", + "#4": "[(1,0)-(18,43)]", + "core:name": "[(3,2)-(4,0)]", + "core:version": "[(4,2)-(5,0)]" + } + } + } + ], + "doc:root": [ + { + "@value": true + } + ], + "doc:processingData": [ + { + "@id": "#3", + "@type": [ + "doc:APIContractProcessingData" + ], + "apiContract:modelVersion": [ + { + "@value": "3.8.2" + } + ], + "doc:transformed": [ + { + "@value": true + } + ], + "doc:sourceSpec": [ + { + "@value": "ASYNC 2.3" + } + ] + } + ], + "doc:declares": [ + { + "@id": "#1", + "@type": [ + "apiContract:Server", + "doc:DomainElement" + ], + "core:name": [ + { + "@value": "myserver" + } + ], + "core:urlTemplate": [ + { + "@value": "http://localhost:5000/ws" + } + ], + "apiContract:protocol": [ + { + "@value": "ws" + } + ], + "smaps": { + "declared-element": { + "#1": "" + }, + "lexical": { + "apiContract:protocol": "[(9,6)-(10,0)]", + "#1": "[(7,4)-(10,0)]", + "core:urlTemplate": "[(8,6)-(9,0)]" + } + } + }, + { + "@id": "#2", + "@type": [ + "apiContract:EndPoint", + "doc:DomainElement" + ], + "apiContract:path": [ + { + "@value": "myChannel" + } + ], + "core:description": [ + { + "@value": "mychannel" + } + ], + "smaps": { + "declared-element": { + "#2": "" + }, + "lexical": { + "core:description": "[(12,6)-(13,0)]", + "#2": "[(11,4)-(13,0)]" + } + } + } + ], + "@context": { + "@base": "amf://id", + "doc": "http://a.ml/vocabularies/document#", + "apiContract": "http://a.ml/vocabularies/apiContract#", + "core": "http://a.ml/vocabularies/core#" + } + } +] diff --git a/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.flattened.jsonld b/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.flattened.jsonld new file mode 100644 index 0000000000..0f05816136 --- /dev/null +++ b/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.flattened.jsonld @@ -0,0 +1,162 @@ +{ + "@graph": [ + { + "@id": "#3", + "@type": [ + "doc:APIContractProcessingData" + ], + "apiContract:modelVersion": "3.8.2", + "doc:transformed": true, + "doc:sourceSpec": "ASYNC 2.3" + }, + { + "@id": "#4", + "@type": [ + "apiContract:AsyncAPI", + "apiContract:API", + "doc:RootDomainElement", + "doc:DomainElement" + ], + "core:name": "components-2.3", + "apiContract:server": [ + { + "@id": "#6" + } + ], + "core:version": "1.0.0", + "apiContract:endpoint": [ + { + "@id": "#5" + } + ], + "smaps": { + "lexical": { + "apiContract:endpoint": "[(16,0)-(18,43)]", + "apiContract:server": "[(13,0)-(16,0)]", + "#4": "[(1,0)-(18,43)]", + "core:name": "[(3,2)-(4,0)]", + "core:version": "[(4,2)-(5,0)]" + } + } + }, + { + "@id": "#6", + "@type": [ + "apiContract:Server", + "doc:DomainElement" + ], + "core:name": "production", + "core:urlTemplate": "http://localhost:5000/ws", + "apiContract:protocol": "ws", + "smaps": { + "resolved-link-target": { + "#6": "amf://id#1" + }, + "declared-element": { + "#6": "" + }, + "lexical": { + "apiContract:protocol": "[(9,6)-(10,0)]", + "#6": "[(7,4)-(10,0)]", + "core:urlTemplate": "[(8,6)-(9,0)]" + }, + "resolved-link": { + "#6": "amf://id#6" + } + } + }, + { + "@id": "#5", + "@type": [ + "apiContract:EndPoint", + "doc:DomainElement" + ], + "apiContract:path": "some/events", + "core:name": "null", + "core:description": "mychannel", + "smaps": { + "resolved-link-target": { + "#5": "amf://id#2" + }, + "declared-element": { + "#5": "" + }, + "lexical": { + "core:description": "[(12,6)-(13,0)]", + "#5": "[(11,4)-(13,0)]" + }, + "resolved-link": { + "#5": "amf://id#5" + } + } + }, + { + "@id": "", + "doc:declares": [ + { + "@id": "#1" + }, + { + "@id": "#2" + } + ], + "@type": [ + "doc:Document", + "doc:Fragment", + "doc:Module", + "doc:Unit" + ], + "doc:encodes": { + "@id": "#4" + }, + "doc:root": true, + "doc:processingData": { + "@id": "#3" + } + }, + { + "@id": "#1", + "@type": [ + "apiContract:Server", + "doc:DomainElement" + ], + "core:name": "myserver", + "core:urlTemplate": "http://localhost:5000/ws", + "apiContract:protocol": "ws", + "smaps": { + "declared-element": { + "#1": "" + }, + "lexical": { + "apiContract:protocol": "[(9,6)-(10,0)]", + "#1": "[(7,4)-(10,0)]", + "core:urlTemplate": "[(8,6)-(9,0)]" + } + } + }, + { + "@id": "#2", + "@type": [ + "apiContract:EndPoint", + "doc:DomainElement" + ], + "apiContract:path": "myChannel", + "core:description": "mychannel", + "smaps": { + "declared-element": { + "#2": "" + }, + "lexical": { + "core:description": "[(12,6)-(13,0)]", + "#2": "[(11,4)-(13,0)]" + } + } + } + ], + "@context": { + "@base": "amf://id", + "doc": "http://a.ml/vocabularies/document#", + "apiContract": "http://a.ml/vocabularies/apiContract#", + "core": "http://a.ml/vocabularies/core#" + } +} diff --git a/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.yaml b/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.yaml new file mode 100644 index 0000000000..85c773e322 --- /dev/null +++ b/amf-cli/shared/src/test/resources/resolution/async20/async-2.3-components.yaml @@ -0,0 +1,18 @@ +asyncapi: 2.3.0 +info: + title: components-2.3 + version: 1.0.0 +components: + servers: + myserver: + url: http://localhost:5000/ws + protocol: ws + channels: + myChannel: + description: mychannel +servers: + production: + $ref: "#/components/servers/myserver" +channels: + some/events: + $ref: "#/components/channels/myChannel" \ No newline at end of file diff --git a/amf-cli/shared/src/test/resources/upanddown/cycle/async20/components/components-2.3.yaml b/amf-cli/shared/src/test/resources/upanddown/cycle/async20/components/components-2.3.yaml new file mode 100644 index 0000000000..2011244b4b --- /dev/null +++ b/amf-cli/shared/src/test/resources/upanddown/cycle/async20/components/components-2.3.yaml @@ -0,0 +1,18 @@ +asyncapi: 2.3.0 +info: + title: components-2.3 + version: 1.0.0 +components: + servers: + myserver: + url: http://localhost:5000/ws + protocol: ws + channels: + myChannel: + description: mychannel +servers: + production: + $ref: "#/components/servers/myserver" +channels: + some/events: + $ref: "#/components/channels/myChannel" diff --git a/amf-cli/shared/src/test/resources/upanddown/cycle/async20/components-cycle.yaml b/amf-cli/shared/src/test/resources/upanddown/cycle/async20/components/components-cycle.yaml similarity index 100% rename from amf-cli/shared/src/test/resources/upanddown/cycle/async20/components-cycle.yaml rename to amf-cli/shared/src/test/resources/upanddown/cycle/async20/components/components-cycle.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 73561e5ea0..5049a02712 100644 --- a/amf-cli/shared/src/test/scala/amf/emit/Async20CycleTest.scala +++ b/amf-cli/shared/src/test/scala/amf/emit/Async20CycleTest.scala @@ -125,7 +125,7 @@ class Async20CycleTest extends FunSuiteCycleTests { FixtureData("Draft 7 schemas cycle", "draft-7-schemas-cycle.yaml", "draft-7-schemas-output.yaml"), FixtureData("Security schemes", "security-schemes.yaml", "security-schemes.yaml"), FixtureData("Operation and message traits", "operation-message-traits.yaml", "operation-message-traits.yaml"), - FixtureData("components emission", "components-cycle.yaml", "components-cycle.yaml"), + FixtureData("components emission", "components/components-cycle.yaml", "components/components-cycle.yaml"), // TODO: fill async 2.x with each spec new features FixtureData("Async 2.1 doc - empty", "empty-async21.yaml", "empty-async21.yaml"), FixtureData("Async 2.2 doc - empty", "empty-async22.yaml", "empty-async22.yaml"), @@ -142,9 +142,10 @@ class Async20CycleTest extends FunSuiteCycleTests { "ibmmq binding", "bindings/ibmmq-binding.yaml", "bindings/ibmmq-binding.yaml" - ) + ), + FixtureData("Async 2.3 components", "components/components-2.3.yaml", "components/components-2.3.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/resolution/Async20ResolutionTest.scala b/amf-cli/shared/src/test/scala/amf/resolution/Async20ResolutionTest.scala index f7bb0858ea..aa77ce5cea 100644 --- a/amf-cli/shared/src/test/scala/amf/resolution/Async20ResolutionTest.scala +++ b/amf-cli/shared/src/test/scala/amf/resolution/Async20ResolutionTest.scala @@ -200,6 +200,16 @@ class Async20ResolutionTest extends ResolutionTest { ) } + multiGoldenTest("Referencing declared servers and channels in 2.3", "async-2.3-components.%s") { config => + cycle( + "async-2.3-components.yaml", + config.golden, + Async20YamlHint, + target = AmfJsonHint, + renderOptions = Some(config.renderOptions) + ) + } + override def transform(unit: BaseUnit, config: CycleConfig, amfConfig: AMFConfiguration): BaseUnit = { super.transform(unit, config, AsyncAPIConfiguration.Async20()) }