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-12689962: add security schemes in operation bindings #1953

Merged
merged 5 commits into from
Mar 26, 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
@@ -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