{"id":24502765,"url":"https://github.com/sul-dlss/sdr-api","last_synced_at":"2026-05-20T09:31:20.743Z","repository":{"id":37079865,"uuid":"221730348","full_name":"sul-dlss/sdr-api","owner":"sul-dlss","description":"HTTP API for the Stanford Digital Repository","archived":false,"fork":false,"pushed_at":"2026-05-18T16:02:51.000Z","size":1294,"stargazers_count":3,"open_issues_count":6,"forks_count":0,"subscribers_count":10,"default_branch":"main","last_synced_at":"2026-05-18T16:38:41.032Z","etag":null,"topics":["application","infrastructure","rails","rails-api"],"latest_commit_sha":null,"homepage":"https://sul-dlss.github.io/sdr-api/","language":"Ruby","has_issues":true,"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/sul-dlss.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-11-14T15:34:43.000Z","updated_at":"2026-05-18T15:49:10.000Z","dependencies_parsed_at":"2023-11-10T05:35:08.000Z","dependency_job_id":"3635dcea-c949-45fa-8f5e-a15bf3a9d4f0","html_url":"https://github.com/sul-dlss/sdr-api","commit_stats":null,"previous_names":[],"tags_count":262,"template":false,"template_full_name":null,"purl":"pkg:github/sul-dlss/sdr-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sul-dlss%2Fsdr-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sul-dlss%2Fsdr-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sul-dlss%2Fsdr-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sul-dlss%2Fsdr-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sul-dlss","download_url":"https://codeload.github.com/sul-dlss/sdr-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sul-dlss%2Fsdr-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33253749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-20T04:48:54.280Z","status":"ssl_error","status_checked_at":"2026-05-20T04:48:10.851Z","response_time":356,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["application","infrastructure","rails","rails-api"],"created_at":"2025-01-21T23:13:51.869Z","updated_at":"2026-05-20T09:31:20.710Z","avatar_url":"https://github.com/sul-dlss.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CircleCI](https://circleci.com/gh/sul-dlss/sdr-api.svg?style=svg)](https://circleci.com/gh/sul-dlss/sdr-api)\n[![codecov](https://codecov.io/github/sul-dlss/sdr-api/graph/badge.svg?token=15B4ZMZJKK)](https://codecov.io/github/sul-dlss/sdr-api)\n[![OpenAPI Validator](http://validator.swagger.io/validator?url=https://raw.githubusercontent.com/sul-dlss/sdr-api/main/openapi.yml)](http://validator.swagger.io/validator/debug?url=https://raw.githubusercontent.com/sul-dlss/sdr-api/main/openapi.yml)\n\n# Stanford Digital Repository API (SDR-API)\n\nAn HTTP API for the SDR.\n\nThere is a [OAS 3.0 spec](http://spec.openapis.org/oas/v3.0.2) that documents the API in [openapi.yml].  If you clone this repo, you can view this by opening [docs/index.html](docs/index.html).\n\n## Functionality\n### Deposit\nThis accepts a series of uploaded files and metadata (Cocina model) and begins the accessioning workflow.\n\n### Register\nSame as deposit, but doesn't begin the accessioning workflow\n\n## Future enhancements\n- Update an existing object. This depends on us having a complete mapping between Cocina descriptive metadata and MODS.\n- Create derivative images for access\n\n## Local Development / Usage\n\n### Start dependencies\n\n#### Database\n\n```\ndocker compose up -d db\n```\n\n### Build the api container\n\n```\ndocker compose build app\n```\n\n### Setup the local database\n\n```\nbin/rails db:create\nbin/rails db:migrate\n```\n\n### Start the app\n\n```\ndocker compose up -d app\n```\n\n### Authorization\n\nLog in to get a token by calling:\n\n```\ncurl -X POST -H 'Content-Type: application/json' \\\n  -d '{\"email\":\"jcoyne@justincoyne.com\",\"password\":\"sekret!\"}' \\\n  https://{hostname}/v1/auth/login\n```\n\nIn subsequent requests, submit the token in the `Authorization` header:\n\n\n```\ncurl -H 'Accept: application/json' -H \"Authorization: Bearer ${TOKEN}\" https://{hostname}/api/myresource\n```\n\n\n### Sequence of operations\n\nGiven that we have a DRO with two Filesets each with a File (image1.png) and (image2.png)\n\n1. Get base64 encoded md5 checksum of the files: `ruby -rdigest -e 'puts Digest::MD5.file(\"image1.png\").base64digest'`\n1. `curl -X POST -H 'Content-Type: application/json' -d '{\"blob\":{\"byte_size\":185464, \"checksum\":\"Yw6eokcdYaqMAYioup0l7g==\",\"content_type\":\"image/png\",\"filename\":\"image.png\"}}' http://localhost:3000/rails/active_storage/direct_uploads`\n  This will return a payload with a `signed_id` and `direct_upload` object has the URL to send the file to.\n1. PUT the content to the URL given in the previous step. _See warning below about behavior to watch for when depositing JSON content._\n1. Repeat step 1-2 for the second file.\n1. POST /filesets with the `signed_id` from step one.  Repeat for the second file. The API will use `ActiveStorage::Blob.find_signed(params[:signed_id])` to find the files.\n1. POST /dro with the fileset ids from the previous step.\n\n#### Gotcha to consider when PUTing JSON content for deposit\n\nDespite our intent to accept user deposited content as given without further validation, some Rails and/or Committee magic is parsing content deposited as `application/json` in step 3 of the above sequence of operations. If the uploaded content does not parse as JSON, but does specify a content type of `application/json`, it will be rejected with a 400 error. You can work around this by using the custom `application/x-stanford-json` content type, which will be translated back to `application/json` before the Cocina is saved. See `/v1/disk/{id}` description in [`openapi.yml`](openapi.yml), and/or https://github.com/sul-dlss/happy-heron/issues/3075 for more context.\n\n## User management\n### Create a user\n```\nbin/rake \"users:create[leland@stanford.edu]\"\nPassword:\n#\u003cUser:0x00007f8647ae4988\u003e {\n                 :id =\u003e 2,\n               :name =\u003e nil,\n              :email =\u003e \"leland@stanford.edu\",\n    :password_digest =\u003e \"[DIGEST]\",\n         :created_at =\u003e Fri, 18 Nov 2022 15:17:22.836184000 UTC +00:00,\n         :updated_at =\u003e Fri, 18 Nov 2022 15:17:22.836184000 UTC +00:00,\n             :active =\u003e true,\n        :full_access =\u003e true,\n        :collections =\u003e []\n}\n```\n\n### Show a user\n```\nbin/rake \"users:show[leland@stanford.edu]\"\n#\u003cUser:0x0000000113fbb6e8\u003e {\n                 :id =\u003e 1,\n               :name =\u003e nil,\n              :email =\u003e \"leland@stanford.edu\",\n   \"password_digest\" =\u003e \"[DIGEST]\",\n         :created_at =\u003e Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,\n         :updated_at =\u003e Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,\n             :active =\u003e true,\n        :full_access =\u003e true,\n        :collections =\u003e []\n}\n```\n\n### Change whether active\n```\nbin/rake \"users:active[leland@stanford.edu,false]\"\n#\u003cUser:0x00000001107805a8\u003e {\n             \"active\" =\u003e false,\n                 \"id\" =\u003e 1,\n               \"name\" =\u003e nil,\n              :email =\u003e \"leland@stanford.edu\",\n    \"password_digest\" =\u003e \"[DIGEST]\",\n         \"created_at\" =\u003e Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,\n         \"updated_at\" =\u003e Thu, 17 Nov 2022 19:10:27.385608000 UTC +00:00,\n        \"full_access\" =\u003e true,\n        \"collections\" =\u003e []\n}\n```\n\n### Add authorized collections and remove full-access\n```\nbin/rake \"users:collections[leland@stanford.edu,'druid:bb408qn5061 druid:bb573tm8486']\"\n#\u003cUser:0x0000000107d0d240\u003e {\n        \"collections\" =\u003e [\n        [0] \"'druid:bb408qn5061\",\n        [1] \"druid:bb573tm8486'\"\n    ],\n        \"full_access\" =\u003e false,\n                 \"id\" =\u003e 1,\n               \"name\" =\u003e nil,\n              :email =\u003e \"leland@stanford.edu\",\n    \"password_digest\" =\u003e \"[DIGEST]\",\n         \"created_at\" =\u003e Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,\n         \"updated_at\" =\u003e Thu, 17 Nov 2022 19:17:11.197967000 UTC +00:00,\n             \"active\" =\u003e false\n}\n```\n\n### Remove authorized collections and make full-access\n```\n bin/rake \"users:collections[leland@stanford.edu,'']\"\n#\u003cUser:0x0000000110b8f090\u003e {\n        \"collections\" =\u003e [],\n        \"full_access\" =\u003e true,\n                 \"id\" =\u003e 1,\n               \"name\" =\u003e nil,\n              :email =\u003e \"leland@stanford.edu\",\n    \"password_digest\" =\u003e \"[DIGEST]\",\n         \"created_at\" =\u003e Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,\n         \"updated_at\" =\u003e Thu, 17 Nov 2022 19:20:47.869335000 UTC +00:00,\n             \"active\" =\u003e false\n}\n```\n\n\n## Docker\n\nNote that this project's continuous integration build will automatically create and publish an updated image whenever there is a passing build from the `main` branch. If you do need to manually create and publish an image, do the following:\n\nBuild image:\n```\ndocker image build -t suldlss/sdr-api:latest .\n```\n\nPublish:\n```\ndocker push suldlss/sdr-api:latest\n```\n\n## Background processing\nBackground processing is performed by [Sidekiq](https://github.com/mperham/sidekiq).\n\nSidekiq can be monitored from [/queues](http://localhost:3000/queues).\nFor more information on configuring and deploying Sidekiq, see this [doc](https://github.com/sul-dlss/DevOpsDocs/blob/master/projects/sul-requests/background_jobs.md).\n\n# Cron check-ins\nCron jobs (configured via the whenever gem) are integrated with Honeybadger check-ins. These cron jobs will check-in with HB (via a curl request to an HB endpoint) whenever run. If a cron job does not check-in as expected, HB will alert.\n\nCron check-ins are configured in the following locations:\n1. `config/schedule.rb`: This specifies which cron jobs check-in and what setting keys to use for the checkin key. See this file for more details.\n2. `config/settings.yml`: Stubs out a check-in key for each cron job. Since we may not want to have a check-in for all environments, this stub key will be used and produce a null check-in.\n3. `config/settings/production.yml` in shared_configs: This contains the actual check-in keys.\n4. HB notification page: Check-ins are configured per project in HB. To configure a check-in, the cron schedule will be needed, which can be found with `bundle exec whenever`. After a check-in is created, the check-in key will be available. (If the URL is `https://api.honeybadger.io/v1/check_in/rkIdpB` then the check-in key will be `rkIdp`).\n\n## Reset Process (for QA/Stage)\n\n### Steps\n\n1. Dump the users table: `pg_dump --table public.users --data-only sdr \u003e users.sql`\n2. Reset the database: `bin/rails -e p db:reset`\n3. Restore the users table: `psql -f users.sql sdr`\n4. Delete file storage: `rm -fr storage/*`\n5. To test, run the `sdr_deposit_spec.rb` integration test.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsul-dlss%2Fsdr-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsul-dlss%2Fsdr-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsul-dlss%2Fsdr-api/lists"}