Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

W-15510077 - Some fixes detected in async 2.x #1973

Merged
merged 3 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ object Async23Syntax extends SpecSyntax {
add(
Async22Syntax.nodes,
"bindings" -> Set(Solace),
"components" -> Set(
"servers",
"channels"
),
"SolaceServerBinding" -> Set(
"msgVpn",
"bindingVersion"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ class Async23EndpointParser(
}

def nameAndAdopt(s: EndPoint, key: Option[YNode]): EndPoint = {
key foreach { k =>
s.setWithoutId(EndPointModel.Name, ScalarNode(k).string(), Annotations(k))
key match {
case Some(name) =>
s.setWithoutId(EndPointModel.Name, ScalarNode(name).string(), Annotations(name))
case None => // Nothing to do
}
s
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import amf.apicontract.client.scala.model.domain.{Message, Payload, Request, Res
import amf.apicontract.internal.annotations.ExampleIndex
import amf.apicontract.internal.metamodel.domain.MessageModel.IsAbstract
import amf.apicontract.internal.metamodel.domain.{MessageModel, OperationModel, PayloadModel}
import amf.apicontract.internal.spec.async.emitters.domain.MessageExamplePair
import amf.apicontract.internal.spec.async.parser.bindings.AsyncMessageBindingsParser
import amf.apicontract.internal.spec.async.parser.context.{Async2WebApiContext, AsyncWebApiContext}
import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext
import amf.apicontract.internal.spec.async.{MessageType, Publish, Subscribe}
import amf.apicontract.internal.spec.common.WebApiDeclarations.ErrorMessage
import amf.apicontract.internal.spec.common.WebApiDeclarations.{ErrorMessage, ErrorRequest, ErrorResponse}
import amf.apicontract.internal.spec.common.parser.SpecParserOps
import amf.apicontract.internal.spec.oas.parser.domain
import amf.apicontract.internal.spec.spec.OasDefinitions
Expand All @@ -26,7 +25,6 @@ import amf.shapes.internal.domain.resolution.ExampleTracking.tracking
import amf.shapes.internal.spec.common.JSONSchemaDraft7SchemaVersion
import amf.shapes.internal.spec.common.parser._
import org.yaml.model.{YMap, YMapEntry, YNode, YSequence}
import amf.apicontract.internal.validation.definitions.ParserSideValidations.DuplicatedMessageId

trait AsyncMessageParser {
def parse(): Message
Expand All @@ -35,7 +33,7 @@ trait AsyncMessageParser {
object Async20MessageParser {

def apply(entryLike: YMapEntryLike, parent: String, messageType: Option[MessageType], isTrait: Boolean = false)(
implicit ctx: AsyncWebApiContext
implicit ctx: AsyncWebApiContext
): AsyncMessageParser = {
val populator = if (isTrait) Async20MessageTraitPopulator() else Async20ConcreteMessagePopulator(parent)
val finder = if (isTrait) MessageTraitFinder() else MessageFinder()
Expand All @@ -45,7 +43,7 @@ object Async20MessageParser {

object Async21MessageParser {
def apply(entryLike: YMapEntryLike, parent: String, messageType: Option[MessageType], isTrait: Boolean = false)(
implicit ctx: AsyncWebApiContext
implicit ctx: AsyncWebApiContext
): AsyncMessageParser = {
val populator = if (isTrait) Async21MessageTraitPopulator() else Async21ConcreteMessagePopulator(parent)
val finder = if (isTrait) MessageTraitFinder() else MessageFinder()
Expand All @@ -64,15 +62,15 @@ object Async24MessageParser {
}

class Async20MessageParser(
entryLike: YMapEntryLike,
parent: String,
messageType: Option[MessageType],
populator: Async2MessagePopulator,
finder: Finder[Message],
isTrait: Boolean
)(implicit val ctx: AsyncWebApiContext)
extends AsyncMessageParser
with SpecParserOps {
entryLike: YMapEntryLike,
parent: String,
messageType: Option[MessageType],
populator: Async2MessagePopulator,
finder: Finder[Message],
isTrait: Boolean
)(implicit val ctx: AsyncWebApiContext)
extends AsyncMessageParser
with SpecParserOps {

def parse(): Message = {
val map: YMap = entryLike.asMap
Expand All @@ -92,6 +90,12 @@ class Async20MessageParser(
case None => Message(annotations)
}

private def buildErrorMessage(fullRef: String): Message = messageType match {
case Some(Publish) => ErrorRequest(fullRef, entryLike.asMap)
case Some(Subscribe) => ErrorResponse(fullRef, entryLike.asMap)
case None => new ErrorMessage(fullRef, entryLike.asMap, isTrait)
}

def nameAndAdopt(m: Message, key: Option[YNode]): Message = {
key foreach { k =>
m.setWithoutId(MessageModel.Name, ScalarNode(k).string(), Annotations(k))
Expand Down Expand Up @@ -121,7 +125,7 @@ class Async20MessageParser(
s"Cannot find link reference $fullRef",
Annotations(entryLike.asMap)
)
val errorMessage = new ErrorMessage(fullRef, entryLike.asMap, isTrait)
val errorMessage = buildErrorMessage(fullRef)
nameAndAdopt(errorMessage.link(fullRef, errorMessage.annotations), entryLike.key)
}
}
Expand All @@ -138,7 +142,7 @@ class Async20MessageParser(
}

case class AsyncMultipleMessageParser(map: YMap, parent: String, messageType: MessageType)(implicit
val ctx: AsyncWebApiContext
val ctx: AsyncWebApiContext
) {
def parse(): List[Message] = {
map.key("oneOf") match {
Expand Down Expand Up @@ -321,10 +325,10 @@ abstract class Async24MessagePopulator()(implicit ctx: AsyncWebApiContext) exten
super.populate(map, message)
map.key("messageId").foreach { entry =>
val messageId = entry.value.toString()
if (!ctx.registerMessageId(messageId))
ctx.eh.violation(
ParserSideValidations.DuplicatedMessageId, message, s"Duplicated message id '$messageId'", entry.value.location
)
if (!ctx.registerMessageId(messageId))
ctx.eh.violation(
ParserSideValidations.DuplicatedMessageId, message, s"Duplicated message id '$messageId'", entry.value.location
)
parseMessageId(map, message)
}
message
Expand Down Expand Up @@ -399,13 +403,13 @@ trait AsyncConcreteMessagePopulator {
}

case class Async21ConcreteMessagePopulator(parentId: String)(implicit ctx: AsyncWebApiContext)
extends Async21MessagePopulator() with AsyncConcreteMessagePopulator {
extends Async21MessagePopulator() with AsyncConcreteMessagePopulator {
override protected def parseTraits(map: YMap, message: Message): Unit = innerParseTrait(map, message, parentId)
override protected def parseSchema(map: YMap, payload: Payload): Unit = innerParseSchema(map, payload)
}

case class Async20ConcreteMessagePopulator(parentId: String)(implicit ctx: AsyncWebApiContext)
extends Async20MessagePopulator() with AsyncConcreteMessagePopulator() {
extends Async20MessagePopulator() with AsyncConcreteMessagePopulator() {

override protected def parseTraits(map: YMap, message: Message): Unit = innerParseTrait(map, message, parentId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ case class Async20DeclarationParser() extends AsyncDeclarationParser with OasLik

override def parseDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit = {
this.parseOnlyDeclarations(map, parent, document)
addDeclarationsToModel(document)
}

override def parseOnlyDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit = {
parseSecuritySchemeDeclarations(map, parent + "/securitySchemes")
parseCorrelationIdDeclarations(map, parent + "/correlationIds")
Expand All @@ -57,8 +64,6 @@ case class Async20DeclarationParser() extends AsyncDeclarationParser with OasLik
parseMessageTraits(map, parent + "/messageTraits")

parseMessageDeclarations(map, parent + "/messages")

super.parseDeclarations(map, parent, document)
}

private def parseMessageDeclarations(componentsMap: YMap, parent: String)(implicit ctx: AsyncWebApiContext): Unit =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ import amf.core.client.scala.model.document.Document
import org.yaml.model.YMap

object Async23DeclarationParser extends AsyncDeclarationParser {

override def parseDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit = {
this.parseOnlyDeclarations(map, parent, document)
addDeclarationsToModel(document)
}

override def parseOnlyDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit = {
parseServerDeclarations(map, parent)
Async20DeclarationParser().parseOnlyDeclarations(map, parent, document)
parseChannelDeclarations(map, parent)

Async20DeclarationParser().parseDeclarations(map, parent, document)
}

private def parseServerDeclarations(componentsMap: YMap, parent: String)(implicit ctx: AsyncWebApiContext): Unit =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,35 @@ package amf.apicontract.internal.spec.async.parser.domain.declarations
import amf.aml.internal.parse.common.DeclarationKey
import amf.aml.internal.parse.dialects.DialectAstOps.DialectYMapOps
import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext
import amf.apicontract.internal.spec.async.parser.domain.{Async20ServerVariableParser, Async24ServerVariableParser}
import amf.core.client.scala.model.document.Document
import amf.core.internal.annotations.{DeclaredElement, DeclaredServerVariable}
import amf.shapes.internal.spec.common.parser.YMapEntryLike
import org.yaml.model.YMap

object Async24DeclarationParser extends AsyncDeclarationParser {

override def parseDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit = {
this.parseOnlyDeclarations(map, parent, document)
addDeclarationsToModel(document)
}

override def parseOnlyDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit = {
parseServerVariableDeclarations(map, parent)
Async23DeclarationParser.parseDeclarations(map, parent, document)
Async23DeclarationParser.parseOnlyDeclarations(map, parent, document)
}

private def parseServerVariableDeclarations(componentsMap: YMap, parent: String)(implicit ctx: AsyncWebApiContext): Unit =
private def parseServerVariableDeclarations(componentsMap: YMap, parent: String)(implicit
ctx: AsyncWebApiContext
): Unit =
componentsMap.key(
"serverVariables",
entry => {
addDeclarationKey(DeclarationKey(entry))
entry.value.as[YMap].entries.foreach { entry =>
val serverVariable = ctx.factory.serverVariableParser(entry, parent).parse()
val serverVariable = ctx.factory.serverVariableParser(entry, parent).parse()
serverVariable.add(DeclaredElement())
serverVariable.add(DeclaredServerVariable())
ctx.declarations += serverVariable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ trait AsyncDeclarationParser extends DeclarationKeyCollector {
def parseDeclarations(map: YMap, parent: String, document: Document)(implicit ctx: AsyncWebApiContext): Unit = {
addDeclarationsToModel(document)
}

// Parse the declarations but not add them to the model
def parseOnlyDeclarations(map: YMap, parent: String, document: Document)(implicit
ctx: AsyncWebApiContext
): Unit
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package amf.apicontract.internal.spec.oas.parser.document

import amf.aml.internal.parse.common.DeclarationKeyCollector
import amf.apicontract.client.scala.model.domain.EndPoint
import amf.apicontract.internal.spec.oas.parser.context.OasLikeWebApiContext
import amf.apicontract.internal.validation.definitions.ParserSideValidations
import amf.core.client.scala.model.domain.NamedDomainElement
import amf.core.client.scala.model.domain.{DomainElement, NamedDomainElement}
import amf.shapes.client.scala.model.domain.AnyShape
import amf.shapes.internal.spec.common.parser.TypeDeclarationParser
import org.yaml.model.YMap
Expand All @@ -27,20 +28,20 @@ trait OasLikeDeclarationsHelper {
def validateNames()(implicit ctx: OasLikeWebApiContext): Unit = {
val declarations = ctx.declarations.declarables()
val keyRegex = """^[a-zA-Z0-9\.\-_]+$""".r
declarations.foreach {
case elem: NamedDomainElement =>
elem.name.option() match {
case Some(name) =>
if (!keyRegex.pattern.matcher(name).matches())
violation(
elem,
s"Name $name does not match regular expression ${keyRegex.toString()} for component declarations"
)
case None =>
violation(elem, "No name is defined for given component declaration")
}
case _ =>
declarations.foreach { case elem: NamedDomainElement =>
elem.name.option() match {
case Some(name) =>
if (!keyRegex.pattern.matcher(name).matches())
violation(
elem,
s"Name $name does not match regular expression ${keyRegex.toString()} for component declarations"
)
case None if !allowedNoNameDeclarations(elem) =>
violation(elem, "No name is defined for given component declaration")
case _ => // Nothing to do
}
}

def violation(elem: NamedDomainElement, msg: String): Unit = {
ctx.eh.violation(
ParserSideValidations.InvalidFieldNameInComponents,
Expand All @@ -50,4 +51,9 @@ trait OasLikeDeclarationsHelper {
)
}
}

private def allowedNoNameDeclarations(declaration: DomainElement): Boolean = declaration match {
case _: EndPoint => true
case _ => false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ class WebApiReferenceResolutionStage(keepEditingInfo: Boolean = false)
domain match {
case channel: EndPoint =>
val copy = channel.copyElement().asInstanceOf[EndPoint]
copy.withId(sourceEndpoint.id).withName(sourceEndpoint.name.value()).withPath(sourceEndpoint.path.value())
sourceEndpoint.name.option().foreach(copy.withName)
copy.withId(sourceEndpoint.id).withPath(sourceEndpoint.path.value())

case _ => domain
}
case _ => domain
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,6 @@
"@value": "some/events"
}
],
"core:name": [
{
"@value": "null"
}
],
"core:description": [
{
"@value": "mychannel"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
"doc:DomainElement"
],
"apiContract:path": "some/events",
"core:name": "null",
"core:description": "mychannel",
"apiContract:server": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
asyncapi: 2.4.0
info:
title: components-2.4
version: 1.0.0
components:
servers:
myserver:
url: http://localhost:5000/ws
protocol: ws
channels:
myChannel:
description: mychannel
serverVariables:
environment:
default: dev-api
description: Development API environment
version:
default: v2
description: Development API version
servers:
production:
$ref: "#/components/servers/myserver"
development:
url: https://{environment}.example.com/{version}
description: Development server using referenced server variables.
protocol: https
variables:
environment:
$ref: "#/components/serverVariables/environment"
version:
$ref: "#/components/serverVariables/version"
channels:
some/events:
$ref: "#/components/channels/myChannel"
Loading