Skip to content

Commit

Permalink
Merge pull request #2017 from aml-org/publish-5.5.3
Browse files Browse the repository at this point in the history
W-16124325 - Publish 5.5.3
  • Loading branch information
looseale authored Jul 4, 2024
2 parents b288e5d + f3d40ef commit 66e2d74
Show file tree
Hide file tree
Showing 910 changed files with 56,955 additions and 46,033 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package amf.apicontract.client.platform

import amf.aml.client.platform.model.document.Dialect
import amf.aml.client.platform.model.document.DialectInstance
import amf.aml.client.platform.model.document.{Dialect, DialectInstance}
import amf.aml.internal.convert.VocabulariesClientConverter.DialectConverter
import amf.apicontract.client.scala.{
APIConfiguration => InternalAPIConfiguration,
AsyncAPIConfiguration => InternalAsyncAPIConfiguration,
OASConfiguration => InternalOASConfiguration,
RAMLConfiguration => InternalRAMLConfiguration,
WebAPIConfiguration => InternalWebAPIConfiguration
WebAPIConfiguration => InternalWebAPIConfiguration,
AvroConfiguration => InternalAvroConfiguration
}
import amf.apicontract.internal.convert.ApiClientConverters._
import amf.core.client.platform.config.{AMFEventListener, ParsingOptions, RenderOptions}
Expand All @@ -21,8 +21,7 @@ import amf.core.internal.convert.TransformationPipelineConverter._

import scala.scalajs.js.annotation.{JSExportAll, JSExportTopLevel}
import amf.apicontract.client.scala
import amf.core.client.platform.AMFGraphConfiguration
import amf.core.client.platform.adoption.{IdAdopter, IdAdopterProvider}
import amf.core.client.platform.adoption.IdAdopterProvider
import amf.core.client.platform.execution.BaseExecutionEnvironment
import amf.core.client.platform.validation.payload.AMFShapePayloadValidationPlugin
import amf.core.internal.convert.PayloadValidationPluginConverter.PayloadValidationPluginMatcher
Expand Down Expand Up @@ -220,3 +219,10 @@ object APIConfiguration {
def API(): AMFConfiguration = InternalAPIConfiguration.API()
def fromSpec(spec: Spec): AMFConfiguration = InternalAPIConfiguration.fromSpec(spec)
}

// AVRO is in alpha support mode
@JSExportAll
@JSExportTopLevel("AvroConfiguration")
object AvroConfiguration {
def Avro(): AMFConfiguration = InternalAvroConfiguration.Avro()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import amf.apicontract.internal.convert.ApiRegister
import amf.apicontract.internal.entities.{APIEntities, FragmentEntities}
import amf.apicontract.internal.plugins.ApiContractFallbackPlugin
import amf.apicontract.internal.spec.async.{Async20ElementRenderPlugin, Async20ParsePlugin, Async20RenderPlugin}
import amf.apicontract.internal.spec.avro.AvroParsePlugin
import amf.apicontract.internal.spec.oas._
import amf.apicontract.internal.spec.raml._
import amf.apicontract.internal.transformation._
Expand Down Expand Up @@ -156,6 +157,12 @@ object RAMLConfiguration extends APIConfigurationBuilder {
}
}

// AVRO is in alpha support mode
object AvroConfiguration extends APIConfigurationBuilder {
def Avro(): AMFConfiguration =
common().withPlugins(List(AvroParsePlugin)) // TODO: add validation profiles and serialization
}

