Skip to content

Commit

Permalink
Merge pull request #1953 from aml-org/W-12689962
Browse files Browse the repository at this point in the history
W-12689962: add security schemes in operation bindings
  • Loading branch information
arielmirra committed Mar 26, 2024
2 parents 4774501 + fdbced1 commit 02074b7
Show file tree
Hide file tree
Showing 30 changed files with 513 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package amf.apicontract.internal.spec.async.emitters.bindings

import amf.core.internal.annotations.SynthesizedField
import amf.core.internal.parser.domain.Fields
import amf.core.internal.render.BaseEmitters.ValueEmitter
import amf.core.internal.render.emitters.EntryEmitter
import amf.apicontract.internal.metamodel.domain.bindings.BindingVersion

import scala.collection.mutable.ListBuffer

abstract class AsyncApiCommonBindingEmitter() extends EntryEmitter {
abstract class AsyncApiCommonBindingEmitter extends EntryEmitter {

def emitBindingVersion(fs: Fields, result: ListBuffer[EntryEmitter]): Unit = {
fs.entry(BindingVersion.BindingVersion).foreach { f =>
if (!f.value.annotations.contains(classOf[SynthesizedField])) result += ValueEmitter("bindingVersion", f)
if (!f.value.annotations.isSynthesized) result += ValueEmitter("bindingVersion", f)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package amf.apicontract.internal.spec.async.emitters.domain
import amf.apicontract.client.scala.model.domain.{Operation, Tag}
import amf.apicontract.internal.metamodel.domain.OperationModel
import amf.apicontract.internal.spec.async.AsyncHelper
import amf.apicontract.internal.spec.common.emitter.SecurityRequirementsEmitter
import amf.apicontract.internal.spec.oas.emitter.context.OasLikeSpecEmitterContext
import amf.apicontract.internal.spec.oas.emitter.domain.{
OasLikeOperationEmitter,
Expand Down Expand Up @@ -52,6 +53,11 @@ case class AsyncOperationPartEmitter(operation: Operation, isTrait: Boolean, ord
emitMessage(fs).map(reqOrRes => tempResult += reqOrRes)
fs.entry(OperationModel.Extends).foreach(f => emitTraits(f, tempResult))
}
fs.entry(OperationModel.Security)
.foreach(f =>
if (!f.value.annotations.isSynthesized)
tempResult += SecurityRequirementsEmitter("security", f, ordering)
)
traverse(ordering.sorted(super.commonEmitters ++ tempResult), eb)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Async20VersionFactory()(implicit ctx: AsyncWebApiContext) extends AsyncSpe
override def serverVariableParser(entry: YMapEntry, parent: String): OasLikeServerVariableParser =
Async20ServerVariableParser(YMapEntryLike(entry), parent)(ctx)
override def operationParser(entry: YMapEntry, adopt: Operation => Operation): OasLikeOperationParser =
AsyncOperationParser(entry, adopt)(ctx)
Async20OperationParser(entry, adopt)(ctx)
override def endPointParser(entry: YMapEntry, parentId: String, collector: List[EndPoint]): OasLikeEndpointParser =
new Async20EndpointParser(entry, parentId, collector)(ctx)
override def securitySchemeParser: (YMapEntryLike, SecurityScheme => SecurityScheme) => SecuritySchemeParser =
Expand All @@ -48,7 +48,8 @@ class Async20VersionFactory()(implicit ctx: AsyncWebApiContext) extends AsyncSpe
parent: String,
messageType: Option[MessageType],
isTrait: Boolean = false
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser = Async20MessageParser(entryLike, parent, messageType, isTrait)
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser =
Async20MessageParser(entryLike, parent, messageType, isTrait)
}

object Async20VersionFactory {
Expand All @@ -61,7 +62,8 @@ class Async21VersionFactory()(implicit ctx: AsyncWebApiContext) extends Async20V
parent: String,
messageType: Option[MessageType],
isTrait: Boolean
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser = Async21MessageParser(entryLike, parent, messageType, isTrait)
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser =
Async21MessageParser(entryLike, parent, messageType, isTrait)
}

object Async21VersionFactory {
Expand All @@ -87,23 +89,27 @@ object Async23VersionFactory {
def apply()(implicit ctx: AsyncWebApiContext): Async23VersionFactory = new Async23VersionFactory()(ctx)
}

class Async24VersionFactory()(implicit ctx: AsyncWebApiContext) extends Async23VersionFactory{
class Async24VersionFactory()(implicit ctx: AsyncWebApiContext) extends Async23VersionFactory {
override def messageParser(
entryLike: YMapEntryLike,
parent: String,
messageType: Option[MessageType],
isTrait: Boolean
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser = Async24MessageParser(entryLike, parent, messageType, isTrait)
)(implicit ctx: AsyncWebApiContext): AsyncMessageParser =
Async24MessageParser(entryLike, parent, messageType, isTrait)

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

override def operationParser(entry: YMapEntry, adopt: Operation => Operation): OasLikeOperationParser =
Async24OperationParser(entry, adopt)(ctx)
}

object Async24VersionFactory {
def apply()(implicit ctx: AsyncWebApiContext): Async24VersionFactory = new Async24VersionFactory()(ctx)
}

class Async25VersionFactory(implicit ctx: AsyncWebApiContext) extends Async24VersionFactory {
class Async25VersionFactory(implicit ctx: AsyncWebApiContext) extends Async24VersionFactory {
override def serversParser(map: YMap, api: AsyncApi): AsyncServersParser = new Async25ServersParser(map, api)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ object Async24Syntax extends SpecSyntax {
Async23Syntax.nodes,
"message" -> Set("messageId"),
"components" -> Set(
"serverVariables",
"serverVariables"
),
"operation" -> Set(
"security"
)
)
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package amf.apicontract.internal.spec.async.parser.domain

import amf.apicontract.client.scala.model.domain.{EndPoint, Operation, Server}
import amf.apicontract.client.scala.model.domain.{EndPoint, Server}
import amf.apicontract.internal.metamodel.domain.{EndPointModel, ServerModel}
import amf.apicontract.internal.spec.async.parser.bindings.AsyncChannelBindingsParser
import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext
Expand All @@ -14,8 +14,6 @@ import amf.core.internal.validation.CoreValidations
import amf.shapes.internal.spec.common.parser.{AnnotationParser, YMapEntryLike}
import org.yaml.model.{YMap, YMapEntry, YNode, YSequence}

import scala.collection.mutable

class Async20EndpointParser(entry: YMapEntry, parentId: String, collector: List[EndPoint])(
override implicit val ctx: AsyncWebApiContext
) extends OasLikeEndpointParser(entry, parentId, collector) {
Expand Down Expand Up @@ -50,11 +48,7 @@ class Async20EndpointParser(entry: YMapEntry, parentId: String, collector: List[
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()
}
val operations = parseOperations(entries)
endpoint.setWithoutId(EndPointModel.Operations, AmfArray(operations, Annotations(map)), Annotations(map))
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@ import amf.core.internal.validation.CoreValidations
import amf.shapes.internal.spec.common.parser.{AnnotationParser, YMapEntryLike}
import org.yaml.model._

object AsyncOperationParser {
object Async20OperationParser {
def apply(entry: YMapEntry, adopt: Operation => Operation, isTrait: Boolean = false)(implicit
ctx: AsyncWebApiContext
): AsyncOperationParser =
if (isTrait) new AsyncOperationTraitParser(entry, adopt)
else new AsyncConcreteOperationParser(entry, adopt)
if (isTrait) new Async20OperationTraitParser(entry, adopt)
else new Async20ConcreteOperationParser(entry, adopt)
}

object Async24OperationParser {
def apply(entry: YMapEntry, adopt: Operation => Operation, isTrait: Boolean = false)(implicit
ctx: AsyncWebApiContext
): AsyncOperationParser =
if (isTrait) Async24OperationTraitParser(entry, adopt)
else Async24ConcreteOperationParser(entry, adopt)
}

abstract class AsyncOperationParser(entry: YMapEntry, adopt: Operation => Operation)(
Expand Down Expand Up @@ -52,19 +60,23 @@ abstract class AsyncOperationParser(entry: YMapEntry, adopt: Operation => Operat

parseTraits(map, operation)

map.key("security").foreach(entry => parseSecuritySchemas(entry, operation))

operation
}

override def parseOperationId(map: YMap, operation: Operation): Unit = {
map.key("operationId", OperationModel.OperationId in operation)
}

protected def parseMessages(map: YMap, operation: Operation)
protected def parseMessages(map: YMap, operation: Operation): Unit = {}

protected def parseTraits(map: YMap, operation: Operation): Unit = {}

protected def parseTraits(map: YMap, operation: Operation)
protected def parseSecuritySchemas(entry: YMapEntry, operation: Operation): Unit = {}
}

private class AsyncConcreteOperationParser(entry: YMapEntry, adopt: Operation => Operation)(implicit
class Async20ConcreteOperationParser(entry: YMapEntry, adopt: Operation => Operation)(implicit
ctx: AsyncWebApiContext
) extends AsyncOperationParser(entry, adopt) {

Expand Down Expand Up @@ -92,9 +104,11 @@ private class AsyncConcreteOperationParser(entry: YMapEntry, adopt: Operation =>
)
}
)

override protected def parseSecuritySchemas(entry: YMapEntry, operation: Operation): Unit = {}
}

private class AsyncOperationTraitParser(entry: YMapEntry, adopt: Operation => Operation)(
class Async20OperationTraitParser(entry: YMapEntry, adopt: Operation => Operation)(
override implicit val ctx: AsyncWebApiContext
) extends AsyncOperationParser(entry, adopt) {

Expand All @@ -113,9 +127,11 @@ private class AsyncOperationTraitParser(entry: YMapEntry, adopt: Operation => Op
}
}

override protected def parseMessages(map: YMap, operation: Operation): Unit = Unit
override protected def parseMessages(map: YMap, operation: Operation): Unit = {}

override protected def parseTraits(map: YMap, operation: Operation): Unit = {}

override protected def parseTraits(map: YMap, operation: Operation): Unit = Unit
override protected def parseSecuritySchemas(entry: YMapEntry, operation: Operation): Unit = {}
}

case class AsyncOperationTraitRefParser(node: YNode, adopt: Operation => Operation, name: Option[String] = None)(
Expand Down Expand Up @@ -162,9 +178,43 @@ case class AsyncOperationTraitRefParser(node: YNode, adopt: Operation => Operati
ctx.navigateToRemoteYNode(url) match {
case Some(result) =>
val operationNode = result.remoteNode
AsyncOperationParser(YMapEntry(name.getOrElse(url), operationNode), adopt, isTrait = true)(result.context)
Async20OperationParser(YMapEntry(name.getOrElse(url), operationNode), adopt, isTrait = true)(result.context)
.parse()
case None => linkError(url, node)
}
}
}

case class Async24ConcreteOperationParser(entry: YMapEntry, adopt: Operation => Operation)(
override implicit val ctx: AsyncWebApiContext
) extends Async20ConcreteOperationParser(entry, adopt)
with SecuritySchemeParser {

override protected def parseMessages(map: YMap, operation: Operation): Unit =
super.parseMessages(map: YMap, operation: Operation)

override protected def parseTraits(map: YMap, operation: Operation): Unit =
super.parseTraits(map: YMap, operation: Operation)

override protected def parseSecuritySchemas(entry: YMapEntry, operation: Operation): Unit = {
super.parseSecuritySchemas(entry, operation)
parseSecurityScheme(entry, OperationModel.Security, operation)
}
}

case class Async24OperationTraitParser(entry: YMapEntry, adopt: Operation => Operation)(
override implicit val ctx: AsyncWebApiContext
) extends Async20OperationTraitParser(entry, adopt)
with SecuritySchemeParser {

override protected def parseMessages(map: YMap, operation: Operation): Unit =
super.parseMessages(map: YMap, operation: Operation)

override protected def parseTraits(map: YMap, operation: Operation): Unit =
super.parseTraits(map: YMap, operation: Operation)

override protected def parseSecuritySchemas(entry: YMapEntry, operation: Operation): Unit = {
super.parseSecuritySchemas(entry, operation)
parseSecurityScheme(entry, OperationModel.Security, operation)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ package amf.apicontract.internal.spec.async.parser.domain

import amf.apicontract.client.scala.model.domain.{Server, Tag}
import amf.apicontract.client.scala.model.domain.api.AsyncApi
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, TagsParser}
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.utils.IdCounter
import amf.core.internal.validation.CoreValidations
import amf.shapes.internal.spec.common.parser.{AnnotationParser, YMapEntryLike}
import org.yaml.model.{YMap, YNode}
Expand Down Expand Up @@ -49,14 +46,16 @@ class Async23ServersParser(map: YMap, api: AsyncApi)(
new Async23ServerParser(api.id, entryLike)
}

class Async25ServersParser(map: YMap, api: AsyncApi)(override implicit val ctx: AsyncWebApiContext) extends AsyncServersParser(map, api){
class Async25ServersParser(map: YMap, api: AsyncApi)(override implicit val ctx: AsyncWebApiContext)
extends AsyncServersParser(map, api) {
override protected def serverParser(entryLike: YMapEntryLike): OasLikeServerParser =
new Async25SeverParser(api.id, entryLike)
}

class Async20ServerParser(parent: String, entryLike: YMapEntryLike)(implicit
override val ctx: AsyncWebApiContext
) extends OasLikeServerParser(parent, entryLike) {
) extends OasLikeServerParser(parent, entryLike)
with SecuritySchemeParser {

override def parse(): Server = {
val server = super.parse()
Expand All @@ -71,14 +70,7 @@ class Async20ServerParser(parent: String, entryLike: YMapEntryLike)(implicit

map.key(
"security",
entry => {
val idCounter = new IdCounter()
val securedBy = entry.value
.as[Seq[YNode]]
.flatMap(s => OasLikeSecurityRequirementParser(s, (_: SecurityRequirement) => Unit, idCounter).parse())

server.setWithoutId(ServerModel.Security, AmfArray(securedBy, Annotations(entry.value)), Annotations(entry))
}
entry => parseSecurityScheme(entry, ServerModel.Security, server)
)

server
Expand Down Expand Up @@ -143,10 +135,10 @@ class Async23ServerParser(parent: String, entryLike: YMapEntryLike)(implicit ove
}

class Async25SeverParser(parent: String, entryLike: YMapEntryLike)(implicit override val ctx: AsyncWebApiContext)
extends Async23ServerParser(parent, entryLike){
extends Async23ServerParser(parent, entryLike) {
override def parse(): Server = {
val server = super.parse()
map.key("tags").foreach{ entry =>
map.key("tags").foreach { entry =>
val tags = entry.value.as[Seq[YMap]].map(tag => TagsParser(tag, (tag: Tag) => tag).parse())
server.setWithoutId(ServerModel.Tags, AmfArray(tags, Annotations(entry.value)), Annotations(entry))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package amf.apicontract.internal.spec.async.parser.domain

import amf.apicontract.client.scala.model.domain.security.SecurityRequirement
import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext
import amf.apicontract.internal.spec.common.parser.OasLikeSecurityRequirementParser
import amf.core.client.scala.model.domain.{AmfArray, AmfObject}
import amf.core.internal.metamodel.Field
import amf.core.internal.parser.domain.Annotations
import amf.core.internal.utils.IdCounter
import org.yaml.model.{YMapEntry, YNode}

trait SecuritySchemeParser {
def parseSecurityScheme(entry: YMapEntry, field: Field, parent: AmfObject)(implicit ctx: AsyncWebApiContext): Unit = {
val idCounter = new IdCounter()
val securedBy = entry.value
.as[Seq[YNode]]
.flatMap(s => OasLikeSecurityRequirementParser(s, (_: SecurityRequirement) => Unit, idCounter).parse())
parent.setWithoutId(field, AmfArray(securedBy, Annotations(entry.value)), Annotations(entry))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import amf.apicontract.internal.spec.async.parser.bindings.{
import amf.apicontract.internal.spec.async.parser.context.AsyncWebApiContext
import amf.apicontract.internal.spec.async.parser.domain.{
AsyncCorrelationIdParser,
AsyncOperationParser,
Async20OperationParser,
AsyncParametersParser
}
import amf.apicontract.internal.spec.oas.parser.document.OasLikeDeclarationsHelper
Expand Down Expand Up @@ -81,7 +81,7 @@ case class Async20DeclarationParser() extends AsyncDeclarationParser with OasLik
addDeclarationKey(DeclarationKey(entry, isAbstract = true))
entry.value.as[YMap].entries.foreach { entry =>
val adopt = (o: Operation) => o
val operation = AsyncOperationParser(entry, adopt, isTrait = true).parse()
val operation = Async20OperationParser(entry, adopt, isTrait = true).parse()
operation.add(DeclaredElement())
ctx.declarations += operation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ case class OasWithExtensionsSecurityRequirementsEmitter(key: String, f: FieldEnt
)
}

def allSchemesAreValidInOas(schemes: Seq[ParametrizedSecurityScheme], spec: Spec): Boolean = {
private def allSchemesAreValidInOas(schemes: Seq[ParametrizedSecurityScheme], spec: Spec): Boolean = {
schemes.forall(s => {
s.scheme match {
case linkable: Linkable if linkable.isLink => return true
Expand Down
Loading

0 comments on commit 02074b7

Please sign in to comment.