From d0ceeb861dd3a36b63cc54af24ac0a98a55c4e70 Mon Sep 17 00:00:00 2001 From: Jack Lynch Date: Thu, 21 Aug 2025 14:46:21 +0100 Subject: [PATCH] FDN-3924 Add InterfaceTypesRewriter --- .../builders/ApiBuilderServiceBuilders.scala | 23 +++++++++++++++++-- .../rewriter/InterfaceTypesRewriter.scala | 18 +++++++++++++++ .../rewriter/MinimalTypesRewriter.scala | 3 ++- .../rewriter/UnionsToModelsRewriter.scala | 1 + .../apibuilder/validation/JsonValidator.scala | 4 +++- 5 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 src/main/scala/io/apibuilder/rewriter/InterfaceTypesRewriter.scala diff --git a/src/main/scala/io/apibuilder/builders/ApiBuilderServiceBuilders.scala b/src/main/scala/io/apibuilder/builders/ApiBuilderServiceBuilders.scala index 55d228c..db28392 100644 --- a/src/main/scala/io/apibuilder/builders/ApiBuilderServiceBuilders.scala +++ b/src/main/scala/io/apibuilder/builders/ApiBuilderServiceBuilders.scala @@ -1,8 +1,7 @@ package io.apibuilder.builders import java.util.UUID - -import io.apibuilder.spec.v0.models.{Annotation, Apidoc, Application, Attribute, Body, Contact, Deprecation, Enum, EnumValue, Field, Header, Import, Info, License, Method, Model, Operation, Organization, Parameter, ParameterLocation, Resource, Response, ResponseCode, ResponseCodeInt, Service, Union, UnionType} +import io.apibuilder.spec.v0.models.{Annotation, Apidoc, Application, Attribute, Body, Contact, Deprecation, Enum, EnumValue, Field, Header, Import, Info, Interface, License, Method, Model, Operation, Organization, Parameter, ParameterLocation, Resource, Response, ResponseCode, ResponseCodeInt, Service, Union, UnionType} import io.apibuilder.validation.{ApiBuilderType, MultiService} import play.api.libs.json.{JsObject, Json} @@ -26,6 +25,7 @@ trait ApiBuilderServiceBuilders { headers: Seq[Header] = Nil, imports: Seq[Import] = Nil, enums: Seq[Enum] = Nil, + interfaces: Seq[Interface] = Nil, unions: Seq[Union] = Nil, models: Seq[Model] = Nil, resources: Seq[Resource] = Nil, @@ -45,6 +45,7 @@ trait ApiBuilderServiceBuilders { headers = headers, imports = imports, enums = enums, + interfaces = interfaces, unions = unions, models = models, resources = resources, @@ -147,6 +148,24 @@ trait ApiBuilderServiceBuilders { Info(license = license, contact = contact) } + def makeInterface( + name: String, + plural: String, + fields: Seq[Field], + description: Option[String] = None, + deprecation: Option[Deprecation] = None, + attributes: Seq[Attribute] = Nil + ): Interface = { + Interface( + name = name, + plural = plural, + description = description, + deprecation = deprecation, + fields = fields, + attributes = attributes + ) + } + def makeUnion( name: String = random(), plural: String = random(), diff --git a/src/main/scala/io/apibuilder/rewriter/InterfaceTypesRewriter.scala b/src/main/scala/io/apibuilder/rewriter/InterfaceTypesRewriter.scala new file mode 100644 index 0000000..9247dfc --- /dev/null +++ b/src/main/scala/io/apibuilder/rewriter/InterfaceTypesRewriter.scala @@ -0,0 +1,18 @@ +package io.apibuilder.rewriter + +import io.apibuilder.validation.{ApiBuilderType, MultiService, ScalarType} + +// Inspired by UnionTypesMustBeModelsRewriter +object InterfaceTypesRewriter extends MultiServiceRewriter { + override def rewrite(multiService: MultiService): MultiService = { + val interfaceNames = multiService.allInterfaces.map(_.qualified).toSet + + val rewritten = TypeRewriter { + case t: ApiBuilderType if interfaceNames.contains(t.qualified) => ScalarType.ObjectType + case t: ApiBuilderType => t + case t: ScalarType => t + }.rewrite(multiService) + + rewritten + } +} \ No newline at end of file diff --git a/src/main/scala/io/apibuilder/rewriter/MinimalTypesRewriter.scala b/src/main/scala/io/apibuilder/rewriter/MinimalTypesRewriter.scala index 53668c9..5687ee5 100644 --- a/src/main/scala/io/apibuilder/rewriter/MinimalTypesRewriter.scala +++ b/src/main/scala/io/apibuilder/rewriter/MinimalTypesRewriter.scala @@ -1,7 +1,7 @@ package io.apibuilder.rewriter import apibuilder.{ApiBuilderHelper, ApiBuilderHelperImpl} -import io.apibuilder.validation.{ApiBuilderType, MultiService} +import io.apibuilder.validation.{ApiBuilderService, ApiBuilderType, MultiService, ScalarType} import scala.annotation.tailrec @@ -24,6 +24,7 @@ case class MinimalTypesRewriter(types: Iterable[ApiBuilderType]) extends Default service = s.service.copy( enums = svcTypes.collect { case t: ApiBuilderType.Enum => t }.map(_.`enum`), models = svcTypes.collect { case t: ApiBuilderType.Model => t }.map(_.model), + interfaces = svcTypes.collect { case t: ApiBuilderType.Interface => t }.map(_.interface), unions = svcTypes.collect { case t: ApiBuilderType.Union => t }.map(_.union), ) ) diff --git a/src/main/scala/io/apibuilder/rewriter/UnionsToModelsRewriter.scala b/src/main/scala/io/apibuilder/rewriter/UnionsToModelsRewriter.scala index 7c00b7f..9b02e99 100644 --- a/src/main/scala/io/apibuilder/rewriter/UnionsToModelsRewriter.scala +++ b/src/main/scala/io/apibuilder/rewriter/UnionsToModelsRewriter.scala @@ -26,6 +26,7 @@ object UnionsToModelsRewriter extends DefaultRewriter { unions = newTypes.collect { case e: ApiBuilderType.Union => e.union }, enums = service.service.enums ++ newTypes.collect { case e: ApiBuilderType.Enum => e.`enum` }, models = service.service.models ++ newTypes.collect { case m: ApiBuilderType.Model => m.model }, + interfaces = service.service.interfaces ++ newTypes.collect { case m: ApiBuilderType.Interface => m.interface }, ) ) } diff --git a/src/main/scala/io/apibuilder/validation/JsonValidator.scala b/src/main/scala/io/apibuilder/validation/JsonValidator.scala index 0c26d3f..1817bd5 100644 --- a/src/main/scala/io/apibuilder/validation/JsonValidator.scala +++ b/src/main/scala/io/apibuilder/validation/JsonValidator.scala @@ -129,7 +129,9 @@ case class ValidatedJsonValidator(services: List[ApiBuilderService]) { private def findType(service: ApiBuilderService, typeName: String): Option[AnyType] = { service.enums.find(_.name.equalsIgnoreCase(typeName)) orElse { service.models.find(_.name.equalsIgnoreCase(typeName)) orElse { - service.unions.find(_.name.equalsIgnoreCase(typeName)) + service.unions.find(_.name.equalsIgnoreCase(typeName)) orElse { + service.interfaces.find(_.name.equalsIgnoreCase(typeName)) + } } } }