sul-dlss/sdr-api

View on GitHub
README.md

Summary

Maintainability
Test Coverage
[![CircleCI](https://circleci.com/gh/sul-dlss/sdr-api.svg?style=svg)](https://circleci.com/gh/sul-dlss/sdr-api)
[![Maintainability](https://api.codeclimate.com/v1/badges/6e11d54474bfaf70480b/maintainability)](https://codeclimate.com/github/sul-dlss/sdr-api/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/6e11d54474bfaf70480b/test_coverage)](https://codeclimate.com/github/sul-dlss/sdr-api/test_coverage)
[![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":"jcoyne@justincoyne.com","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[leland@stanford.edu]"
Password:
#<User:0x00007f8647ae4988> {
                 :id => 2,
               :name => nil,
              :email => "leland@stanford.edu",
    :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[leland@stanford.edu]"
#<User:0x0000000113fbb6e8> {
                 :id => 1,
               :name => nil,
              :email => "leland@stanford.edu",
   "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[leland@stanford.edu,false]"
#<User:0x00000001107805a8> {
             "active" => false,
                 "id" => 1,
               "name" => nil,
              :email => "leland@stanford.edu",
    "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[leland@stanford.edu,'druid:bb408qn5061 druid:bb573tm8486']"
#<User:0x0000000107d0d240> {
        "collections" => [
        [0] "'druid:bb408qn5061",
        [1] "druid:bb573tm8486'"
    ],
        "full_access" => false,
                 "id" => 1,
               "name" => nil,
              :email => "leland@stanford.edu",
    "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[leland@stanford.edu,'']"
#<User:0x0000000110b8f090> {
        "collections" => [],
        "full_access" => true,
                 "id" => 1,
               "name" => nil,
              :email => "leland@stanford.edu",
    "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.