{"id":31045105,"url":"https://github.com/dativebase/dativebaseclj","last_synced_at":"2025-09-14T16:57:51.103Z","repository":{"id":245384878,"uuid":"714809088","full_name":"dativebase/dativebaseclj","owner":"dativebase","description":"DativeBase in Clojure: Linguistic Data Management","archived":false,"fork":false,"pushed_at":"2024-06-22T14:22:15.000Z","size":170,"stargazers_count":0,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-22T22:58:45.408Z","etag":null,"topics":["fieldwork","languages","linguistics"],"latest_commit_sha":null,"homepage":"https://app.dative.ca","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/dativebase.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-05T21:37:29.000Z","updated_at":"2024-06-22T14:22:18.000Z","dependencies_parsed_at":"2024-06-21T22:54:27.778Z","dependency_job_id":"31a52f94-f7a2-413f-8b51-6670c4225077","html_url":"https://github.com/dativebase/dativebaseclj","commit_stats":null,"previous_names":["dativebase/dativebaseclj"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dativebase/dativebaseclj","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dativebase%2Fdativebaseclj","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dativebase%2Fdativebaseclj/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dativebase%2Fdativebaseclj/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dativebase%2Fdativebaseclj/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dativebase","download_url":"https://codeload.github.com/dativebase/dativebaseclj/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dativebase%2Fdativebaseclj/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275136757,"owners_count":25411709,"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","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"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":["fieldwork","languages","linguistics"],"created_at":"2025-09-14T16:57:50.056Z","updated_at":"2025-09-14T16:57:51.087Z","avatar_url":"https://github.com/dativebase.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"================================================================================\n  DativeBase in Clojure\n================================================================================\n\nFor local development documentation, see ``docs/local-development.rst``.\n\nDativeBase is an application for linguistic data management. It is designed to\nbe useful for linguists, language revitalizers, teachers, linguaphiles, and\nanybody who needs to manage language-focused data. DativeBase facilitates\nstoring, searching, sharing, and analyzing linguistic data.\n\nDativeBase is the successor of the earlier Dative/OLD project. Dative/OLD and\nDativeBase are both open-source software. However, there is only one significant\npublic *deployment* of the Dative/OLD, namely the one served at app.dative.ca.\nThe plan is for the DativeBase rewrite to ultimately replace the Dative/OLD app\nat app.dative.ca.\n\nTable of Contents:\n\n- `Authorization`_\n- `User Flows`_\n- `Data Model`_\n- `Continuous Integration \u0026 Deployment`_\n- `TODOs`_\n- `Principles`_\n- `How Immutable Data Works in DativeBase`_\n- `History of DativeBase`_\n- `Components`_\n- `Proof-of-concept Feature Brief for Read-only Offline Functionality`_\n- `Local Development`_\n- `Local SwaggerUI`_\n- `Docker`_\n- `The Online Linguistic Database (OLD)`_\n- `Usage`_\n- `Database Migrations`_\n\n\nAuthorization\n================================================================================\n\nThis section details the authorization rules in DativeBase. The guiding\nprinciple underlying the authorization design decisions described below is that\nanyone should be able to sign up for a free DativeBase plan and get started\nusing DativeBase right away.\n\nOur first important, foundational distinction is that between OLD-specific\nresources, like forms, and OLD-independent resources, like users, plans and OLDs.\n\nIn general, if a user has the contributor or administrator role for a given OLD,\nthen that user will be authorized to make mutative (data-changing) requests on\nany resource under said OLD. A user with the viewer role under an OLD can only\nmake read requests on that OLD.\n\nA second salient distinction is superuser status. Each user is a superuser or a\nnon-superuser. Most users are non-superusers. A superuser can, in general,\nperform any read or write action in the system. All entities must reference a\nuser as their creator and updater. The only exception to this is the user\nentity itself, which must be boostrap-able without a user.\n\nAn OLD must have an active plan in order to be usable. If an action on an OLD is\nnot covered by the entitlements granted by the plan, then the action will be\nprohibited.\n\n\nAuthorization for the User Resource\n--------------------------------------------------------------------------------\n\nUsers are the entrypoint to DativeBase. Anyone can create a user and then use\nthat user to create a free plan and then a number of OLDs running under that\nfree plan.\n\n\nUser Creation\n````````````````````````````````````````````````````````````````````````````````\n\nA new user may be created via the create-user operation, i.e., POST /users.\n\nAuthentication is not required in order to create a user in DativeBase. Such\nuser creation is effectively signup. Anybody on the public internet should be\nable to sign up to DativeBase. They should be able to create a user, a free\nplan, and one or more OLDs (with restricted entitlements) under said plan.\n\nObviously, since a superuser has unlimited access, a user created without\nauthentication may never be a superuser.\n\nIn addition, a user created without authentication must be activated before it\ncan be used. User activation means hitting a specific endpoint with a specific,\nrandomly generated UUID in its path. In production, this URL will be emailed to\nthe user.\n\n\nUser Update \u0026 Delete\n````````````````````````````````````````````````````````````````````````````````\n\nUser update is only allowed to superusers and the target user itself. Only a\nsuperuser can update a user into a superuser. Therefore, a superuser can only be\ncreated by someone with backend access to the DativeBase system.\n\nUser deletion is prohibited. Soft deletion may be supported in the future. Lossy\nor non-lossy user redaction may also be supported in the future. The challenge\nwith user deletion is that provenance is crucial to a knowledge base such as\nDativeBase. Therefore, full user deletion, without careful attention, would\ncorrupt the data.\n\nIt would probably be wise to support user deactivation (as a minimal user\ndeletion strategy) in the short term. It should be noted that a user could still\nexist in the system while having access to no OLDs and no plans, which in itself\nis a form of deactivation.\n\nIn order to reset the password of a user, the following steps must be taken.\n\n1. The user makes a ``GET /users/\u003cID\u003e/initiate-password-reset`` call.\n2. DativeBase refreshes the user's registration key and emails this key to the\n   email address of the user.\n3. The user makes a ``PUT /users/\u003cID\u003e/reset-password`` call. The JSON payload\n   contains the new ``password`` and the ``secret-key``, whose value is the\n   registration key that we emailed to the user. If successful, the password of\n   the user will be set to the password supplied in this PUT request.\n\n\nUser Read\n````````````````````````````````````````````````````````````````````````````````\n\nAny authenticated and activated user can view (read) the set of users in\nDativeBase. Users need to be able to view other users in order to be able to add\nthese users to their OLDs and/or to their plans.\n\nOn the other hand, users should be able to submit a request for access to an OLD\nand administrators should be able to view such requests.\n\nNote that non-superusers receive limited user data. They are not able to view the\nemail addresses of users, for example. A non-superuser can view their own data\nin full, howeever.\n\n\n\nAuthorization for the Plan Resource\n--------------------------------------------------------------------------------\n\nOnce a non-superuser has been created, the typical next step is to create a free\nplan with that user. A free plan allows limited access to the DativeBase\nservice. The details are still to-be-developed. However, we may provisionally\nassume that each free plan allows for 3 OLDs building under it, each with a\nmaximum number of forms. Further restrictions may be enabled later.\n\n\nPlan Creation\n````````````````````````````````````````````````````````````````````````````````\n\nAny user may create a new, free plan. This is accomplished via a POST /plans\nrequest.\n\nHowever, each (non-superuser) user is permitted to be the manager of at most 1\nplan. Given that creating a plan also entails the creator receiving a manager\nrole on said plan, this means, in effect, that each (non-superuser) user can\nonly create one plan. (If the user revokes their manager role over the plan,\nthen they may create a new plan.)\n\n\nPlan Update \u0026 Delete\n````````````````````````````````````````````````````````````````````````````````\n\nPlan update is not currently supported. The only property of a plan that can\nmeaningfully be updated is the tier and upgrading the tier from free to higher\nrequires a billing event.\n\nA plan can be deleted by a superuser or one of the plan's managers. However, a\nplan cannot be deleted while it is supporting OLDs. If any OLDs are running\nunder a plan, then these OLDs must first be removed from the plan before it can\nbe deleted. To remove an OLD from a plan, update the OLD (PUT /olds/:id) while\nsetting the plan ID to nil.\n\n\nAuthorization for the OLD Resource\n--------------------------------------------------------------------------------\n\nOLDs are a core resource in DativeBase. Each OLD (= Online Linguistic Database)\nis a data set, usually focused on a particular language, but sometimes on a\nresearch topic.\n\n\nOLD Creation\n````````````````````````````````````````````````````````````````````````````````\n\nAny user may create an OLD via the POST /olds operation. Creation of an OLD\nautomatically entails making the creating user an administrator of the\nnewly-created OLD.\n\nAn OLD that is not covered by a plan is not usable. An OLD can be configured to\nbe paid for under a plan during OLD creation or OLD update. In either case, the\nauthenticated user must be a manager of the plan in question (or a superuser of\nthe system) in order for the request to be authorized.\n\n\nOLD Update \u0026 Deletion\n````````````````````````````````````````````````````````````````````````````````\n\nAn OLD can be updated or deleted only by its administrators and by superusers.\n\nAll users can read the collection of OLDs (index) and get details on a specific\nOLD (show). Users need to be able to browse the set of OLDs in order for\nDativeBase to work.\n\n\nAuthorization for Forms and Other OLD-Dependent Resources\n--------------------------------------------------------------------------------\n\nForms belong to OLDs. As do tags, corpora, files, phonologies, etc. A user's\nauthorization to read or write OLD-specific resources depends on that user's\nrole within the OLD.\n\nAn administrator can perform any action. A contributor can perform most write\nactions and all reads. A viewer can perform all read actions but no writes.\n\n\nUser Flows\n================================================================================\n\n- Signup: person creates a DativeBase user\n- Plan Creation: User creates a plan for managing OLDs.\n- Grant Access: Administrator of an OLD grants access to a user to an OLD.\n- Cover OLD: Administrator of a plan covers an OLD under that plan.\n\nSignup\n--------------------------------------------------------------------------------\n\nAs a prospective user of DativeBase, I can create an account (a user) in\nDativeBase. As a result of signing up, a new user is created for me in\nDativeBase.\n\nImplications:\n\n- Anybody on the public internet can create a new account.\n- Email verification must be required. Therefore, signup is a two-step process.\n\n  1. First, the user signs up by entering their PII and desired credentials.\n     DativeBase then emails the user a registration confirmation link containing\n     a key, which expires.\n  2. Then, the user visits the link, which triggers authentiction. If the\n     authentication test passes, the user is verified.\n\n\nSteps to implement:\n\n- All users must have a registration-status attribute. Its default is pending.\n  It can transition from pending to registered.\n- A pending user cannot perform any actions except verification. Once\n  verification succeeds, the user becomes registered.\n\n\nPlan Creation\n--------------------------------------------------------------------------------\n\nAs a user of DativeBase, I can create a plan. A plan lets me pay for and manage\nOLDs. If I have a plan, I can create new OLDs that are covered by that plan,\ninsofar as the entitlements of my plan allow for this. If I have a plan, I can\ncover existing with that plan. I can transfer coverage of an OLD from its\nexisting plan to my plan.\n\n\nGrant Access\n--------------------------------------------------------------------------------\n\n\nData Model\n================================================================================\n\nThere are four basic entities:\n\n- Users\n- OLDs\n- Plans\n- Forms\n\nUsers have inherent roles. All users are either regular users or superusers.\nSuperusers have unlimited access to all public APIs.\n\nA user may have access to an OLD or not. In order for a user to have access to\nan OLD, there must be an active ``users_olds`` row linking said user to said OLD.\nThe ``role`` value of this row determines the user's level of access to the OLD.\nAn administrator can perform all actions on an OLD. A contributor can perform\nnearly all actions on an OLD. A viewer can only perform read actions on an OLD;\nno writes are permitted.\n\nA plan pays for an OLD. Every OLD must be covered by a plan. If an OLD exceeds\nthe entitlements of its plan, then the OLD becomes non-operational. In order to\nre-enable the OLD, the plan must be upgraded or the OLD must be moved under\nanother, more entitled plan.\n\n\nContinuous Integration \u0026 Deployment\n================================================================================\n\n\nTODOs\n================================================================================\n\n- Ensure that the commands in the ``Docker`` section are working.\n- I need to more clearly justify the inserted vs created distinction. Are both\n  of these columns really necessary?\n- Add stats infrastructure. See https://www.metricfire.com/blog/monitoring-your-infrastructure-with-statsd-and-graphite/.\n- Add specs for database tables.\n\n\nPrinciples\n================================================================================\n\n- Sustainability\n- Open Data\n- Immutability\n\n\nSustainability\n--------------------------------------------------------------------------------\n\nDativeBase must be sustainable. That is why it is both open-source and\nmonetizable as a service.\n\nThe source code of DativeBase is, and always will be, open-source and free. This\nmeans that even if the maintainers and developers of DativeBase change, its\ninner workings are always available for inspection, adoption, and future\ndevelopment.\n\nSoftware requires maintenance and non-remunerated maintenance is almost\ninevitably short-lived. If DativeBase provides value to its users, then those\nusers should be happy to pay a modest fee for its use. If a prospective user\nlacks the funds, they may reach out and be granted an exemption from the\nsubscription fee.\n\n\nOpen Data\n--------------------------------------------------------------------------------\n\nDativeBase will never hold your data hostage. DativeBase will provide full\nexports of data to the owners or stewards of that data, in open formats, i.e.,\nformats that do not require proprietary software to be read and manipulated.\n\nDativeBase will provide standard OpenAPI-compliant HTTP REST endpoints for\nfetching data sets. Datasets will be available in standard, open formats:\nprimarily JSON, .zip archives, and CSV files.\n\nDativeBase will include local-first functionality. This may be a fully-fledged\nDesktop application or it may be a progressive web app that stores data locally\nin the browser's local storage. Whatever the case, DativeBase will give users\naccess to the data on their own machines. DativeBase will provide seamless\nsynchronization between local data and shared datasets on the server.\n\n\nImmutability\n--------------------------------------------------------------------------------\n\nDativeBase will provide immutable data. This means data that both changes yet\nalso preserves its history. All previous states of all data points are preserved.\n\nThis strategy facilitates synchronization between local datasets and their\nremote counterparts. However, it also preserves the history and provenance of\ndata, which may itself have scientific utility.\n\n\nHow Immutable Data Works in DativeBase\n================================================================================\n\nThe data in DativeBase is immutable. This means that the data changes yet its\nhistory is never lost. The effect of this is that updated or destroyed data can\nbe restored. Another, perhaps more important, consequence is that two versions\nof a dataset (i.e., an OLD) can diverge and can later be merged (or\nsynchronized).\n\nAll immutable entities have their current state stored in traditional database\ntables. For example, the current state of a form with ID \"A\" is stored in table\n``forms``.\n\nWhen an entity, such as a form, is deleted, we do not actually drop the row from\nthe database. Instead, we update its ``destroyed_at`` value, changing it from\n``NULL`` to the timestamp of deletion.\n\nTo see the database schema of the OLD server, inspect the top-level file\n``schema.sql``. Alternatively, interact with the database directly via PSQL\nusing ``make db`` and run commands like ``\\dt`` and ``\\d+ events``.\n\n\nThe ``events`` Table\n--------------------------------------------------------------------------------\n\nThe histories of all immutable entities are stored in the ``events`` table.\nEvery time an entity is created, updated, or deleted, we store an event in this\ntable.\n\nThe data in the ``events`` table is (and must be) sufficient to fully\nreconstruct all of the data within the DativeBase instance. That is, we should\nbe able to drop all rows from all other tables and then perfectly reconstruct\nthe data in those tables using only the data in the events table.\n\nThe ``events`` table is an append-only log. No SQL ``UPDATE`` or ``DELETE``\noperations should ever be run on this table. Only ``INSERT`` oeprations are\npermitted.\n\nIn order to fully understand the events table, one must first internalize the\nbasic relationship between users, OLDs, and OLD-internal types, prototypically\nforms. Every user has access to zero or more OLDs. Every OLD contains zero or\nmore forms.\n\nHere is the schema of the ``events`` table::\n\n  CREATE TABLE public.events (\n      id uuid DEFAULT public.uuid_generate_v4() NOT NULL,\n      created_at timestamp with time zone DEFAULT now(),\n      old_slug text,\n      table_name text NOT NULL,\n      row_id uuid,\n      row_data text NOT NULL,\n      CONSTRAINT events_check_old_slug_or_row_id\n        CHECK (((old_slug IS NOT NULL)\n                OR (row_id IS NOT NULL)))\n  );\n\nDetails on the columns of the ``events`` table are provided below.\n\n- ``id``: This is the unique identifier and primary key of the event. Its value\n  is A UUID.\n- ``created_at``: This is a (UTC) timestamp indicating when the event was\n  created in DativeBase.\n- ``old_slug``: This is the slug (unique identifier) of the OLD to which the\n  event applies.\n\n  - Some entities, such as users, are not specific to a single OLD. The events\n    of such non-OLD-specific entities will have a value of ``NULL`` in this\n    column.\n  - Other entities, such as forms, are specific to a single OLD. The events\n    of such non-OLD-specific entities will have the slug of the entity's OLD in\n    this column.\n\n    - The OLDs themselves do have a non-null value in the ``events.old_slug``\n      column. This value is the ``slug`` value of the OLD itself.\n\n- ``table_name``: This is the name of the table where the entity's current state\n  is held. The table defines the type of the entity. Forms, for example, are\n  stored in the ``forms`` table and mutation events on forms have a value of\n  ``\"forms\"`` in the ``table_name`` column of the ``events`` table.\n- ``row_id``: This column holds the unique ID of the entity. Typically, this is\n  the value of the ``id`` column in the corresponding entity table, e.g.,\n  ``forms.id`` or ``users.id``.\n\n  - Since OLDs use ``slug`` as their ID, mutation events on OLDs have a ``NULL``\n    value in ``events.row_id``.\n\n- ``row_data``: This column holds a serialized representation of the state of\n  the entity at the ``created_at`` date.\n\n  - The data in ``row_data`` is serialized using EDN.\n  - Example:\n\n    - If a new form is created with transcription ``\"a\"``, an event will be\n      created where ``row_data`` contains an EDN-serialized representation of\n      the form with transcription ``\"a\"``.\n    - If a our form is updated to have transcription ``\"b\"``, an event will be\n      created where ``row_data`` contains an EDN-serialized representation of\n      the form with transcription ``\"b\"``.\n    - Finally, if a our form is deleted, an event will be created where\n      ``row_data`` contains an EDN-serialized representation of the form with a\n      ``destroyed_at`` value of the timestamp of deletion.\n\n\nThe ``forms`` Table\n--------------------------------------------------------------------------------\n\nForms are an example of an immutable and OLD-specific entity type. Forms are\nstored in the ``forms`` table. See below.::\n\n  CREATE TABLE public.forms (\n      id uuid DEFAULT public.uuid_generate_v4() NOT NULL,\n      old_slug text NOT NULL,\n      transcription text NOT NULL,\n      inserted_at timestamp with time zone DEFAULT now() NOT NULL,\n      created_at timestamp with time zone DEFAULT now() NOT NULL,\n      updated_at timestamp with time zone DEFAULT now() NOT NULL,\n      destroyed_at timestamp with time zone,\n      created_by uuid NOT NULL\n  );\n\nEach form belongs to a specific OLD. The ``forms.old_slug`` value is the\n``olds.slug`` value of the OLD to which the form belongs.\n\nThe ``inserted_at`` and ``created_at`` columns are similar in that both are\ntimestamps that default to the time of insertion. However, they are importantly\ndifferent. The ``created_at`` value indicates when the form was created by the\nuser. The ``created_at`` value should never change.\n\nThe ``inserted_at`` value is generally identical to ``created_at``. However,\nwhen a changeset (i.e., an ordered set of events) is ingested into the OLD, the\n``inserted_at`` value will be the time of insertion.\n\n\nHistory of DativeBase\n================================================================================\n\nDativeBase is a complete rewrite (in Clojure \u0026 ClojureScript) of the existing\nDative/OLD suite of linguistic data management tools.\n\nDative is already 1/3 rewritten in ClojureScript. See DativeReFrame. That project\nwill become a submodule of this one.\n\nThe motivation behind this rewrite is twofold. First, DativeBase must be\nmonetizable. Second, DativeBase must be a local-first application. (Third,\nPython is not as good as Clojure.)\n\n\nComponents\n================================================================================\n\n- common: Common code between components: specs, OpenAPI schemata, etc.\n- server: HTTP OpenAPI JSON service\n  - One set of users managing multiple OLDs, each containing forms.\n  - Monetization built in: plans cover the costs of OLDs. Plans have free,\n    subscriber, and supporter tiers. Users manage plans.\n- client: HTTP client conveniences for interacting with server. Can be required\n  by desktop, synchronizer, gui, etc.\n- gui: Dative ReFrame SPA\n  - Uses the API to provide user-friendly access to a user's OLDs.\n  - Uses the API to allow manager users to manage OLD plans.\n- TODO: desktop: DativeTop: Desktop-native, or Electron-like, desktop app that\n  interacts with local OLDs and allows synchronization.\n  - Similar experience to Dative, but as a native app built on JVM CLJ-F\n    (https://github.com/cljfx/cljfx), ClojureDart, Electron with ClojureScript,\n    or other.\n- TODO: synchronizer: library for synchronizaing follower OLDs with leaders. Can\n  be used by desktop.\n- TODO: morphoparser: separate, queue-based service for morphological parser\n  compilation, parsing, serving, etc.\n\n\nProof-of-concept Feature Brief for Read-only Offline Functionality\n================================================================================\n\nProof-of-concept feature brief::\n\n  Given DativeTopCLJ running on a local machine\n    And OLDCLJ running as a service on a local machine\n    And an OLD data set that is synced across DativeTopCLJ and OLDCLJ\n  When the user disconnects their wifi\n  Then the user can still read their OLD data set in DativeTopCLJ\n\n\nLocal Development\n================================================================================\n\nFollow these detailed steps to get the server (API) running locally and to\nconfirm that it is working as expected.\n\nConstruct the OpenAPI YAML from the OpenAPI EDN source and validate it::\n\n  $ make openapi\n  $ make lint-openapi\n  No results with a severity of 'error' found!\n\nThe first command generates the OpenAPI YAML specification file\n``resources/public/openapi/api.yaml`` from the Clojure source of truth at\n``dvb.server.http.openapi.spec/api``. The second command lints the YAML file using\nthe spectral library.\n\nStart the PostgreSQL database in a container and create the tables::\n\n  $ docker compose up -d --build\n\nRun the tests (optional)::\n\n  $ make tests\n\nConnect to the database via PSQL (optional)::\n\n  $ make db\n\nThe default configuration for the application is in ``dev-config.edn``.\n\nThe recommended way to run the server code while developing is from a\nClojure-integrated REPL, e.g., Emacs with Cider. See the expressions in the\ncomment block of ``dvb.server.repl``. Executing the following expression in that\ncode block will restart the system after reloading any code changes::\n\n  =\u003e (component.repl/reset)\n  :ok\n\nTo serve the application from the command line (i.e., a fresh Java process) with\nthe default config, the following are equivalent::\n\n  $ make run\n\t$ clj -X:run\n\nNo matter how the app was started up, you may access the API at\n``http://localhost:8080`` and the Swagger UI at\n``http://localhost:8080/swagger-ui/dist/index.html``.\n\nTo serve the application with a different configuration file::\n\n  $ clj -X:run :config-path '\"/home/joel/apps/dativebaseclj/dev-config-SECRET.edn\"'\n\n\nCreating a User and Authenticating to the API\n--------------------------------------------------------------------------------\n\nCreate a user with a specified email and password (optional)::\n\n  $ clj -X:init :password abc :email '\"abc@bmail.com\"'\n  {:user\n   {:id #uuid \"9af83804-2354-4884-8600-f4699794a468\",\n    :first_name \"Anne\",\n    :last_name \"Boleyn\",\n    :email \"abc@bmail.com\",\n    :password \"HASH\"})}\n\nWe can also create a new user from the REPL. In the ``dvb.server.repl`` ns,\nsearch for ``Create a new user, so we can login`` and define a ``user`` while\ncreating it in the database, as shown there.\n\nFOX\n\nCurrent issue: we cannot authenticate API requests because we cannot yet create\na user and an API key (machine user). See above.\n\nThe following log message is emitted when we attempt an API call with an app ID\nthat is not valid, i.e., does not exist in the DB::\n\n  Unable to locate the referenced machine-user.\n  {:x-app-id \"7ffb9182-f7f9-4a32-a931-0e9ad303e830\"}\n\nThis happens when the app ID is not a valid UUID string::\n\n  Exception thrown when attempting to query machine user based on X-APP-ID\n  {:x-app-id \"def\"}\n\nThis happens when one has not provided X-API-KEY (or X-APP-ID) in the request,\ni.e., has not \"authorized\" in the SwaggerUI interface::\n\n  A required API key value was not provided in the request.\n  {:name \"X-API-KEY\", :in :header}\n\n\nLocal SwaggerUI\n================================================================================\n\nIf you have DativeBase running locally, you can interact with its HTTP API via\nthe SwaggerUI at http://localhost:8080/swagger-ui/dist/index.html.\n\nFirst, you must ensure that you have a valid user in the database and that you\nhave identified an API key and ID for that server.\n\n\nDocker\n================================================================================\n\nBuild a docker image for DativeBase::\n\n  $ docker build -t dativebase .\n\nRun DativeBase in a docker container::\n\n  $ docker run -it --rm --name my-running-dativebase dativebase\n\nNote that the last command above currently fails because the DativeBase server is\nunable to make a connection to PostgreSQL at ``localhost:5432``. TODO\n\n\nThe Online Linguistic Database (OLD)\n================================================================================\n\nThe code under ``src/dvb/server`` corresponds to the Online Linguistic Database\n(OLD) of the original Python Dative system.\n\nA major sub-component of the server is an HTTP REST API that conforms to the\nOpenAPI spec.\n\nThis project is written in Clojure. This is a rewrite of a previous project of\nthe same name, written in Python. See TODO. When it is important to distinguish\nbetween the two projects, this one may be referred to as \"OLD-CLJ\".\n\n\nUsage\n================================================================================\n\nTo serve the OLD and a Swagger UI for interacting with it::\n\n  $ lein run\n\nNow visit the Swagger UI at::\n\n  http://localhost:8080/swagger-ui/dist/index.html\n\nClick the \"Authorize\" button and enter the API key \"olddative\".\n\nNow click \"GET /api/v1/forms\", then \"Try it out\", then \"Execute\". The Swagger UI\nwill make a request to the OLD and will receive a mock response.\n\n\nDatabase Migrations\n================================================================================\n\nTo create a database migration, first create a new migration file under\n``migrator/sql`` with::\n\n  $ ./scripts/create-migration.sh replace_me_with_migration_name\n\nThen rebuild the docker images and bring up the containers in order to trigger\nthe Flyway container ``migrator`` into creating the database schema in the\n``postgres`` container::\n\n  $ docker compose up -d --build --force-recreate\n\nVerify that the migrator exited successfully, with either of the following::\n\n  $ docker compose logs -f migrator\n  $ docker compose ps\n\nFinally, write the schema to ``schema.sql`` so that the revised schema (post\nmigration application) can be checked into version control::\n\n  $ make schema.sql\n\nIf the above works, you should see changes in the ``schema.sql`` file that\nreflect your migration.\n\n\nMigrating Legacy Dative/OLD OLDs to DativeBase\n================================================================================\n\nIn order to transition from Dative(/OLD) to DativeBase, we need to be able to\ningest the OLD data into the DativeBase schema.\n\nKeeping it simple to start, imagine we can shut down all external mutation to a\ngiven OLD. How would we migrate it?\n\nTODO. Return here\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdativebase%2Fdativebaseclj","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdativebase%2Fdativebaseclj","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdativebase%2Fdativebaseclj/lists"}