{"id":15143816,"url":"https://github.com/hodur-org/hodur-contentful-schema","last_synced_at":"2025-03-30T20:31:58.857Z","repository":{"id":62432897,"uuid":"158166736","full_name":"hodur-org/hodur-contentful-schema","owner":"hodur-org","description":"Hodur is a domain modeling approach and collection of libraries to Clojure.  By using Hodur you can define your domain model as data, parse and validate it, and then either consume your model via an API or use one of the many plugins to help you achieve mechanical results faster and in a purely functional manner.","archived":false,"fork":false,"pushed_at":"2019-07-10T19:49:44.000Z","size":75,"stargazers_count":4,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T10:12:35.651Z","etag":null,"topics":["clojure","cms","content","contentful","data","modeling","schema"],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/hodur-org.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-11-19T05:29:29.000Z","updated_at":"2023-01-07T22:36:48.000Z","dependencies_parsed_at":"2022-11-01T21:15:31.173Z","dependency_job_id":null,"html_url":"https://github.com/hodur-org/hodur-contentful-schema","commit_stats":null,"previous_names":["luchiniatwork/hodur-contentful-schema"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hodur-org%2Fhodur-contentful-schema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hodur-org%2Fhodur-contentful-schema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hodur-org%2Fhodur-contentful-schema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hodur-org%2Fhodur-contentful-schema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hodur-org","download_url":"https://codeload.github.com/hodur-org/hodur-contentful-schema/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246379366,"owners_count":20767694,"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":["clojure","cms","content","contentful","data","modeling","schema"],"created_at":"2024-09-26T10:04:30.421Z","updated_at":"2025-03-30T20:31:58.602Z","avatar_url":"https://github.com/hodur-org.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"[circleci-badge]: https://circleci.com/gh/hodur-org/hodur-contentful-schema.svg?style=shield\u0026circle-token=146b7ab96c4056373f64b69de3200c2485266c1f\n[circleci]: https://circleci.com/gh/hodur-org/hodur-contentful-schema\n[clojars-badge]: https://img.shields.io/clojars/v/hodur/contentful-schema.svg\n[clojars]: http://clojars.org/hodur/contentful-schema\n[contentful]: https://contentful.com/\n[contentful-cli]: https://www.contentful.com/developers/docs/tutorials/general/import-and-export/\n[contentful-config]: https://github.com/contentful/contentful-import/blob/master/example-config.json\n[contentful-types]: https://www.contentful.com/developers/docs/references/content-management-api/#/reference/content-types/content-type\n[contentful-editor]: https://www.contentful.com/developers/docs/concepts/editor-interfaces/\n[github-issues]: https://github.com/hodur-org/hodur-contentful-schema/issues\n[hodur-engine]: https://github.com/hodur-org/hodur-engine\n[hodur-engine-clojars-badge]: https://img.shields.io/clojars/v/hodur/engine.svg\n[hodur-engine-clojars]: http://clojars.org/hodur/engine\n[hodur-engine-definition]: https://github.com/hodur-org/hodur-engine#model-definition\n[hodur-engine-started]: https://github.com/hodur-org/hodur-engine#getting-started\n[license-badge]: https://img.shields.io/badge/license-MIT-blue.svg\n[license]: ./LICENSE\n[logo]: ./docs/logo-tag-line.png\n[motivation]: https://github.com/hodur-org/hodur-engine/blob/master/docs/MOTIVATION.org\n[plugins]: https://github.com/hodur-org/hodur-engine#hodur-plugins\n[status-badge]: https://img.shields.io/badge/project%20status-beta-brightgreen.svg\n[nodejs]: https://nodejs.org/en/\n\n# Hodur Contentful Schema\n\n[![CircleCI][circleci-badge]][circleci]\n[![Clojars][hodur-engine-clojars-badge]][hodur-engine-clojars]\n[![Clojars][clojars-badge]][clojars]\n[![License][license-badge]][license]\n![Status][status-badge]\n\n![Logo][logo]\n\nHodur is a descriptive domain modeling approach and related collection\nof libraries for Clojure.\n\nBy using Hodur you can define your domain model as data, parse and\nvalidate it, and then either consume your model via an API making your\napps respond to the defined model or use one of the many plugins to\nhelp you achieve mechanical, repetitive results faster and in a purely\nfunctional manner.\n\n\u003e This Hodur plugin provides the ability to generate\n\u003e [Contentful][contentful] schemas out of your Hodur model. You can\n\u003e then apply your schema to your Contentful environments.\n\n## Motivation\n\nFor a deeper insight into the motivations behind Hodur, check the\n[motivation doc][motivation].\n\n## Getting Started\n\nHodur has a highly modular architecture. [Hodur Engine][hodur-engine]\nis always required as it provides the meta-database functions and APIs\nconsumed by plugins.\n\nTherefore, refer the [Hodur Engine's Getting\nStarted][hodur-engine-started] first and then return here for\nDatomic-specific setup.\n\nAfter having set up `hodur-engine` as described above, we also need to\nadd `hodur/contentful-schema`, the plugin that creates Contentful\nSchemas out of your model to the `deps.edn` file:\n\n``` clojure\n  {:deps {hodur/engine            {:mvn/version \"0.1.6\"}\n          hodur/contentful-schema {:mvn/version \"0.1.1\"}}}\n```\n\nYou should `require` it any way you see fit:\n\n``` clojure\n  (require '[hodur-engine.core :as hodur])\n  (require '[hodur-contentful-schema.core :as hodur-contentful])\n```\n\nLet's expand our `Person` model from the original getting started by\n\"tagging\" the `Person` entity for Contentful. You can read more about\nthe concept of tagging for plugins in the sessions below but, in\nshort, this is the way we, model designers, use to specify which\nentities we want to be exposed to which plugins.\n\n``` clojure\n  (def meta-db (hodur/init-schema\n                '[^{:contentful/tag-recursive true}\n                  Person\n                  [^String first-name\n                   ^String last-name]]))\n```\n\nThe `hodur-contentful-schema` plugin exposes a function called\n`schema` that generates your model as a Contentful schema payload:\n\n``` clojure\n  (def contentful-schema (hodur-contentful/schema meta-db {:space-id \"\u003cYOUR_SPACE_ID\u003e\"))\n```\n\nYou should replace `\u003cYOUR_SPACE_ID\u003e` with the space id of your\nContentful instance.\n\nWhen you inspect `contentful-schema`, this is what you have:\n\n``` json\n  {\n    \"contentTypes\" : [ {\n      \"sys\" : {\n        \"space\" : {\n          \"sys\" : {\n            \"type\" : \"Link\",\n            \"linkType\" : \"Space\",\n            \"id\" : \"\u003cYOUR_SPACE_ID\u003e\"\n          }\n        },\n        \"id\" : \"person\",\n        \"type\" : \"ContentType\",\n        \"publishedVersion\" : 1\n      },\n      \"name\" : \"Person\",\n      \"description\" : null,\n      \"fields\" : [ {\n        \"id\" : \"firstName\",\n        \"name\" : \"First Name\",\n        \"type\" : \"Symbol\",\n        \"localized\" : false,\n        \"required\" : true,\n        \"validations\" : [ ],\n        \"omitted\" : false,\n        \"disabled\" : false\n      }, {\n        \"id\" : \"lastName\",\n        \"name\" : \"Last Name\",\n        \"type\" : \"Symbol\",\n        \"localized\" : false,\n        \"required\" : true,\n        \"validations\" : [ ],\n        \"omitted\" : false,\n        \"disabled\" : false\n      } ]\n    } ],\n    \"editorInterfaces\" : [ {\n      \"sys\" : {\n        \"id\" : \"default\",\n        \"type\" : \"EditorInterface\",\n        \"space\" : {\n          \"sys\" : {\n            \"type\" : \"Link\",\n            \"linkType\" : \"Space\",\n            \"id\" : \"\u003cYOUR_SPACE_ID\u003e\"\n          }\n        },\n        \"contentType\" : {\n          \"sys\" : {\n            \"id\" : \"person\",\n            \"type\" : \"Link\",\n            \"linkType\" : \"ContentType\"\n          }\n        }\n      },\n      \"controls\" : [ {\n        \"fieldId\" : \"firstName\",\n        \"widgetId\" : \"singleLine\"\n      }, {\n        \"fieldId\" : \"lastName\",\n        \"widgetId\" : \"singleLine\"\n      } ]\n    } ]\n  }\n```\n\nIn order to import the model above to your Contentful space, first\nmake sure you have [NodeJS installed][nodejs], then save the JSON\nreturned from `schema` to a file (i.e. `my-model.json`).\n\nYou will also need your Contentful settings to either on a\n`contentful-config.json` file to run the [Contentful\nCLI][contentful-cli]. More info on the [config file\nhere][contentful-config]. For the purposes of this getting started,\nI'm using something along the lines of:\n\n``` json\n{\n  \"spaceId\": \"\u003cYOUR_SPACE_ID\u003e\",\n  \"managementToken\": \"\u003cYOUR_MANAGEMENT_TOKEN\u003e\"\n}\n```\n\nThen you can run the importer with the following command:\n\n``` bash\n$ npx contentful-cli space import --config contentful-config.json --content-file my-model.json\n```\n\nYou can also specify the environment you are importing the model to\nwith the parameter `--environment-id`.\n\n## Model Definition\n\nAll Hodur plugins follow the [Model\nDefinition][hodur-engine-definition] as described on Hodur [Engine's\ndocumentation][hodur-engine].\n\n## Setting Display Name\n\nThe display name of entities and fields can be controlled by using the\nmarker `:contentful/display-name`:\n\n``` clojure\n  [^{:contentful/display-name \"My Dream List\"}\n   Dream\n   [^{:contentful/display-name \"The Dream Title\"}\n    title]]\n```\n\nIf no `:contentful/display-name` is provided, the plugin will default\nto a capitalized version of the entity or field name.\n\n## Making a Field the Display of an Entity\n\nContentful uses one of the fields of each entity as a visual\nidentifier for editors on its admin interface.\n\nIn order to specify which field is used for it, mark it with\n`:contentful/display-field true`.\n\n## Documenting Entities and Fields\n\nThe marker `:doc` is fully supported. Both entity and field\ndocumentations will show on the admin for editors.\n\n## Specifying Contentful Types\n\nContentful-specific types can be specified by using the marker\n`:contentful/type`.\n\nThe supported basic types are:\n\n- `Symbol` (short text - default for `String`)\n- `Text` (long text)\n- `Integer` (default for `Integer`)\n- `Number` (default for `Float`)\n- `Date` (default for `DateTime`)\n- `Boolean` (default for `Boolean`)\n- `Object`\n- `Location`\n- `RichText`\n\nIn general you don't need to specify the following ones because they\nare managed internally by the plugin but, for reference:\n\n- `Array` (default for any multiple `cardinality`)\n- `Link` (default for linking to one asset and other user-specified entities)\n\nLast but not least, you can also specify `Asset` as a special type\nthat will point to an asset (or more if cardinality is many) on the\ndigital asset manager:\n\n- `Asset`\n\n## Asset Fields\n\nBy specifying `:contentful/type \"Asset\"` you are letting Contentful\nknow that a certain field should be associated with and asset from the\ndigital asset manager.\n\nThis field can also have cardinality of many (`[0 n]`) and it should\nlet editors choose several assets for it.\n\nPlease refer to the section describing [further validations down\nbelow](#field-validations) for examples on how to limit to certain\nkind of assets (images for instance) and certain image features.\n\nAlso, refer to the [widget configuration\nbelow](#choosing-widget-for-fields) as there are different widgets\nthat can be used for asset selection.\n\n## ID Fields\n\n`ID` fields are sent to Contentful as `Symbol` by default. Please do\nprovide a different `:contentful/type` if you need something else.\n\nIn addition, `ID` fields are automatically marked as unique by\ndefault. If you prefer to control this more granularly, use a more\nbasic data type (`String` i.e.) and detail your validations manually\nas documented in the section below.\n\n## Enum Fields\n\nFields that point to `:enum` entities will be sent to Contentful as\n`Symbol` by default. If you need a different type, please provide it\nvia `:contentful/type`.\n\nBy default the values of the enum are used as an `:in` validation for\nthe field. Therefore, the editor will be constrained to select one of\nthe options.\n\nA `dropdown` widget is chosen by default in order to help editors\nunderstand the selection. If you prefer a different rendering (such as\na `radio` or a `singleLine`) you can specify it with the\n`:contentful/widget-id` marker as documented in the respective section\nbelow.\n\n## Field Validations\n\nThis plugin acts as a pass-through to the validations specified on\nmarker `:contentful/validations`. This marker, when specified, must be\nan array of at least one entry. The full documentation of all the\n[field validations available on Contentful can be found\nhere][contentful-types].\n\nHere's an example showing some of these combined. They are pretty\nself-explanatory:\n\n``` clojure\n  [ValidationEntity\n   [;; will validate that `platform-field` is either `iOS` or `Android`\n    ^{:type String\n      :contentful/validations [{:in [\"iOS\" \"Android\"]}]}\n    platform-field\n\n    ;; will validate that `range-field` is between 5 and 15 with a custom message\n    ^{:type Integer\n      :contentful/validations [{:range {:min 5\n                                        :max 15}\n                                :message \"Must be between 5 and 15\"}]}\n    range-field\n\n    ;; will validate that `regexp-field` follows regexp `/^such/im`\n    ^{:type String\n      :contentful/validations [{:regexp {:pattern \"^such\"\n                                         :flags \"im\"}}]}\n    regexp-field\n\n    ;; will validate that `unique-field` is unique\n    ^{:type String\n      :contentful/validations [{:unique true}]}\n    unique-field\n\n    ;; will validate that `date-range-field` is between the min and max date\n    ^{:type DateTime\n      :contentful/validations [{:date-range {:min \"2017-05-01\"\n                                             :max \"2020-05-01\"}}]}\n    date-range-field\n\n    ;; will validate that `enabled-node-types-field` has only the specified node types active\n    ^{:type String\n      :contentful/type \"RichText\"\n      :contentful/validations [{:enabled-node-types [\"heading-1\"\n                                                     \"quote\"\n                                                     \"embedded-entry-block\"]}]}\n    enabled-node-types-field\n\n    ;; will validate that `enabled-marks-field` has only the specified marks enabled\n    ^{:type String\n      :contentful/type \"RichText\"\n      :contentful/validations [{:enabled-marks [\"bold\" \"italics\"]}]}\n    enabled-marks-field\n\n    ;; will validate that `multiple-validations-field` is both foo or bar, and between\n    ;; 2 and 5 characters with custom messages\n    ^{:type String\n      :contentful/validations [{:in [\"foo\" \"bar\"]\n                                :message \"Should be foo or bar\"}\n                               {:size {:min 2\n                                       :max 5}\n                                :message \"Should have 2 to 5 characters\"}]}\n    multiple-validations-field\n\n    ;; will validate that `multiple-asset-validations-field` is an image, within certain\n    ;; dimensions foo or bar, and certain byte size between with custom messages\n    ^{:contentful/type \"Asset\"\n      :contentful/validations [{:link-mimetype-group [\"image\"]\n                                :message \"Must be of MIME-Type image\"}\n                               {:asset-image-dimensions\n                                {:width {:min 100\n                                         :max 1000}\n                                 :height {:min 200\n                                          :max 2300}}\n                                :message \"Width must be 100-1000 and height 200-2300\"}\n                               {:asset-file-size {:min 1048576\n                                                  :max 8388608}\n                                :message \"File must be between 1048576B and 8388608B\"}]}\n    multiple-asset-validations-field]]\n```\n\n## Choosing Widget for Fields\n\nIn order to make the experience more interesting for editors,\nContentful supports several dedicated widgets. A widget for a field\ncan be specified with the marker `:contentful/widget-id`. If a widget\nis not specified a reasonable default one will be selected.\n\nA full list of the [available widgets can be found\nhere][contentful-editor]. As of this writing, the options are:\n\n| Widget ID            | Applicable field types        | Description                                                                                                     |\n|----------------------|-------------------------------|-----------------------------------------------------------------------------------------------------------------|\n| `assetLinkEditor`    | Asset                         | Search, attach, and preview an asset.                                                                           |\n| `assetLinksEditor`   | Asset (array)                 | Search, attach, reorder, and preview multiple assets.                                                           |\n| `assetGalleryEditor` | Asset (array)                 | Search, attach, reorder, and preview multiple assets in a gallery layout                                        |\n| `boolean`            | Boolean                       | Radio buttons with customizable labels.                                                                         |\n| `datePicker`         | Date                          | Select date, time, and timezone.                                                                                |\n| `entryLinkEditor`    | Entry                         | Search and attach another entry.                                                                                |\n| `entryLinksEditor`   | Entry (array)                 | Search and attach multiple entries.                                                                             |\n| `entryCardEditor`    | Entry                         | Search, attach, and preview another entry.                                                                      |\n| `entryCardsEditor`   | Entry (array)                 | Search, attach and preview multiple entries.                                                                    |\n| `numberEditor`       | Integer, Number               | A simple input for numbers.                                                                                     |\n| `rating`             | Integer, Number               | Uses stars to select a number.                                                                                  |\n| `locationEditor`     | Location                      | A map to select or find coordinates from an address.                                                            |\n| `objectEditor`       | Object                        | A code editor for JSON                                                                                          |\n| `urlEditor`          | Symbol                        | A text input that also shows a preview of the given URL.                                                        |\n| `slugEditor`         | Symbol                        | Automatically generates a slug and validates its uniqueness across entries.                                     |\n| `listInput`          | Symbol (array)                | Text input that splits values on , and stores them as an array.                                                 |\n| `checkbox`           | Symbol (array)                | A group of checkboxes. One for each value from the in validation on the content type field                      |\n| `tagEditor`          | Symbol (array)                | A text input to add a string to the list. Shows the items as tags and allows to remove them.                    |\n| `multipleLine`       | Text                          | A simple \u003ctextarea\u003e input                                                                                       |\n| `markdown`           | Text                          | A full-fledged markdown editor                                                                                  |\n| `singleLine`         | Text, Symbol                  | A simple text input field                                                                                       |\n| `dropdown`           | Text, Symbol, Integer, Number | A \u003cinput type=\"select\"\u003e element. It uses the values from an in validation on the content type field as options. |\n| `radio`              | Text, Symbol, Integer, Number | A group of radio buttons. One for each value from the in validation on the content type field                   |\n\n\nHere's a simple example:\n\n``` clojure\n  [MarketingEntry\n   [^{:type String\n      :contentful/widget-id \"urlEditor\"} url\n    ^{:type Integer\n      :contentful/widget-id \"rating\"}    stars]]\n```\n\n## Widget Settings\n\nWith the exception of the help text, all other widget settings are\navailable via Hodur with specific markers.\n\nFor `boolean` widget:\n\n- `:contentful/true-label` : Shows this text next to the radio button\n  that sets this value to `true`. Defaults to \"Yes\".\n- `:contentful/false-label` : Shows this text next to the radio button\n  that sets this value to `false`. Defaults to \"No\".\n\nFor `rating` widget:\n\n- `:contentful/stars` : Number of stars to select from. Defaults to 5.\n\nFor `datePicker` widget:\n\n- `:contentful/format` : One of \"dateonly\", \"time\", \"timeZ\"\n  (default). Specifies whether to show the clock and/or timezone\n  inputs.\n- `:contentful/ampm` : Specifies which type of clock to use. Must be\n  one of the strings \"12\" or \"24\" (default).\n\nExample:\n\n``` clojure\n  [Entity\n   [^{:type Integer\n      :contentful/widget-id \"rating\"\n      :contentful/stars 10}\n    stars-field\n\n    ^{:type Boolean\n      :contentful/true-label \"Si!\"\n      :contentful/false-label \"No!\"}\n    si-o-no-field\n\n    ^{:type DateTime\n      :contentful/format \"dateonly\"}\n    date-only-field\n\n    ^{:type DateTime\n      :contentful/format \"time\"}\n    time-field\n\n    ^{:type DateTime\n      :contentful/format \"timeZ\"}\n    full-date-time-field\n\n    ^{:type DateTime\n      :contentful/ampm \"12\"}\n    american-style-time-field]]\n```\n\n## Limitations \u0026 Assumptions\n\n- This plugin ignores `interfaces` and field parameters.\n- `PascalCasing` is used on naming entities and `camelCasing` is used\n  on all fields\n- There are no validations on the widgets, validations, or other\n  Contentful-specific markers. They are simply passed over to\n  Contentful.\n- Unions are supported by creating entry relationships that support\n  multiple content types.\n\n## Bugs\n\nIf you find a bug, submit a [GitHub issue][github-issues].\n\n## Help!\n\nThis project is looking for team members who can help this project\nsucceed! If you are interested in becoming a team member please open\nan issue.\n\n## License\n\nCopyright © 2019 Tiago Luchini\n\nDistributed under the MIT License (see [LICENSE][license]).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhodur-org%2Fhodur-contentful-schema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhodur-org%2Fhodur-contentful-schema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhodur-org%2Fhodur-contentful-schema/lists"}