/** [[APIConfigurationBuilder.common common()]] configuration with all configurations needed for OAS like:
* - Validation rules
* - Parse and emit plugins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import amf.core.client.scala.model.domain.Shape
import amf.core.internal.render.BaseEmitters.pos
import amf.core.internal.render.SpecOrdering
import amf.core.internal.render.emitters.EntryEmitter
import amf.shapes.internal.spec.common.{RAML10SchemaVersion, SchemaVersion}
import amf.shapes.internal.spec.common.{AVROSchema, RAML10SchemaVersion, SchemaVersion}
import amf.shapes.internal.spec.oas.emitter.OasTypePartEmitter
import amf.shapes.internal.spec.raml.emitter.Raml10TypeEmitter
import org.yaml.model.YDocument.EntryBuilder
Expand All @@ -27,8 +27,9 @@ case class AsyncSchemaEmitter(
override def emit(b: EntryBuilder): Unit = {
val schemaVersion = AsyncSchemaFormats.getSchemaVersion(mediaType)(spec.eh)
schemaVersion match {
case RAML10SchemaVersion => emitAsRaml(b)
case _ => emitAsOas(b, schemaVersion)
case RAML10SchemaVersion => emitAsRaml(b)
case AVROSchema(avroType) => emitAsAvro(b, schemaVersion, avroType) // todo: is it necessary?
case _ => emitAsOas(b, schemaVersion)
}
}

Expand All @@ -53,5 +54,17 @@ case class AsyncSchemaEmitter(
)
}

private def emitAsAvro(b: EntryBuilder, schemaVersion: SchemaVersion, avroType: String): Unit = {
b.entry(
key,
b => {
val newCtx = new Async20SpecEmitterContext(spec.eh, config = spec.renderConfig, schemaVersion = schemaVersion)
// todo: call a specific AVRO Schema emitter (tbd in W-15633198)
OasTypePartEmitter(shape, ordering, references = references)(OasLikeShapeEmitterContextAdapter(newCtx))
.emit(b)
}
)
}

override def position(): Position = pos(shape.annotations)
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Async20EndpointParser(entry: YMapEntry, parentId: String, collector: List[
"subscribe|publish",
entries => {
val operations = parseOperations(entries)
endpoint.setWithoutId(EndPointModel.Operations, AmfArray(operations, Annotations(map)), Annotations(map))
endpoint.setWithoutId(EndPointModel.Operations, AmfArray(operations, Annotations.virtual()), Annotations.virtual())
}
)

Expand All @@ -73,7 +73,7 @@ class Async22EndpointParser(
entry => {
val nodes = entry.value.as[YSequence].nodes
val servers = nodes.map { n =>
val server = Server()
val server = Server(Annotations(n))
server.setWithoutId(
ServerModel.Name,
AmfScalar(n.toString, Annotations(n.value)),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package amf.apicontract.internal.spec.async.parser.domain

import amf.apicontract.client.scala.model.domain.Payload
import amf.apicontract.internal.spec.avro.AvroSettings
import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.apicontract.internal.spec.avro.parser.domain.AvroShapeParser
import amf.apicontract.internal.spec.common.WebApiDeclarations
import amf.apicontract.internal.spec.oas.parser.context.OasLikeWebApiContext
import amf.apicontract.internal.spec.spec.toRaml
Expand Down Expand Up @@ -46,8 +49,8 @@ object AsyncSchemaFormats {
value match {
case Some(format) if oas30Schema.contains(format) => OAS30SchemaVersion(SchemaPosition.Schema)
case Some(format) if ramlSchema.contains(format) => RAML10SchemaVersion
// async20 schemas are handled with draft 7. Avro schema is not supported
case _ => JSONSchemaDraft7SchemaVersion
case Some(format) if avroSchema.contains(format) => AVROSchema()
case _ => JSONSchemaDraft7SchemaVersion // async20 schemas are handled with draft 7 by default
}
}

Expand All @@ -57,7 +60,9 @@ case class AsyncApiTypeParser(entry: YMapEntry, adopt: Shape => Unit, version: S

def parse(): Option[Shape] = version match {
case RAML10SchemaVersion => CustomRamlReferenceParser(YMapEntryLike(entry), adopt).parse()
case _ => OasTypeParser(entry, adopt, version).parse()
case AVROSchema(_) =>
new AvroShapeParser(YMapEntryLike(entry).asMap)(new AvroSchemaContext(ctx, AvroSettings)).parse()
case _ => OasTypeParser(entry, adopt, version).parse()
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package amf.apicontract.internal.spec.avro

import amf.apicontract.internal.plugins.ApiParsePlugin
import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.apicontract.internal.spec.avro.parser.document.AvroDocumentParser
import amf.core.client.scala.model.document.BaseUnit
import amf.core.client.scala.parse.document.ParserContext
import amf.core.internal.parser.Root
import amf.core.internal.remote.{Mimes, Spec}

object AvroParsePlugin extends ApiParsePlugin {

override def spec: Spec = Spec.AVRO_SCHEMA

override def parse(document: Root, ctx: ParserContext): BaseUnit = {
new AvroDocumentParser(document)(new AvroSchemaContext(ctx, AvroSettings)).parseDocument()
}

/** media types which specifies vendors that are parsed by this plugin. */
override def mediaTypes: Seq[String] = Seq(Mimes.`application/json`)

