An open API service indexing awesome lists of open source software.

https://github.com/sul-dlss/sdr-api

HTTP API for the Stanford Digital Repository
https://github.com/sul-dlss/sdr-api

application infrastructure rails rails-api

Last synced: 3 months ago
JSON representation

HTTP API for the Stanford Digital Repository

Awesome Lists containing this project

README

        

[![CircleCI](https://circleci.com/gh/sul-dlss/sdr-api.svg?style=svg)](https://circleci.com/gh/sul-dlss/sdr-api)
[![codecov](https://codecov.io/github/sul-dlss/sdr-api/graph/badge.svg?token=15B4ZMZJKK)](https://codecov.io/github/sul-dlss/sdr-api)
[![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)

# Stanford Digital Repository API (SDR-API)

An HTTP API for the SDR.

There 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).

## Functionality
### Deposit
This accepts a series of uploaded files and metadata (Cocina model) and begins the accessioning workflow.

### Register
Same as deposit, but doesn't begin the accessioning workflow

## Future enhancements
- Update an existing object. This depends on us having a complete mapping between Cocina descriptive metadata and MODS.
- Create derivative images for access

## Local Development / Usage

### Start dependencies

#### Database

```
docker compose up -d db
```

### Build the api container

```
docker compose build app
```

### Setup the local database

```
bin/rails db:create
bin/rails db:migrate
```

### Start the app

```
docker compose up -d app
```

### Authorization

Log in to get a token by calling:

```
curl -X POST -H 'Content-Type: application/json' \
-d '{"email":"[email protected]","password":"sekret!"}' \
https://{hostname}/v1/auth/login
```

In subsequent requests, submit the token in the `Authorization` header:

```
curl -H 'Accept: application/json' -H "Authorization: Bearer ${TOKEN}" https://{hostname}/api/myresource
```

### Sequence of operations

Given that we have a DRO with two Filesets each with a File (image1.png) and (image2.png)

1. Get base64 encoded md5 checksum of the files: `ruby -rdigest -e 'puts Digest::MD5.file("image1.png").base64digest'`
1. `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`
This will return a payload with a `signed_id` and `direct_upload` object has the URL to send the file to.
1. PUT the content to the URL given in the previous step. _See warning below about behavior to watch for when depositing JSON content._
1. Repeat step 1-2 for the second file.
1. 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.
1. POST /dro with the fileset ids from the previous step.

#### Gotcha to consider when PUTing JSON content for deposit

Despite 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.

## User management
### Create a user
```
bin/rake "users:create[[email protected]]"
Password:
# {
:id => 2,
:name => nil,
:email => "[email protected]",
:password_digest => "[DIGEST]",
:created_at => Fri, 18 Nov 2022 15:17:22.836184000 UTC +00:00,
:updated_at => Fri, 18 Nov 2022 15:17:22.836184000 UTC +00:00,
:active => true,
:full_access => true,
:collections => []
}
```

### Show a user
```
bin/rake "users:show[[email protected]]"
# {
:id => 1,
:name => nil,
:email => "[email protected]",
"password_digest" => "[DIGEST]",
:created_at => Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,
:updated_at => Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,
:active => true,
:full_access => true,
:collections => []
}
```

### Change whether active
```
bin/rake "users:active[[email protected],false]"
# {
"active" => false,
"id" => 1,
"name" => nil,
:email => "[email protected]",
"password_digest" => "[DIGEST]",
"created_at" => Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,
"updated_at" => Thu, 17 Nov 2022 19:10:27.385608000 UTC +00:00,
"full_access" => true,
"collections" => []
}
```

### Add authorized collections and remove full-access
```
bin/rake "users:collections[[email protected],'druid:bb408qn5061 druid:bb573tm8486']"
# {
"collections" => [
[0] "'druid:bb408qn5061",
[1] "druid:bb573tm8486'"
],
"full_access" => false,
"id" => 1,
"name" => nil,
:email => "[email protected]",
"password_digest" => "[DIGEST]",
"created_at" => Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,
"updated_at" => Thu, 17 Nov 2022 19:17:11.197967000 UTC +00:00,
"active" => false
}
```

### Remove authorized collections and make full-access
```
bin/rake "users:collections[[email protected],'']"
# {
"collections" => [],
"full_access" => true,
"id" => 1,
"name" => nil,
:email => "[email protected]",
"password_digest" => "[DIGEST]",
"created_at" => Thu, 17 Nov 2022 17:26:28.927725000 UTC +00:00,
"updated_at" => Thu, 17 Nov 2022 19:20:47.869335000 UTC +00:00,
"active" => false
}
```

## Docker

Note 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:

Build image:
```
docker image build -t suldlss/sdr-api:latest .
```

Publish:
```
docker push suldlss/sdr-api:latest
```

## Background processing
Background processing is performed by [Sidekiq](https://github.com/mperham/sidekiq).

Sidekiq can be monitored from [/queues](http://localhost:3000/queues).
For more information on configuring and deploying Sidekiq, see this [doc](https://github.com/sul-dlss/DevOpsDocs/blob/master/projects/sul-requests/background_jobs.md).

# Cron check-ins
Cron 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.

Cron check-ins are configured in the following locations:
1. `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.
2. `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.
3. `config/settings/production.yml` in shared_configs: This contains the actual check-in keys.
4. 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`).

## Reset Process (for QA/Stage)

### Steps

1. Dump the users table: `pg_dump --table public.users --data-only sdr > users.sql`
2. Reset the database: `bin/rails -e p db:reset`
3. Restore the users table: `psql -f users.sql sdr`
4. Delete file storage: `rm -fr storage/*`
5. To test, run the `sdr_deposit_spec.rb` integration test.