{"id":23122771,"url":"https://github.com/folio-org/mod-circulation-storage","last_synced_at":"2025-08-17T03:30:51.444Z","repository":{"id":37905192,"uuid":"83340403","full_name":"folio-org/mod-circulation-storage","owner":"folio-org","description":"FOLIO mod-circulation-storage","archived":false,"fork":false,"pushed_at":"2024-10-18T09:25:00.000Z","size":2393,"stargazers_count":2,"open_issues_count":0,"forks_count":19,"subscribers_count":24,"default_branch":"master","last_synced_at":"2024-10-18T13:59:11.229Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/folio-org.png","metadata":{"files":{"readme":"README.MD","changelog":"NEWS.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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":"2017-02-27T17:58:40.000Z","updated_at":"2024-10-16T13:44:36.000Z","dependencies_parsed_at":"2024-03-07T16:58:34.806Z","dependency_job_id":"727d1fdd-780e-464c-9fcb-7c313bd75d24","html_url":"https://github.com/folio-org/mod-circulation-storage","commit_stats":null,"previous_names":[],"tags_count":82,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folio-org%2Fmod-circulation-storage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folio-org%2Fmod-circulation-storage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folio-org%2Fmod-circulation-storage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/folio-org%2Fmod-circulation-storage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/folio-org","download_url":"https://codeload.github.com/folio-org/mod-circulation-storage/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230080770,"owners_count":18169618,"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":[],"created_at":"2024-12-17T07:30:21.279Z","updated_at":"2025-08-17T03:30:51.425Z","avatar_url":"https://github.com/folio-org.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mod-circulation-storage\n\nCopyright (C) 2017-2025 The Open Library Foundation\n\nThis software is distributed under the terms of the Apache License,\nVersion 2.0. See the file \"[LICENSE](LICENSE)\" for more information.\n\n## Goal\n\nFOLIO compatible persistent storage of loans, loan policies, circulation rules,\nrequests, and fixed due date schedules\n\n## API\n\n### Where the API code lives\n\nThe implementation java files live in the\n[`/src/main/java/org/folio/rest/impl`](src/main/java/org/folio/rest/impl)\npackage. These implement the actual\njava interfaces, to be found in the\n[`/src/main/java/org/folio/rest/jaxrs/resource`](src/main/java/org/folio/rest/jaxrs/resource)\npackage, which are automatically generated from the `raml` files in\n[`/ramls/`](ramls).\n\nCorresponding API tests can be found in the\n[`/src/test/java/org/folio/rest/api`](src/test/java/org/folio/rest/api)\npackage.\n\n### API documentation\n\nThe API documentation generated from the `*.raml` files can be found online at\n[https://dev.folio.org/reference/api/#mod-circulation-storage](https://dev.folio.org/reference/api/#mod-circulation-storage)\n\n\n## Implementation Specific Behaviours\n\nAs a FOLIO interface is limited to describing the protocol (HTTP endpoints and\nthe structure of expected requests and responses) between the client and server\nthere are behaviors which go beyond this definition and are implementation specific,\nsome of which are noted below.\n\n### Only one Open Loan per Item\n\nThis implementation introduces the constraint that only one loan can be open for a\ngiven item.\n\nHTTP Requests (either POST or PUT) which could result in two loans with the\nsame `itemId` and the `status` of `Open` should be rejected with a error (422) response\n\n### Unique Position in per-item Request Queue\n\nThis implementation introduces the constraint that only one open (e.g. `Open - Not yet filled`) request\nfor a given item can be at a particular position in the request queue for that item.\n\nHTTP Requests (either POST or PUT) which could result two open requests with the\nsame `itemId` and `position` should be rejected with a error (422) response\n\n### Known Limitations\n\n#### Anonymization SQL\n\nAt the moment, this is constructed with string concatenation (it will hopefully soon be replaced by prepared statements).\n\nThe `userId` parameter is checked to take the form of a UUID to try to reduce the exposure of this.\n\n## Prerequisites\n\n## Required\n\n- Java 11 JDK\n- Maven 3.3.9\n- Docker ([minimum requirements](https://www.testcontainers.org/supported_docker_environment/))\n- PostgreSQL 9.6.1 (running and listening on the default port, logged in user must have admin rights), with the following extensions\n  - pgcrypto\n  - unaccent\n  - pg_trgm\n- Kafka 2.6 (running and listening on localhost:9092)\n\n### Optional\n\n- Python 3.6.0 (for un-registering module during managed deployment scripts, and the lint-raml tools)\n\n## Preparation\n\n### Git Submodules\n\nThere are some common RAML definitions that are shared between FOLIO projects via Git submodules.\n\nTo initialise these please run `git submodule init \u0026\u0026 git submodule update` in the root directory.\n\nIf these are not initialised, the inventory-storage module will fail to build correctly, and other operations may also fail.\n\nMore information is available on the [developer site](https://dev.folio.org/guides/developer-setup/#update-git-submodules).\n\n### PostgreSQL\n\nRun the `setup-test-db.sh` script in the inventory-storage directory to setup Postgres with a database to be used in tests.\nThis is only required to run tests against an external Postgres instance, the default is to use an embedded Postgres instance.\n\n### Kafka\n\nMod-circulation-storage implements domain event pattern and requires kafka to be listening to\n`localhost:9092`. You can override kafka port and host by setting `KAFKA_PORT` and\n`KAFKA_HOST` environment variables.\n\nFor production deployments it is also required to set `REPLICATION_FACTOR`\nenv variable, this property has following description:\n\u003e The replication factor controls how many servers will replicate each message that is written.\n\u003e If replication factor set to 3 then up to 2 servers can fail before access to the data will be lost.\n\nThe default configuration for this property is `1`, for production environments\nit is usually `3`.\n\nThere is another important property - `number of partitions` for a topic - it has following\ndescription:\n\u003e The partition count controls how many logs the topic will be sharded into.\n\nThis property has fixed value - `10`.\n\n## Common activities\n\n### Checking the RAML and JSON.Schema definitions\n\nFollow the [guide](https://dev.folio.org/guides/raml-cop/) to use raml-cop to assess RAML, schema, and examples.\n\n# Local Deployment using Docker\n\n## Preparation\n\nExecute `mvn clean package` to build the jar artefact needed for building a Docker image\n\n## Start the infrastructure\n\nClone `https://github.com/folio-org/folio-tools/`\n\nNavigate to the folder `infrastructure/local` directory within the cloned repository\n\nExecute `docker compose up -d` to start the infrastructure containers needed to run the module\n\n## Start the Module\n\nIn the root of this repository, execute `docker compose up -d` to deploy the module\n\n## Stop the Module\n\nIn the root of this repository, execute `docker compose down` to undeploy the module\n\n## Stop the infrastructure\n\nNavigate to the folder `infrastructure/local` directory within the clone of the folio-tools repository\n\nExecute `docker compose down` to stop the infrastructure containers\n\n## Frequently Asked Questions\n\n### Dates and times\n\nA loan has the date and time when and item was leant to a user and when it was returned. The module expects them to be represented in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-3).\n\nAt the moment, the JSON.schema for loan does provide validation for these (it will eventually use the date-time format included in the standard when this is supported by the RAML module builder).\n\n## Additional Information\n\nOther [modules](https://dev.folio.org/source-code/#server-side).\n\nOther FOLIO Developer documentation is at [dev.folio.org](https://dev.folio.org/)\n\n### Issue tracker\n\nSee project [CIRCSTORE](https://issues.folio.org/browse/CIRCSTORE)\nat the [FOLIO issue tracker](https://dev.folio.org/guidelines/issue-tracker/).\n\n### ModuleDescriptor\n\nSee the built `target/ModuleDescriptor.json` for the interfaces that this module\nrequires and provides, the permissions, and the additional module metadata.\n\n### API documentation\n\nThis module's [API documentation](https://dev.folio.org/reference/api/#mod-circulation-storage).\n\n### Code analysis\n\n[SonarQube analysis](https://sonarcloud.io/dashboard?id=org.folio%3Amod-circulation-storage).\n\n### Download and configuration\n\nThe built artifacts for this module are available.\nSee [configuration](https://dev.folio.org/download/artifacts) for repository access,\nand the [Docker image](https://hub.docker.com/r/folioorg/mod-circulation-storage/).\n\n## Design notes\n\n### Request status transition\n\nThere is database trigger for request update. It works in the following way:\n\nGiven a request with status 'Open - Awaiting pickup'\u003cbr/\u003e\nWhen the request is updated and new status is 'Closed - Pickup expired' or 'Closed - Cancelled'\u003cbr/\u003e\nThen an update trigger adds the property 'awaitingPickupRequestClosedDate' to the request JSONB\u003cbr/\u003e\n\nSuch behavior is required by Expired Holds Report CSV functionality in mod-circulation\u003cbr/\u003e\nSee [CIRC-320](https://issues.folio.org/browse/CIRC-320)\n\n### Requests Batch API\n\nIn order to go through `itemId`-`position` constraint for **request** table we're removing all positions\nfor requests as the first operation of batch, before executing the updates.\n\nLet's say we have following requests in batch package:\n* *Request A*;\n* *Request B*;\n* *Request C*.\n\nThen, in order to execute them successfully and do not get constraint violation we're\nremoving positions for these request, so we will do:\n1. `UPDATE requests SET jsonb = jsonb - 'position' WHERE id IN (A, B, C)`;\n1. `UPDATE requests SET jsonb = A WHERE id = A`;\n1. `UPDATE requests SET jsonb = B WHERE id = B`;\n1. `UPDATE requests SET jsonb = C WHERE id = C`;\n\n# Domain event pattern\n\nThe pattern means that every time when a domain entity is created/updated/removed\na message is posted to kafka topic. Currently domain events are supported for _loans_, _requests_, _check-in_ entities. \nThe events are posted into the following topics: \n* `circulation.loan` - for loans;\n* `circulation.request` - for requests;\n* `circulation.check-in` - for check-in records.\n\nThe event payload has following structure:\n```javascript\n{\n  \"id\": \"12bb13f6-d0fa-41b5-b0ad-d6561975121b\",  // event UUID\n  \"type\": \"CREATED|UPDATED|DELETED|ALL_DELETED\", // type of the event\n  \"tenant\": \"diku\",                              // tenant name\n  \"timestamp\": 1642707670092,                    // event creation timestamp in Unix Epoch time \n  \"data\": {\n    \"old\": {...},                                // the entity before update or delete\n    \"new\": {...},                                // the entity after update or create  \n  }\n}\n```\n\n`X-Okapi-Url` and `X-Okapi-Tenant` headers are set from the request to the kafka message.\n\nKafka partition key for all the events is **entity id**.\n\n## Domain events for delete all APIs\n\nThere are delete all APIs for loan and request records. For such\nAPIs we're issuing a special domain event of `ALL_DELETED` type:\n* Partition key: `00000000-0000-0000-0000-000000000000`\n* Event payload:\n```javascript\n{\n  \"id\": \"12bb13f6-d0fa-41b5-b0ad-d6561975121b\",\n  \"type\": \"ALL_DELETED\",\n  \"tenant\": \"diku\",\n  \"timestamp\": 1642707670092\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffolio-org%2Fmod-circulation-storage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffolio-org%2Fmod-circulation-storage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffolio-org%2Fmod-circulation-storage/lists"}