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

[gen] dictionaries with referenced keys extension #3043

Merged
merged 1 commit into from
Aug 30, 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
Expand Up @@ -7,7 +7,7 @@ import zio.Chunk
import zio.http.Method
import zio.http.endpoint.openapi.OpenAPI.ReferenceOr
import zio.http.endpoint.openapi.{JsonSchema, OpenAPI}
import zio.http.gen.scala.Code.{CodecType, Collection, PathSegmentCode, ScalaType}
import zio.http.gen.scala.Code.{CodecType, Collection, PathSegmentCode, ScalaType, TypeRef}
import zio.http.gen.scala.{Code, CodeGen}

object EndpointGen {
Expand Down Expand Up @@ -268,7 +268,7 @@ final case class EndpointGen(config: Config) {
case tref: Code.TypeRef => f(tref)
case Collection.Seq(inner, nonEmpty) => Collection.Seq(mapTypeRef(inner)(f), nonEmpty)
case Collection.Set(inner, nonEmpty) => Collection.Set(mapTypeRef(inner)(f), nonEmpty)
case Collection.Map(inner) => Collection.Map(mapTypeRef(inner)(f))
case Collection.Map(inner, keysType) => Collection.Map(mapTypeRef(inner)(f), keysType)
case Collection.Opt(inner) => Collection.Opt(mapTypeRef(inner)(f))
case _ => sType
}
Expand Down Expand Up @@ -1302,11 +1302,36 @@ final case class EndpointGen(config: Config) {
throw new Exception("Object with properties and additionalProperties is not supported")
case JsonSchema.Object(properties, additionalProperties, _)
if properties.isEmpty && additionalProperties.isRight =>
val (vSchema, kSchemaOpt) = {
val vs = additionalProperties.toOption.get
val (ks, annotations) = JsonSchema.Object.extractKeySchemaFromAnnotations(vs)
vs.withoutAnnotations.annotate(annotations) -> ks
}

Some(
Code.Field(
name,
Code.Collection.Map(
schemaToField(additionalProperties.toOption.get, openAPI, name, annotations).get.fieldType,
schemaToField(vSchema, openAPI, name, annotations).get.fieldType,
kSchemaOpt.collect {
case ss: JsonSchema.String =>
schemaToField(ss, openAPI, name, annotations).get.fieldType
case JsonSchema.RefSchema(ref) =>
val baref = ref.replaceFirst("^#/components/schemas/", "")
resolveSchemaRef(openAPI, baref) match {
case ks: JsonSchema.String =>
if (config.generateSafeTypeAliases) TypeRef(baref + ".Type")
else schemaToField(ks, openAPI, name, annotations).get.fieldType
case nonStringSchema =>
throw new IllegalArgumentException(
s"x-string-key-schema must reference a string schema, but got: ${nonStringSchema.toJson}",
)
}
case nonStringSchema =>
throw new IllegalArgumentException(
s"x-string-key-schema must be a string schema, but got: ${nonStringSchema.toJson}",
)
},
),
),
)
Expand Down
10 changes: 5 additions & 5 deletions zio-http-gen/src/main/scala/zio/http/gen/scala/Code.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object Code {
sealed trait ScalaType extends Code { self =>
def seq(nonEmpty: Boolean): Collection.Seq = Collection.Seq(self, nonEmpty)
def set(nonEmpty: Boolean): Collection.Set = Collection.Set(self, nonEmpty)
def map: Collection.Map = Collection.Map(self)
def map: Collection.Map = Collection.Map(self, None)
def opt: Collection.Opt = Collection.Opt(self)
}

Expand Down Expand Up @@ -165,10 +165,10 @@ object Code {
}

object Collection {
final case class Seq(elementType: ScalaType, nonEmpty: Boolean) extends Collection
final case class Set(elementType: ScalaType, nonEmpty: Boolean) extends Collection
final case class Map(elementType: ScalaType) extends Collection
final case class Opt(elementType: ScalaType) extends Collection
final case class Seq(elementType: ScalaType, nonEmpty: Boolean) extends Collection
final case class Set(elementType: ScalaType, nonEmpty: Boolean) extends Collection
final case class Map(elementType: ScalaType, keysType: Option[ScalaType]) extends Collection
final case class Opt(elementType: ScalaType) extends Collection
}

sealed trait Primitive extends ScalaType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,12 @@ object CodeGen {
val (imports, tpe) = render(basePackage)(elementType)
if (nonEmpty) (Code.Import("zio.prelude.NonEmptySet") :: imports) -> s"NonEmptySet[$tpe]"
else imports -> s"Set[$tpe]"
case Code.Collection.Map(elementType) =>
val (imports, tpe) = render(basePackage)(elementType)
imports -> s"Map[String, $tpe]"
case Code.Collection.Map(elementType, keysType) =>
val (vImports, vType) = render(basePackage)(elementType)
keysType.fold(vImports -> s"Map[String, $vType]") { keyType =>
val (kImports, kType) = render(basePackage)(keyType)
(kImports ::: vImports).distinct -> s"Map[$kType, $vType]"
}
case Code.Collection.Opt(elementType) =>
val (imports, tpe) = render(basePackage)(elementType)
imports -> s"Option[$tpe]"
Expand Down
9 changes: 9 additions & 0 deletions zio-http-gen/src/test/resources/ComponentAliasOrderId.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test.component

import zio.prelude.Newtype
import zio.schema.Schema
import java.util.UUID

object OrderId extends Newtype[UUID] {
hochgi marked this conversation as resolved.
Show resolved Hide resolved
implicit val schema: Schema[OrderId.Type] = Schema.primitive[UUID].transform(wrap, unwrap)
hochgi marked this conversation as resolved.
Show resolved Hide resolved
}
9 changes: 9 additions & 0 deletions zio-http-gen/src/test/resources/ComponentAliasUserId.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test.component

import zio.prelude.Newtype
import zio.schema.Schema
import java.util.UUID

object UserId extends Newtype[UUID] {
implicit val schema: Schema[UserId.Type] = Schema.primitive[UUID].transform(wrap, unwrap)
}
14 changes: 14 additions & 0 deletions zio-http-gen/src/test/resources/ComponentOrder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package test.component

import zio.schema._
import java.util.UUID

case class Order(
id: UUID,
product: String,
@zio.schema.annotation.validate[Int](zio.schema.validation.Validation.greaterThan(0)) quantity: Int,
@zio.schema.annotation.validate[Double](zio.schema.validation.Validation.greaterThan(-1.0)) price: Double,
)
object Order {
implicit val codec: Schema[Order] = DeriveSchema.gen[Order]
}
13 changes: 13 additions & 0 deletions zio-http-gen/src/test/resources/ComponentOrderWithAliases.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package test.component

import zio.schema._

case class Order(
id: OrderId.Type,
product: String,
@zio.schema.annotation.validate[Int](zio.schema.validation.Validation.greaterThan(0)) quantity: Int,
@zio.schema.annotation.validate[Double](zio.schema.validation.Validation.greaterThan(-1.0)) price: Double,
)
object Order {
implicit val codec: Schema[Order] = DeriveSchema.gen[Order]
}
12 changes: 12 additions & 0 deletions zio-http-gen/src/test/resources/ComponentUserOrderHistory.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package test.component

import zio.schema._
import java.util.UUID

case class UserOrderHistory(
user_id: UUID,
history: Map[UUID, Order],
)
object UserOrderHistory {
implicit val codec: Schema[UserOrderHistory] = DeriveSchema.gen[UserOrderHistory]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.component

import zio.schema._

case class UserOrderHistory(
user_id: UserId.Type,
history: Map[OrderId.Type, Order],
)
object UserOrderHistory {
implicit val codec: Schema[UserOrderHistory] = DeriveSchema.gen[UserOrderHistory]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
info:
title: Shop Service
version: 0.0.1
servers:
- url: http://127.0.0.1:5000/
tags:
- name: Order_API
paths:
/api/v1/shop/history/{id}:
get:
operationId: get_user_history
parameters:
- in: path
name: id
schema:
type: string
format: uuid
required: true
tags:
- Order_API
description: Get user order history by user id
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrderHistory'
description: OK
openapi: 3.0.3
components:
schemas:
UserOrderHistory:
type: object
required:
- user_id
- history
properties:
user_id:
type: string
format: uuid
history:
type: object
additionalProperties:
$ref: '#/components/schemas/Order'
x-string-key-schema:
type: string
format: uuid
Order:
type: object
required:
- id
- product
- quantity
- price
properties:
id:
type: string
format: uuid
product:
type: string
quantity:
type: integer
format: int32
minimum: 1
price:
type: number
minimum: 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
info:
title: Shop Service
version: 0.0.1
servers:
- url: http://127.0.0.1:5000/
tags:
- name: Order_API
paths:
/api/v1/shop/history/{id}:
get:
operationId: get_user_history
parameters:
- in: path
name: id
schema:
$ref: '#/components/schemas/UserId'
required: true
tags:
- Order_API
description: Get user order history by user id
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrderHistory'
description: OK
openapi: 3.0.3
components:
schemas:
UserOrderHistory:
type: object
required:
- user_id
- history
properties:
user_id:
$ref: '#/components/schemas/UserId'
history:
type: object
additionalProperties:
$ref: '#/components/schemas/Order'
x-string-key-schema:
$ref: '#/components/schemas/OrderId'
Order:
type: object
required:
- id
- product
- quantity
- price
properties:
id:
$ref: '#/components/schemas/OrderId'
product:
type: string
quantity:
type: integer
format: int32
minimum: 1
price:
type: number
minimum: 0
OrderId:
type: string
format: uuid
UserId:
type: string
format: uuid
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
info:
title: Shop Service
version: 0.0.1
servers:
- url: http://127.0.0.1:5000/
tags:
- name: Order_API
paths:
/api/v1/shop/history/{id}:
get:
operationId: get_user_history
parameters:
- in: path
name: id
schema:
$ref: '#/components/schemas/UserId'
required: true
tags:
- Order_API
description: Get user order history by user id
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/UserOrderHistory'
description: OK
openapi: 3.0.3
components:
schemas:
UserOrderHistory:
type: object
required:
- user_id
- history
properties:
user_id:
$ref: '#/components/schemas/UserId'
history:
type: object
additionalProperties:
$ref: '#/components/schemas/Order'
x-string-key-schema:
$ref: '#/components/schemas/OrderId'
Order:
type: object
required:
- id
- product
- quantity
- price
properties:
id:
$ref: '#/components/schemas/OrderId'
product:
type: string
quantity:
type: integer
format: int32
minimum: 1
price:
type: number
minimum: 0
OrderId:
type: integer
format: int32
UserId:
type: string
format: uuid
Loading
Loading