Skip to content

Commit

Permalink
Merge pull request #1948 from aml-org/W-12689961
Browse files Browse the repository at this point in the history
W:12689961: Add ServerVariables to Components in  Async 2.4
  • Loading branch information
damianpedra authored Mar 13, 2024
2 parents 0baf321 + 3d29a40 commit 8767d50
Show file tree
Hide file tree
Showing 19 changed files with 780 additions and 539 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ case class AsyncDeclarationsEmitters(declares: Seq[DomainElement], ordering: Spe
ordering
)

if (declarations.serverVariables.nonEmpty)
result += AsyncApiServerVariablesDeclarationEmitter(
"serverVariables",
declarations.serverVariables.values.toSeq,
ordering
)

if (declarations.channels.nonEmpty)
result += AsyncApiChannelsDeclarationEmitter(
"channels",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package amf.apicontract.internal.spec.async.emitters.domain
import amf.apicontract.client.scala.model.domain.Parameter
import amf.apicontract.internal.spec.common.emitter.{OasServerVariableEmitter, OasServerVariablesEmitter}
import amf.apicontract.internal.spec.oas.emitter.context.OasLikeSpecEmitterContext
import amf.core.internal.render.BaseEmitters.pos
import amf.core.internal.render.SpecOrdering
import amf.core.internal.render.emitters.EntryEmitter
import org.mulesoft.common.client.lexical.Position
import org.mulesoft.common.client.lexical.Position.ZERO
import org.yaml.model.YDocument.{EntryBuilder, PartBuilder}

case class AsyncApiServerVariablesDeclarationEmitter(
key: String,
serverVariable: Seq[Parameter],
ordering: SpecOrdering
)(implicit val spec: OasLikeSpecEmitterContext)
extends EntryEmitter {
override def emit(b: EntryBuilder): Unit = {
val namedServersVariableEmitters = serverVariable.map(s => OasServerVariableEmitter(s, ordering))
b.entry(
key,
_.obj(pb => namedServersVariableEmitters.foreach(e => e.emit(pb)))
)
}
override def position(): Position = serverVariable.headOption.map(b => pos(b.annotations)).getOrElse(ZERO)
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class AsyncApiServerPartEmitter(server: Server, ordering: SpecOrdering)(implicit
fs.entry(ServerModel.ProtocolVersion).foreach(f => result += ValueEmitter("protocolVersion", f))
fs.entry(ServerModel.Description).foreach(f => result += ValueEmitter("description", f))
fs.entry(ServerModel.Tags).foreach(f => result += TagsEmitter("tags", f.array.values.asInstanceOf[Seq[Tag]], ordering))
fs.entry(ServerModel.Variables).foreach(f => result += OasServerVariablesEmitter(f, ordering))
fs.entry(ServerModel.Variables).foreach(f => result += OasServerVariablesEmitter("variables", 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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ trait AsyncSpecVersionFactory extends OasLikeSpecVersionFactory {

class Async20VersionFactory()(implicit ctx: AsyncWebApiContext) extends AsyncSpecVersionFactory {
override def serverVariableParser(entry: YMapEntry, parent: String): OasLikeServerVariableParser =
AsyncServerVariableParser(entry, parent)(ctx)
Async20ServerVariableParser(YMapEntryLike(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 =
Expand Down Expand Up @@ -94,6 +94,9 @@ class Async24VersionFactory()(implicit ctx: AsyncWebApiContext) extends Async23
messageType: Option[MessageType],
isTrait: Boolean
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser = Async24MessageParser(entryLike, parent, messageType, isTrait)

override def serverVariableParser(entry: YMapEntry, parent: String): OasLikeServerVariableParser =
new Async24ServerVariableParser(YMapEntryLike(entry), parent)(ctx)
}

object Async24VersionFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ object Async24Syntax extends SpecSyntax {
override val nodes: Map[String, Set[String]] =
add(
Async23Syntax.nodes,
"message" -> Set("messageId")
"message" -> Set("messageId"),
"components" -> Set(
"serverVariables",
)
)
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package amf.apicontract.internal.spec.async.parser.domain

import amf.apicontract.client.scala.model.domain.Parameter
import amf.apicontract.client.scala.model.domain.{Parameter, Server}
import amf.apicontract.internal.metamodel.domain.{ParameterModel, ServerModel}
import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext
import amf.apicontract.internal.spec.common.WebApiDeclarations.{ErrorServer, ErrorServerVariable}
import amf.apicontract.internal.spec.oas.parser.domain.OasLikeServerVariableParser
import amf.apicontract.internal.spec.spec.OasDefinitions
import amf.core.client.scala.model.domain.{AmfArray, AmfScalar}
import amf.core.internal.metamodel.domain.ExternalSourceElementModel
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.client.scala.model.domain.Example
import amf.shapes.internal.domain.metamodel.common.ExamplesField
import amf.shapes.internal.spec.common.parser.YMapEntryLike
import amf.shapes.internal.validation.definitions.ShapeParserSideValidations.ExamplesMustBeASeq
import org.yaml.model.{YMap, YMapEntry, YSequence, YType}
import org.yaml.model.{YMap, YMapEntry, YNode, YSequence, YType}

case class AsyncServerVariableParser(entry: YMapEntry, parent: String)(implicit override val ctx: AsyncWebApiContext)
case class Async20ServerVariableParser(entry: YMapEntryLike, parent: String)(implicit override val ctx: AsyncWebApiContext)
extends OasLikeServerVariableParser(entry, parent)(ctx) {

override protected def parseMap(variable: Parameter, map: YMap): Unit = {
Expand Down Expand Up @@ -45,3 +50,58 @@ case class AsyncServerVariableParser(entry: YMapEntry, parent: String)(implicit
)
}
}
class Async24ServerVariableParser(override val entry: YMapEntryLike, override val parent: String)(implicit override val ctx: AsyncWebApiContext)
extends Async20ServerVariableParser(entry, parent)(ctx) {
override def parse(): Parameter = {
val map: YMap = entry.asMap
ctx.link(map) match {
case Left(fullRef) => handleRef(fullRef)
case Right(_) => super.parse()
}
}

private def handleRef(fullRef: String): Parameter = {
val label = OasDefinitions.stripOas3ComponentsPrefix(fullRef, "serverVariables")
ctx.declarations
.findServerVariable(label, SearchScope.Named)
.map(serverVariables => nameAndAdopt(generateLink(label, serverVariables, entry), entry.key))
.getOrElse(remote(fullRef, entry))
}

private def remote(fullRef: String, entryLike: YMapEntryLike)(implicit ctx: AsyncWebApiContext): Parameter = {
ctx.navigateToRemoteYNode(fullRef) match {
case Some(result) =>
val serverVariableNode = result.remoteNode
val external = Async20ServerVariableParser(YMapEntryLike(serverVariableNode), parent)(result.context).parse()
nameAndAdopt(
external.link(AmfScalar(fullRef), entryLike.annotations, Annotations.synthesized()),
entryLike.key
)
case None =>
ctx.eh.violation(
CoreValidations.UnresolvedReference,
"",
s"Cannot find link reference $fullRef",
entryLike.asMap.location
)
val errorServerVariable = ErrorServerVariable(fullRef, entryLike.asMap)
nameAndAdopt(errorServerVariable.link(fullRef, errorServerVariable.annotations), entryLike.key)
}
}

private def generateLink(label: String, effectiveTarget: Parameter, entryLike: YMapEntryLike): Parameter = {
val serverVariable = Parameter(entryLike.annotations)
val hash = s"${serverVariable.id}$label".hashCode
serverVariable
.withId(s"${serverVariable.id}/link-$hash")
.withLinkTarget(effectiveTarget)
.withLinkLabel(label, Annotations(entryLike.value))
}

def nameAndAdopt(serverVariable: Parameter, key: Option[YNode]): Parameter = {
key foreach { k =>
serverVariable.setWithoutId(ParameterModel.Name, ScalarNode(k).string(), Annotations(k))
}
serverVariable
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
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 = {
parseServerVariableDeclarations(map, parent)
Async23DeclarationParser.parseDeclarations(map, parent, document)
// TODO: add stuff....
}

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()
serverVariable.add(DeclaredElement())
serverVariable.add(DeclaredServerVariable())
ctx.declarations += serverVariable
}
}
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,19 @@ package amf.apicontract.internal.spec.common

import amf.aml.client.scala.model.document.Dialect
import amf.apicontract.client.scala.model.domain._
import amf.apicontract.client.scala.model.domain.bindings.{
ChannelBindings,
MessageBindings,
OperationBindings,
ServerBindings
}
import amf.apicontract.client.scala.model.domain.bindings.{ChannelBindings, MessageBindings, OperationBindings, ServerBindings}
import amf.apicontract.client.scala.model.domain.security.SecurityScheme
import amf.apicontract.client.scala.model.domain.templates.{ResourceType, Trait}
import amf.apicontract.internal.metamodel.domain._
import amf.apicontract.internal.metamodel.domain.bindings.{
ChannelBindingsModel,
MessageBindingsModel,
OperationBindingsModel,
ServerBindingsModel
}
import amf.apicontract.internal.metamodel.domain.bindings.{ChannelBindingsModel, MessageBindingsModel, OperationBindingsModel, ServerBindingsModel}
import amf.apicontract.internal.metamodel.domain.security.SecuritySchemeModel
import amf.apicontract.internal.metamodel.domain.templates.{ResourceTypeModel, TraitModel}
import amf.apicontract.internal.spec.common.WebApiDeclarations._
import amf.core.client.scala.errorhandling.AMFErrorHandler
import amf.core.client.scala.model.document.BaseUnit
import amf.core.client.scala.model.domain.{DataNode, DomainElement, ObjectNode, Shape}
import amf.core.client.scala.parse.document.EmptyFutureDeclarations
import amf.core.internal.annotations.{DeclaredElement, DeclaredHeader, ErrorDeclaration}
import amf.core.internal.annotations.{DeclaredElement, DeclaredHeader, DeclaredServerVariable, ErrorDeclaration}
import amf.core.internal.parser.domain.SearchScope.Named
import amf.core.internal.parser.domain._
import amf.core.internal.utils.QName
Expand Down Expand Up @@ -62,6 +52,7 @@ class WebApiDeclarations(
var messageTraits: Map[String, Message] = Map()
var servers: Map[String, Server] = Map()
var channels: Map[String, EndPoint] = Map()
var serverVariables: Map[String, Parameter] = Map()
var others: Map[String, BaseUnit] = Map()

override def addLibrary(alias: String, declarations: Declarations): Unit = {
Expand Down Expand Up @@ -95,6 +86,7 @@ class WebApiDeclarations(
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) }
serverVariables.foreach {case (k,s) => merged.serverVariables = merged.serverVariables + (k -> s) }
channels.foreach { case (k, s) => merged.channels = merged.channels + (k -> s) }
}

Expand Down Expand Up @@ -128,6 +120,7 @@ class WebApiDeclarations(
next.operationTraits = operationTraits
next.messageTraits = next.messageTraits
next.servers = next.servers
next.serverVariables = next.serverVariables
next.channels = next.channels
next.others = others
next
Expand All @@ -142,6 +135,8 @@ class WebApiDeclarations(
traits = traits + (indexKey -> t)
case h: Parameter if h.annotations.contains(classOf[DeclaredHeader]) =>
headers = headers + (indexKey -> h)
case sv: Parameter if sv.annotations.contains(classOf[DeclaredServerVariable]) =>
serverVariables = serverVariables + (indexKey -> sv)
case p: Parameter =>
parameters = parameters + (indexKey -> p)
case p: Payload =>
Expand Down Expand Up @@ -224,7 +219,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 ++ servers.values ++ channels.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 ++ serverVariables.values ++ channels.values).toList

def findParameterOrError(ast: YPart)(key: String, scope: SearchScope.Scope): Parameter =
findParameter(key, scope) match {
Expand Down Expand Up @@ -323,6 +318,9 @@ class WebApiDeclarations(
def findServer(key: String, scope: SearchScope.Scope): Option[Server] =
findForType(key, _.asInstanceOf[WebApiDeclarations].servers, scope) collect { case s: Server => s }

def findServerVariable(key: String, scope: SearchScope.Scope): Option[Parameter] =
findForType(key, _.asInstanceOf[WebApiDeclarations].serverVariables, scope) collect { case s: Parameter => s }

def findChannel(key: String, scope: SearchScope.Scope): Option[EndPoint] =
findForType(key, _.asInstanceOf[WebApiDeclarations].channels, scope) collect { case c: EndPoint => c }

Expand Down Expand Up @@ -609,6 +607,16 @@ object WebApiDeclarations {
override val model: ServerModel.type = ServerModel
}

case class ErrorServerVariable(idPart: String, ast: YPart)
extends Parameter(Fields(), Annotations(ast))
with ErrorDeclaration[ParameterModel.type] {
override val namespace: String = "http://amferror.com/#errorServerVariable/"
withId(idPart)

override protected def newErrorInstance: ErrorDeclaration[ParameterModel.type] = ErrorServerVariable(idPart, ast)
override val model: ParameterModel.type = ParameterModel
}

case class ErrorChannel(idPart: String, ast: YPart)
extends EndPoint(Fields(), Annotations(ast))
with ErrorDeclaration[EndPointModel.type] {
Expand Down
Loading

0 comments on commit 8767d50

Please sign in to comment.