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] normalized fields with annotations holding original field name #3069

Merged
merged 13 commits into from
Sep 4, 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
13 changes: 7 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,9 @@ lazy val zioHttpExample = (project in file("zio-http-example"))
.settings(libraryDependencies ++= Seq(`jwt-core`, `zio-schema-json`))
.settings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio-config" % ZioConfigVersion,
"dev.zio" %% "zio-config-typesafe" % ZioConfigVersion,
"dev.zio" %% "zio-config-magnolia" % ZioConfigVersion,
`zio-config`,
`zio-config-magnolia`,
`zio-config-typesafe`,
"dev.zio" %% "zio-metrics-connectors" % "2.3.1",
"dev.zio" %% "zio-metrics-connectors-prometheus" % "2.3.1",
),
Expand All @@ -305,6 +305,7 @@ lazy val zioHttpGen = (project in file("zio-http-gen"))
`zio`,
`zio-test`,
`zio-test-sbt`,
`zio-config`,
scalafmt.cross(CrossVersion.for3Use2_13),
scalametaParsers
.cross(CrossVersion.for3Use2_13)
Expand Down Expand Up @@ -404,9 +405,9 @@ lazy val docs = project
libraryDependencies ++= Seq(
`jwt-core`,
"dev.zio" %% "zio-test" % ZioVersion,
"dev.zio" %% "zio-config" % ZioConfigVersion,
"dev.zio" %% "zio-config-magnolia" % ZioConfigVersion,
"dev.zio" %% "zio-config-typesafe" % ZioConfigVersion,
`zio-config`,
`zio-config-magnolia`,
`zio-config-typesafe`,
),
publish / skip := true,
mdocVariables ++= Map(
Expand Down
4 changes: 4 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ object Dependencies {
val ZioVersion = "2.1.9"
val ZioCliVersion = "0.5.0"
val ZioJsonVersion = "0.7.1"
val ZioParserVersion = "0.1.10"
val ZioSchemaVersion = "1.4.1"
val SttpVersion = "3.3.18"
val ZioConfigVersion = "4.0.2"
Expand Down Expand Up @@ -36,6 +37,9 @@ object Dependencies {

val zio = "dev.zio" %% "zio" % ZioVersion
val `zio-cli` = "dev.zio" %% "zio-cli" % ZioCliVersion
val `zio-config` = "dev.zio" %% "zio-config" % ZioConfigVersion
val `zio-config-magnolia` = "dev.zio" %% "zio-config-magnolia" % ZioConfigVersion
val `zio-config-typesafe` = "dev.zio" %% "zio-config-typesafe" % ZioConfigVersion
val `zio-json-yaml` = "dev.zio" %% "zio-json-yaml" % ZioJsonVersion
val `zio-streams` = "dev.zio" %% "zio-streams" % ZioVersion
val `zio-schema` = "dev.zio" %% "zio-schema" % ZioSchemaVersion
Expand Down
97 changes: 71 additions & 26 deletions zio-http-gen/src/main/scala/zio/http/gen/openapi/Config.scala
Original file line number Diff line number Diff line change
@@ -1,38 +1,83 @@
package zio.http.gen.openapi

import zio.config.ConfigOps

import zio.http.gen.openapi.Config.NormalizeFields

// format: off
/**
* @param commonFieldsOnSuperType oneOf expressions in openapi result in sealed traits in generated scala code.
* if this flag is set to true, and all oneOf's "subtypes" are defined in terms of
* an allOf expression, and all share same object(s) included in the allOf expression,
* then the common fields from that shared object(s) will result in abstract fields
* defined on the sealed trait.
*
* @param generateSafeTypeAliases Referencing primitives, and giving them a name makes the openapi spec more readable.
* By default, the generated scala code will resolve the referenced name,
* and replace it with the primitive type.
* By setting this flag to true, the generator will create components with zio.prelude Newtype
* definitions wrapping over the aliased primitive type.
*
* Note: only aliased primitives are supported for now.
*
* TODO: in the future we can consider an enum instead of boolean for different aliased types.
* e.g: scala 3 opaque types, neotype, prelude's newtype, etc'…
*
* @param fieldNamesNormalization OpenAPI can declare fields that have unconventional "casing" in scala,
* like snake_case, or kebab-case.
* This configuration allows to normalize these fields.
* The original casing will be preserved via a @fieldName("<original-name>") annotation.
*/
// format: on
final case class Config(
commonFieldsOnSuperType: Boolean /*
* oneOf expressions in openapi result in sealed traits in generated scala code.
* if this flag is set to true, and all oneOf's "sub types" are defined in terms of
* an allOf expression, and all share same object(s) included in the allOf expression,
* then the common fields from that shared object(s) will result in abstract fields
* defined on the sealed trait.
*/,
generateSafeTypeAliases: Boolean, /*
* Referencing primitives, and giving them a name makes the openapi spec more readable.
* By default, the generated scala code will resolve the referenced name,
* and replace it with the primitive type.
* By setting this flag to true, the generator will create components with zio.prelude Newtype
* definitions wrapping over the aliased primitive type.
*
* Note: only aliased primitives are supported for now.
*
* TODO: in the future we can consider an enum instead of boolean for different aliased types.
* e.g: scala 3 opaque types, neotype, prelude's newtype, etc'…
*/
commonFieldsOnSuperType: Boolean,
generateSafeTypeAliases: Boolean,
fieldNamesNormalization: NormalizeFields,
)
object Config {

// format: off
/**
* @param enableAutomatic If enabled, the generator will attempt to normalize field names to camelCase,
* unless original field is defined in the specialReplacements map.
*
* @param manualOverrides When normalization is enabled, a heuristic parser will attempt to normalize field names.
* But this is not always possible, or may not yield the desired result.
* Consider field names that are defined in the JSON as `"1st"`, `"2nd"`, or `"3rd"`:
* You may want to override auto normalization in this case and provide a map like: {{{
* Map(
* "1st" -> "first",
* "2nd" -> "second",
* "3rd" -> "third"
* )
* }}}
*/
// format: on
final case class NormalizeFields(
enableAutomatic: Boolean,
manualOverrides: Map[String, String],
)
object NormalizeFields {
lazy val config: zio.Config[NormalizeFields] = (
zio.Config.boolean("enabled").withDefault(Config.default.fieldNamesNormalization.enableAutomatic) ++
zio.Config
.table("special-replacements", zio.Config.string)
.withDefault(Config.default.fieldNamesNormalization.manualOverrides)
).to[NormalizeFields]
}

val default: Config = Config(
commonFieldsOnSuperType = false,
generateSafeTypeAliases = false,
fieldNamesNormalization = NormalizeFields(
enableAutomatic = false,
manualOverrides = Map.empty,
),
)

lazy val config: zio.Config[Config] = {
val c =
zio.Config.boolean("common-fields-on-super-type").withDefault(Config.default.commonFieldsOnSuperType) ++
zio.Config.boolean("generate-safe-type-aliases").withDefault(Config.default.generateSafeTypeAliases)

c.map { case (a, b) => Config(a, b) }
}
lazy val config: zio.Config[Config] = (
zio.Config.boolean("common-fields-on-super-type").withDefault(Config.default.commonFieldsOnSuperType) ++
zio.Config.boolean("generate-safe-type-aliases").withDefault(Config.default.generateSafeTypeAliases) ++
NormalizeFields.config.nested("fields-normalization")
).to[Config]
}
Loading
Loading