{"id":37026439,"url":"https://github.com/dividat/docless","last_synced_at":"2026-01-14T03:04:42.470Z","repository":{"id":49433388,"uuid":"128015365","full_name":"dividat/docless","owner":"dividat","description":"A scala DSL to generate JSON schema and swagger documentation for your web services.","archived":false,"fork":true,"pushed_at":"2021-06-18T07:31:48.000Z","size":164,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2023-06-30T04:41:26.457Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"timeoutdigital/docless","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dividat.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":"2018-04-04T06:08:58.000Z","updated_at":"2021-06-18T07:31:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dividat/docless","commit_stats":null,"previous_names":[],"tags_count":5,"template":null,"template_full_name":null,"purl":"pkg:github/dividat/docless","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dividat%2Fdocless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dividat%2Fdocless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dividat%2Fdocless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dividat%2Fdocless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dividat","download_url":"https://codeload.github.com/dividat/docless/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dividat%2Fdocless/sbom","scorecard":{"id":345301,"data":{"date":"2025-08-11","repo":{"name":"github.com/dividat/docless","commit":"d9bb7d0c172ac3f3de0e764b3cb05554db7b398c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":1,"reason":"Found 3/17 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T06:54:31.743Z","repository_id":49433388,"created_at":"2025-08-18T06:54:31.744Z","updated_at":"2025-08-18T06:54:31.744Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408800,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2026-01-14T03:04:41.779Z","updated_at":"2026-01-14T03:04:42.457Z","avatar_url":"https://github.com/dividat.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Docless\n\nA scala DSL to generate JSON schema and [swagger](http://swagger.io) documentation for your web services.\n\n-   [Why not just using Swagger-core?](#why-not-just-using-swagger-core)\n-   [Installation](#installation)\n    -   [JSON schema derivation](#json-schema-derivation)\n    -   [Algebraic data types](#algebraic-data-types)\n    -   [Swagger DSL](#swagger-dsl)\n    -   [Aggregating documentation from multiple\n        modules](#aggregating-documentation-from-multiple-modules)\n-   [Known issues](#known-issues)\n\nWhy not just using Swagger-core?\n--------------------------------\n\nWhile being to some extent usable for Scala projects,\n[swagger-core](https://github.com/swagger-api/swagger-core) suffers from\nsome serious limitations:\n\n-   It heavily relies on Java runtime reflection to generate Json\n    schemas for your data models. This might be fine for plain Java\n    objects, but it does not really play well with key scala idioms such\n    as case classes and sealed trait hierarchies.\n\n-   Swagger is implemented through JAX-RS annotations. These provide way\n    more limited means of abstraction and code reuse than a DSL directly\n    embedded into Scala.\n\nInstallation\n------------\n\nAdd the following to your `build.sbt`\n\n``` {.scala}\nlibraryDependencies += \"com.dividat\" %% \"docless\" % doclessVersion\n```\n\n### JSON schema derivation\n\nThis project uses Shapeless to automatically derive JSON schemas for\ncase classes and ADTs at compile time. By scraping unnecessary\nboilerplate code, this approach helps keeping documentation in sync with\nthe relevant business entities.\n\n``` {.scala}\nimport com.dividat.docless.schema._\n\ncase class Pet(id: Int, name: String, tag: Option[String])\n\nval petSchema = JsonSchema.deriveFor[Pet]\n```\n\n#### Case classes\n\nGiven a case class, generating a JSON schema is as easy as calling the\n`deriveFor` method and supplying the class as type parameter.\n\n``` {.scala}\nscala\u003e petSchema.asJson\nres2: io.circe.Json =\n{\n  \"type\" : \"object\",\n  \"required\" : [\n    \"id\",\n    \"name\"\n  ],\n  \"properties\" : {\n    \"id\" : {\n      \"type\" : \"integer\",\n      \"format\" : \"int32\"\n    },\n    \"name\" : {\n      \"type\" : \"string\"\n    },\n    \"tag\" : {\n      \"type\" : \"string\"\n    }\n  }\n}\n```\n\nThe generated schema can be serialised to JSON by calling the `asJson`\nmethod, which will return a\n[Circe](https://github.com/travisbrown/circe) JSON ast.\n\n### Algebraic data types\n\nArguably, the idea of ADT or sum type is best expressed using JsonSchema\n*oneOf* keyword. However, as Swagger UI seems to only support the\n`allOf`,\\\nthis library uses the latter as default. This can be easily overriden by\ndefining an implicit instance of `derive.Config` in the local scope:\n\n``` {.scala}\nimport com.dividat.docless.schema.derive.{Config, Combinator}\n\nsealed trait Contact\ncase class EmailAndPhoneNum(email: String, phoneNum: String) extends Contact\ncase class EmailOnly(email: String) extends Contact\ncase class PhoneOnly(phoneNum: String) extends Contact\n\nobject Contact {\n  implicit val conf: Config = Config(Combinator.OneOf)\n  val schema = JsonSchema.deriveFor[Contact]\n}\n```\n\n``` {.scala}\nscala\u003e Contact.schema.asJson\nres5: io.circe.Json =\n{\n  \"type\" : \"object\",\n  \"oneOf\" : [\n    {\n      \"$ref\" : \"#/definitions/EmailAndPhoneNum\"\n    },\n    {\n      \"$ref\" : \"#/definitions/EmailOnly\"\n    },\n    {\n      \"$ref\" : \"#/definitions/PhoneOnly\"\n    }\n  ]\n}\n```\n\nFor ADTs, as well as for case classes, the\n`JsonSchema.relatedDefinitions`\\\nmethod can be used to access the child definitions referenced in a\nschema:\n\n``` {.scala}\nscala\u003e Contact.schema.relatedDefinitions.map(_.id)\nres6: scala.collection.immutable.Set[String] = Set(PhoneOnly, EmailOnly, EmailAndPhoneNum)\n```\n\n#### Enumerable support\n\nDocless can automatically derive a Json schema enum for sum types\nconsisting of case objects only:\n\n``` {.scala}\n\nsealed trait Diet\n\ncase object Herbivore extends Diet\ncase object Carnivore extends Diet\ncase object Omnivore extends Diet\n```\n\nEnumeration values can be automatically converted into a string\nidentifier\\\nusing one of the pre-defined formats.\n\n``` {.scala}\nimport com.dividat.docless.schema.PlainEnum.IdFormat\n\nimplicit val format: IdFormat = IdFormat.SnakeCase\nval schema = JsonSchema.deriveEnum[Diet]\n```\n\n``` {.scala}\nscala\u003e schema.asJson\nres10: io.circe.Json =\n{\n  \"enum\" : [\n    \"herbivore\",\n    \"carnivore\",\n    \"omnivore\"\n  ]\n}\n```\n\nFinally, types that extend\n[enumeratum](https://github.com/lloydmeta/enumeratum) `EnumEntry` are\nalso supported through the `EnumSchema` trait:\n\n``` {.scala}\nimport enumeratum._\nimport com.dividat.docless.schema.EnumSchema\n\nsealed trait RPS extends EnumEntry with EnumEntry.Snakecase\n\nobject RPS extends Enum[RPS] with EnumSchema[RPS] {\n  case object Rock extends RPS\n  case object Paper extends RPS\n  case object Scissors extends RPS\n\n  override def values = findValues\n}\n```\n\nThis trait will define on the companion object an implicit instance of\\\n`JsonSchema[RPS]`.\n\n### Swagger DSL\n\nDocless provides a native scala implementation of the Swagger 2.0 model\ntogether with a DSL to easily manipulate and transform it.\n\n``` {.scala}\n\nimport com.dividat.docless.swagger._\nimport com.dividat.docless.schema._\n\nobject PetsRoute extends PathGroup {\n  val petResp = petSchema.asResponse(\"The pet\")\n\n  val petIdParam = Parameter\n    .path(\n      name = \"id\",\n      description = Some(\"The pet id\"),\n      format = Some(Format.Int32)\n    ).as[Int]\n\n  override val definitions = List(petSchema, errSchema).map(_.definition)\n\n  override val paths = List(\n    \"/pets/{id}\"\n       .Get(\n         Operation(\n           summary = Some(\"info for a specific pet\")\n         ).withParams(petIdParam)\n          .responding(errorResponse)(200 -\u003e petResp)\n       )\n       .Delete(\n         Operation() //...\n       )\n )\n\n}\n```\n\nThis not only provides better means of abstraction that JSON or YAML\n(i.e. binding, high order functions, implicit conversions, etc.), but it\nalso allows to integrate API documentation more tightly to the\napplication code.\n\n### Aggregating documentation from multiple modules\n\nAside for using Circe for JSON serialisation, Docless is not coupled to\nany specific Scala web framework. Nevertheless, it does provide a\ngeneric facility to enrich separate code modules with Swagger metadata,\nbeing these routes, controllers, or whatever else your framework calls\nthem.\n\n``` {.scala}\nimport com.dividat.docless.swagger._\n\ncase class Dino(name: String, extinctedSinceYears: Long, diet: Diet)\n\nobject DinosRoute extends PathGroup {\n\n  val dinoSchema = JsonSchema.deriveFor[Dino]\n  val dinoId = Parameter.path(\"id\").as[Int]\n  val dinoResp = dinoSchema.asResponse(\"A dinosaur!\")\n\n  override def definitions = Nil //\u003c= this should be instead: `dinoSchema.definitions.toList`\n\n  override def paths = List(\n    \"/dinos/{id}\"\n      .Get(\n        Operation(\n          summary = Some(\"info for a specific pet\")\n        ).withParams(dinoId)\n         .responding(errorResponse)(200 -\u003e dinoResp)\n      )\n    )\n}\n```\n\nThe `PathGroup` trait allows any Scala class or object to publish a list\nof endpoint paths and schema definitions. The `aggregate` method in the\n`PathGroup` companion object can then be used to merge the supplied\ngroups into a single Swagger API description.\n\n``` {.scala}\nscala\u003e val apiInfo = Info(\"Example API\")\napiInfo: com.dividat.docless.swagger.Info = Info(Example API,1.0,None,None,None,None)\n\nscala\u003e PathGroup.aggregate(apiInfo, List(PetsRoute, DinosRoute))\nres15: cats.data.ValidatedNel[com.dividat.docless.swagger.SchemaError,com.dividat.docless.swagger.APISchema] = Invalid(NonEmptyList(MissingDefinition(RefWithContext(TypeRef(Dino,None),ResponseContext(Get,/dinos/{id})))))\n```\n\nThe `aggregate` method will also verify that the schema definitions\nreferenced either in endpoint responses or in body parameters can be\nresolved. In the example above, the method returns a non-empty list with\na single `ResponseRef` error, pointing to the missing `Dino` definition.\nOn correct inputs, the method will return instead the resulting\n`APISchema` wrapped into a `cats.data.Validated.Valid`.\n\nKnown issues\n------------\n\nCurrently Docless does not support recursive types (e.g. trees or linked\nlists). As a way around, one can always define them manually using the\n`JsonSchema.instance[A]` method.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdividat%2Fdocless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdividat%2Fdocless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdividat%2Fdocless/lists"}