override def applies(element: Root): Boolean = element.mediatype == Mimes.`application/json`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package amf.apicontract.internal.spec.avro

import amf.core.client.scala.parse.document.ParserContext
import amf.core.internal.plugins.syntax.SyamlAMFErrorHandler
import amf.core.internal.remote.Spec
import amf.core.internal.remote.Spec.AVRO_SCHEMA
import amf.shapes.internal.spec.common.parser._
import amf.shapes.internal.spec.common.{OAS20SchemaVersion, SchemaPosition, SchemaVersion}
import amf.shapes.internal.spec.raml.parser.RamlWebApiContextType.RamlWebApiContextType
import org.yaml.model.YNode

object AvroSettings extends SpecSettings {
override val spec: Spec = AVRO_SCHEMA

override def link(node: YNode)(implicit eh: SyamlAMFErrorHandler): Either[String, YNode] = Left(node.toString)

override def ignoreCriteria: IgnoreCriteria = IgnoreAllCriteria

override def ramlContextType: Option[RamlWebApiContextType] = None

override val defaultSchemaVersion: SchemaVersion = OAS20SchemaVersion.apply(SchemaPosition.Other)
override val annotationValidatorBuilder: AnnotationSchemaValidatorBuilder = IgnoreAnnotationSchemaValidatorBuilder

override def shouldLinkTypes(parent: ParserContext): Boolean = parent match {
case ctx: ShapeParserContext if ctx.isRamlContext => false
case _ => true
}

override val syntax: SpecSyntax = SpecSyntax.empty
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package amf.apicontract.internal.spec.avro.parser.context

import amf.core.client.scala.parse.document.ParserContext
import amf.shapes.internal.spec.common.parser.{ShapeParserContext, SpecSettings}

import scala.collection.mutable

class AvroSchemaContext(ctx: ParserContext, settings: SpecSettings)
extends ShapeParserContext(
ctx.rootContextDocument,
ctx.refs,
ctx.parsingOptions,
ctx,
None,
mutable.Map.empty,
settings
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package amf.apicontract.internal.spec.avro.parser.document

import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.apicontract.internal.spec.avro.parser.domain.AvroShapeParser
import amf.core.client.scala.model.document.BaseUnitProcessingData
import amf.core.client.scala.parse.document.SyamlParsedDocument
import amf.core.internal.metamodel.document.DocumentModel
import amf.core.internal.parser.domain.Annotations
import amf.core.internal.parser.{Root, YMapOps}
import amf.core.internal.remote.Spec
import amf.shapes.client.scala.model.document.AvroSchemaDocument
import amf.shapes.client.scala.model.domain.AnyShape
import amf.shapes.internal.spec.common.parser.QuickFieldParserOps
import org.yaml.model.YMap

class AvroDocumentParser(root: Root)(implicit ctx: AvroSchemaContext) extends QuickFieldParserOps {

def parseDocument(): AvroSchemaDocument = {
val map = root.parsed.asInstanceOf[SyamlParsedDocument].document.as[YMap]
val doc = AvroSchemaDocument(Annotations(map))
.withLocation(ctx.loc)
.withProcessingData(BaseUnitProcessingData().withSourceSpec(Spec.AVRO_SCHEMA))

map.key("namespace", (DocumentModel.Package in doc).allowingAnnotations)

val parsedShape = parseType(map)
parsedShape.foreach(shape => doc.withEncodes(shape))
doc
}

def parseType(map: YMap): Option[AnyShape] = new AvroShapeParser(map).parse()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package amf.apicontract.internal.spec.avro.parser.domain
import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.shapes.client.scala.model.domain.{AnyShape, ArrayShape}
import org.yaml.model.{YMap, YMapEntry}

case class AvroArrayShapeParser(map: YMap)(implicit ctx: AvroSchemaContext)
extends AvroCollectionShapeParser[ArrayShape](map, "items") {
override val shape: ArrayShape = ArrayShape(map)
override def setMembers(anyShape: AnyShape): Unit = shape.withItems(anyShape)
override def parseMembers(e: YMapEntry): AnyShape = AvroTextParser(e.value).parse()

override def parseSpecificFields(): Unit = {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package amf.apicontract.internal.spec.avro.parser.domain

import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.core.internal.parser.YMapOps
import amf.shapes.client.scala.model.domain.AnyShape
import org.yaml.model.{YMap, YMapEntry}

abstract class AvroCollectionShapeParser[T <: AnyShape](map: YMap, membersKey: String)(implicit ctx: AvroSchemaContext)
extends AvroComplexShapeParser(map) {
val shape: T

protected def setMembers(anyShape: AnyShape): Unit

override def parse(): AnyShape = {
map
.key(membersKey)
.map(parseMembers)
.foreach(setMembers)
parseDefault()
shape
}

protected def parseMembers(e: YMapEntry): AnyShape = AvroTextTypeParser(e.value.as[String], None).parse()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package amf.apicontract.internal.spec.avro.parser.domain

import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.core.internal.datanode.DataNodeParser
import amf.core.internal.metamodel.domain.ShapeModel
import amf.core.internal.parser.YMapOps
import amf.core.internal.parser.domain.Annotations
import amf.shapes.client.scala.model.domain.AnyShape
import amf.shapes.internal.domain.metamodel.AnyShapeModel
import amf.shapes.internal.spec.common.parser.QuickFieldParserOps
import org.yaml.model._

abstract class AvroComplexShapeParser(map: YMap)(implicit ctx: AvroSchemaContext)
extends QuickFieldParserOps
with AvroKeyExtractor {
val shape: AnyShape

def parse(): AnyShape = {
addTypeToCache()
parseCommonFields()
parseSpecificFields()
parseDefault()
shape
}

def parseCommonFields(): Unit = {
map.key("name", AnyShapeModel.Name in shape)
map.key("namespace", (AnyShapeModel.AvroNamespace in shape).allowingAnnotations)
map.key("aliases", (AnyShapeModel.Aliases in shape).allowingAnnotations)
map.key("doc", (AnyShapeModel.Description in shape).allowingAnnotations)
}

// each specific parser should override and parse it's specific fields
def parseSpecificFields(): Unit

def parseDefault(): Unit = {
map.key(
"default",
entry => {
val dataNode = DataNodeParser(entry.value).parse()
shape.set(ShapeModel.Default, dataNode, Annotations(entry))
}
)
}

private def addTypeToCache(): Unit = {
def getText(node: YNode) = node.as[YScalar].text
def getAliases(entry: YMapEntry): IndexedSeq[String] = entry.value.as[YSequence].nodes.map(getText)
val name = map.key("name").map(name => getText(name.value))
val aliases = map.key("aliases").map(getAliases)
name.foreach(ctx.globalSpace.put(_, shape))
aliases.foreach(_.foreach(alias => ctx.globalSpace.put(alias, shape)))
}
}

trait AvroKeyExtractor {
implicit class YMapKeys(map: YMap) {
def typeValue: Option[YNode] = map.key("type").map(_.value)
def `type`: Option[String] = typeValue.flatMap(_.asScalar).map(_.text)
}

implicit class StringAvroOps(value: String) {
def isPrimitive: Boolean =
Seq("null", "boolean", "int", "long", "float", "double", "bytes", "string").contains(value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package amf.apicontract.internal.spec.avro.parser.domain

import amf.apicontract.internal.spec.avro.parser.context.AvroSchemaContext
import amf.core.client.scala.model.DataType
import amf.core.client.scala.model.domain.ScalarNode
import amf.core.internal.parser.{YMapOps, YScalarYRead}
import amf.shapes.client.scala.model.domain.AnyShape
import org.yaml.model._

class AvroEnumParser(map: YMap)(implicit ctx: AvroSchemaContext) extends AvroTextTypeParser("string", Some(map)) {

override def parse(): AnyShape = {
val shape = super.parse()
parseCommonFields()
parseSpecificFields()
parseDefault()
shape
}

override def parseSpecificFields(): Unit = {
map
.key("symbols")
.map(parseSymbols)
.map(shape.withValues)
}

private def parseSymbols(e: YMapEntry): Seq[ScalarNode] = {
val symbols = e.value.as[YSequence]
symbols.nodes.map(buildDataNode)
}

private def buildDataNode(symbol: YNode) = ScalarNode(symbol.as[YScalar].text, Some(DataType.String), symbol)
}
Loading

0 comments on commit 66e2d74

Please sign in to comment.