{"id":15286060,"url":"https://github.com/numberfour/openapi-scala","last_synced_at":"2025-04-13T03:07:52.369Z","repository":{"id":53195116,"uuid":"259959923","full_name":"NumberFour/openapi-scala","owner":"NumberFour","description":"An opinionated library and SBT plugin for generating Scala code from OpenAPI 3.","archived":false,"fork":false,"pushed_at":"2021-04-01T12:52:40.000Z","size":291,"stargazers_count":6,"open_issues_count":6,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-13T03:07:46.631Z","etag":null,"topics":["openapi","openapi-generator","openapi3","scala","swagger"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NumberFour.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-29T15:08:59.000Z","updated_at":"2023-08-25T08:39:34.000Z","dependencies_parsed_at":"2022-08-27T19:40:26.174Z","dependency_job_id":null,"html_url":"https://github.com/NumberFour/openapi-scala","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NumberFour%2Fopenapi-scala","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NumberFour%2Fopenapi-scala/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NumberFour%2Fopenapi-scala/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NumberFour%2Fopenapi-scala/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NumberFour","download_url":"https://codeload.github.com/NumberFour/openapi-scala/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248657918,"owners_count":21140846,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["openapi","openapi-generator","openapi3","scala","swagger"],"created_at":"2024-09-30T15:10:19.622Z","updated_at":"2025-04-13T03:07:52.312Z","avatar_url":"https://github.com/NumberFour.png","language":"Scala","readme":"# OpenAPI Scala\n\nThis project contains an opinionated library and SBT Plugin for Scala code generation from [OpenAPI 3.0](https://swagger.io/specification/) compliant YAML. \n\nThis will generate data structures(i.e., schemas or types), Circe JSON serializers, and Paths (i.e., route definitions) for REST APIs.\n\nWe do not support all OpenAPI 3.0 features. For more details on what is supported, look at the [Limitations](#limitations) section.\n\n## Modules\n\n### sbt-openapi\nThe SBT module `sbt-openapi` is the interface for the generator toolchain and contains:\na) an interface to easily add the openAPI generator to a project\nb) Put external openAPI resources/definitions/yaml-files next to internal definitions to make them referable. This enables\nsharing artifact across multiple openAPI projects (like shared error messages/ data structures).\nc) logic to load, resolve and aggregate openAPI definitions using the io.swagger.parser and generated a single combined\n openapi.json definition as output\nd) trigger code generation. (`openapi-scala`) \n\n### openapi-scala\n`openapi-scala`s main responsibility is to parse a single openAPI.json file into memory (`ast`), transform it into a\ngenerator friendly intermediate representation (`repr`) and generate scala code (`generator`).\n\n### openapi-lib \u0026 openapi-htt4s-lib\nLibraries used by the generated code.\n\n#### Routes\n\nWe support different types of Route generation, depending on the backend you need it for.  //TODO which backends do we have and how do we configure it?\n\n- The generic Routes are each translated to a Scala `trait` declaring interfaces for that particular HTTP route.\n- The http4s Routes translate into two files, one file with a trait for the implementation you'll need to provide and \none file with an object apply function of which accepts mentioned implementation trait. \n\nThe names of the functions implementing the route are either\n a) the concatenation of http method and path or\n b) the optionally more descriptive content of the `operationId` field (as specified in OAS 3.0) \n \n## Components\n\nComponents in OpenAPI are the types that can be referred to as inputs/outputs for routes. A common use of components is \nto define product types. We take these components and translate them to Scala case classes. Consider the following example:\n\n```yaml\ncomponents:\n    schemas:\n        Person:\n            description: My test description\n            properties:\n                name:\n                    type: string\n                weight:\n                    type: number\n```\n\nThe code above will be translated to following Scala code:\n\n```scala\n/**\n * My test description\n**/\nfinal case class Person(name: String, weight: Double)\n\nobject Person {\n    implicit val customDecoders = deriveDecoder[Person](renaming.snakeCase, None)\n    implicit val customEncoders = deriveEncoder[Person](renaming.snakeCase, true, None)\n\n}\n```\n\n### sbt-openapi\n\nSBT sub-project `sbt-openapi` contains an SBT plugin that allows the use of `openapi-scala` library to a given YAML file that will be loaded\nand used to generate managed Scala sources.\n\n## Quickstart\n\nA release log of the plugin and the library can be found [here](https://github.numberfour.eu/Server/openapi-scala/releases).\n\nTo use the plugin, you first need to make it available in your SBT.\n\n```scala\n// project/plugins.sbt\naddSbtPlugin(\"com.enfore\" % \"sbt-openapi\" % \"\u003copenapi-scala-version\u003e\")\n```\n\nYou will also need to make the library available to be able to use the generated code.\n\n```scala\nlibraryDependencies += \"com.enfore\" %% \"openapi-lib\" % \"\u003copenapi-scala-version\u003e\"\n```\n\nAdditionally, you will need to satisfy these library dependencies for `openapi-lib`. The provided versions are tested and made sure to work. You should be able to use any version compatible with your project though.\n\n// TODO shouldn't we automatically add these dependencies via the SBT auto plugin via flag?\n```scala\n\"com.beachape\" %% \"enumeratum\"       % \"1.5.13\",\n\"com.beachape\" %% \"enumeratum-circe\" % \"1.5.20\",\n\"io.circe\"     %% \"circe-derivation\" % \"0.11.0-M1\",\n\"com.chuusai\"  %% \"shapeless\"        % \"2.3.3\"\n```\n\nFor http4s Routes you will also need: \n\n```scala\n\"org.http4s\" %% s\"http4s-dsl\" % \"0.20.0-M7\",\n\"org.http4s\" %% s\"http4s-circe\" % \"0.20.0-M7\"\n```\n\nOnce the plugin is available in your project you can enable it on a given an SBT sub-project and use the setting `openAPIOutputPackage` to specify\nthe package name for your components. \n\n```scala\n// build.sbt\nlazy val root = (project in (\".\"))\n    .settings(\n        openAPIOutputPackage := \"com.enfore.model\",\n        libraryDependencies += \"com.enfore\" %% \"openapi-lib\" % \"\u003copenapi-scala-version\u003e\"\n    )\n    .enablePlugins(OpenapiPlugin)\n```\n\n### SBT Settings\n\nFollowing are the settings available for your SBT project.\n\n**openAPISource**: Source directory for OpenAPI. Defaults to `src/main/openapi` of the project.\n\n**openAPIOutput**: Output directory for the OpenAPI. Defaults to managed sources — `openapi`.\n\n**openAPIOutputPacakge**: Name of the package to be used for OpenAPI components.\n\n**routeImplementations**: A List of `com.enfore.apis.generator.RouteImplementation`, which controls which kind of routes should be generated. Find out more about [routes](#Routes)\n\n## Refinements\n\nOAS 3.0 allows one to set constraints on the primitive input types. We use the [Refined](https://github.com/fthomas/refined) library for Scala to reproduce these in the generated code. This means that you do not need to manually setup validations for any refinements and constraints in the inputs for your API. This, however, also means that you will have to return values with refinements if there are any in the return types.\n\nIn the example code below, we also generate a constructor for a value over which refinements exist. These refinements exist inside an object called `RefinementConstructors` that is further nested in the companion object for the generated case class. The names of the constructors will be the same as the values for which the refinements exist.\n\n```scala\nimport shapeless._\n\nfinal case class MyTestOutput(name: String Refined AllOf[MinSize[W.`1`.T] :: MaxSize[W.`256`.T] :: HNil])\n\nobject MyTestOutput {\n    object RefinementConstructors {\n        val name = String Refined AllOf[MinSize[W.`1`.T] :: MaxSize[W.`256`.T] :: HNil]\n\n    }\n}\n```\n\n## Read Only Properties\n\nOpenAPI 3.0 supports marking properties in objects as read-only. This checks that the property is available if the type is used as the output of a route, but not for inputs.\n\nWe model this in Scala by generating two types in case a field is marked read-only. The generator will then automatically use the right type depending on weather the object is used as input or output.\n\n```yaml\nMyObject:\n    type: object\n    properties:\n        id:\n            type: string\n            readOnly: true\n        body:\n            type: string\n```\n\nThis will generate two Scala classes that are as follows:\n\n```scala\nfinal case class MyObjectRequest(body: String)\n\nfinal case class MyObject(id: String, body: String)\n```\n\n## Unions \u0026 Enums\n\nOpenAPI 3.0 uses `oneOf` types to represent unions or enumerations. In Scala, there is no way to have proper Algebraic Data Types (ADTs). While products can be represented using case classes, the only way to represent unions is to create classes that extend a given trait.\n\nThis, however, makes defining arbitrary unions very difficult. Therefore, we use [Shapeless' discriminated unions](https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#coproducts-and-discriminated-unions) to represent `anyOf` types in Scala.\n\n```scala\nfinal case class MyUnionWrapper(value: Union)\n\nobject MyUnionWrapper {\n    type Union = Item1 :+: Item2 :+: CNil\n\n    object jsonConversions extends Poly1 {\n        implicit def caseItem1 = at[SingleTargetConversion](_.asJson.deepMerge(Json.obj(\"@type\" -\u003e Json.fromString(\"Item1\"))))\n        implicit def caseItem2 = at[MultiTargetConversion](_.asJson.deepMerge(Json.obj(\"@type\" -\u003e Json.fromString(\"Item2\"))))\n\n    }\n\n    implicit val customerEncoders = new Encoder[MyUnionWrapper] {\n      def apply(a: Conversion): Json = {\n        (a.value map jsonConversions).unify\n      }\n    }\n\n  implicit val customDecoder: Decoder[MyUnionWrapper] = new Decoder[Conversion] {\n    def apply(c: HCursor): Decoder.Result[MyUnionWrapper] = {\n      val output = c.downField(\"@type\").as[String] match {\n        case Right(\"Item1\") =\u003e c.value.as[Item1].map(Coproduct[Union](_))\n        case Right(\"Item2\") =\u003e c.value.as[Item2].map(Coproduct[Union](_))\n        case _ =\u003e Left(DecodingFailure.apply(\"Type information not available\", List(CursorOp.DownField(\"@type\"))))\n      }\n      output.map(MyUnionWrapper.apply)\n    }\n  }\n\n}\n```\n\nUsing these arbitrary unions is easier than it first appears. To create a value of type `MyUnionWrapper`, which is defined using `oneOf` in OpenAPI, we can use the following code:\n\n```scala\nimport shapeless._\nval item1Union = MyUnionWrapper(Coproduct[MyUnionWrapper.Union](item1))\nval item2Union = MyUnionWrapper(Coproduct[MyUnionWrapper.Union](item2))\n```\n\nHandling the values of that are already wrapped and mapping them to something is slightly more work. We use the `Poly1`  from Shapeless to create functions that can map over a co-product.\n\n```scala\nobject convertToT extends Poly1 {\n    implicit val oneToT = at[Item1](x =\u003e oneToT(x))\n    implicit val twoToT = at[Item2](x =\u003e twoToT(x))\n}\n\nval convertedItem: T = item1Union.fold(convertToT)\n```\n\nOur `convertToT` function converts every possible value to a type `T` and then we unify them. Since we know that our co-product will contain exactly one value of the possible given types, we can be certain that we will end up with at least one value of type `T`.\n\n## Limitations\n\nThis generator is highly opinionated and skips over certain features defined OpenAPI 3.0 specification for the sake of API consistency and compatibility with modern typed languages.\n\n### Limitations\n\n- We do not support any anonymous types for representing HTTP requests and responses.\n- We do not support nested type definitions. Therefore, any objects that are used must be defined in the components and must not contain\n  any other nested objects. Nested arrays, however, are supported.\n- We do not support more than one type for Content-Type header for incoming requests. Multiple types corresponding to their encodings are, however, available with an Accept-Encoding header on a request.\n- We do not support `anyOf` types from OpenAPI 3.0. Suggestions as to how the behaviour for the same should be represented in generated Scala code are welcome.\n- We do not support inlined schema definition for input and output object types in route definitions in OpenAPI input files. Therefore, these must be defined as components and referred.\n- We do not support type aliasing or rich typing. For instance, if you have a primitive type with a particular set of refinements, you would have to point out those refinements everywhere with those refinements instead of referring them from a definition.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnumberfour%2Fopenapi-scala","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnumberfour%2Fopenapi-scala","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnumberfour%2Fopenapi-scala/lists"}