Skip to content

API v1 Documentation

gjacob24 edited this page Mar 27, 2024 · 4 revisions

DMPRoadmap API - Version 1

This version of the DMPRoadmap API complies with the RDA Common Standard Metadata schema v1.0. This schema is a recommendation for the transfer of DMP metadata between systems. The specification was created by the RDA Common Standard Working Group

Table of Contents

  1. Notes
  2. Quick Guide on How to Use API V1
  3. Base Url & Request Urls
  4. Heartbeat
  5. Authentication
  6. Authorization
  7. Pagination
  8. Retrieve a list of DMP templates
  9. Create a plan
  10. Retrieve a list of plans
  11. Retrieve a plan
  12. Internal DMPRoadmap errors
  13. Testing
  14. API Use Cases

Notes

  • DMPRoadmap does not currently support a separate and distinct Project. The Project title and DMP title are the same for now.
  • DMPRoadmap does not currently support multiple datasets. This means that all exported DMPs will contain a 'Generic Dataset' until we support multiple datasets.
  • DMPRoadmap does not yet support the ethical_issues entries.
  • DMPRoadmap will be moving towards full support of all elements of the RDA standard over the next several years.

Quick Guide on How to Use API V1

API V1 Quick Guide.pdf

Base Url & Request Urls

The base url <BASE_URL> is the url of the Roadmap tool login page, e.g.,

<BASE_URL> = http://localhost:3000/

<BASE_URL> = https://dmponline.dcc.ac.uk/

<BASE_URL> = https://dmptool.org/

The request url format for all api calls will be a combination of the <BASE_URL> and the api route with optional parameters,

Request URL Format: <BASE_URL>/<API_ROUTE>?

e.g.,

http://localhost:3000/api/v1/templates

Heartbeat

The API provides a heartbeat endpoint that can be pinged to determine if it is online. The caller does not need to be authenticated.

GET /api/v1/heartbeat:

Format:

<BASE_URL>/api/v1/heartbeat

Example using curl:

curl -v GET https://localhost:3000/api/v1/heartbeat 

Response:

The API will respond with an HTTP status of 200 and the following JSON output:

{
  "application": "DMPRoadmap",
  "source": "GET /api/v1/heartbeat",
  "time": "2020-02-13T19:47:04-07:00",
  "caller": "::1",
  "code": 200,
  "message": "OK",
  "total_items": 0,
  "items": []
}

Authentication

This version of the API uses JSON Web Token technology to manage a session. It supports access by Users and ApiClients.

Organizational Admins with the "Use API" privilege can access the API via their email address and the API token found on their Profile page when logged in. Client applications can access the API via their client_id and client_secret. ApiClients can be managed by Super Admins via the Admin Features -> Api Clients menu.

POST api/v1/authenticate

This endpoint authenticates the User or ApiClient and returns a JSON Web Token that should be used for all further communication with the API.

Format:

<BASE_URL>/api/v1/authenticate

Example ApiClient login:

curl -v -X POST https://localhost:3000/api/v1/authenticate \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "{\"grant_type\":\"client_credentials\",\"client_id\":\"[api_client client_id]\",\"client_secret\":\"[api_client client_secret]\"}"

Example Organizational Admin login:

curl -v -X POST https://localhost:3000/api/v1/authenticate \
-H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" \
-H "Accept: application/json" \
-d "{\"grant_type\":\"authorization_code\",\"email\":\"[user email]\",\"code\":\"[user api_token]\"}"

Response

DMPRoadmap will then either return a HTTP 401 Unauthorized if the authentication failed (see JSON Body below) or an HTTP 200 Ok if the request succeeds along with a JSON Web Token:

{
  "access_token": "eyJhbGciOiJIUzUxMiIsImtpZCI6IkM4MVV6dG9jaHFGOEhMTWxHbHZRUHZCWnJySmx3UTNfOW1PQkROWUMwUGMifQ.eyJpc3MiOiJEbXBodWI6OkFwcGxpY2F0aW9uIiwiaWF0IjoxNTY3NTM3MDg0LCJqdGkiOiI2YzEyNTVjMC1iOWU4LTRiODgtOGZjZC1kYjlhODJiOWFiMjYiLCJjbGllbnQiOnsiaWQiOiJDODFVenRvY2hxRjhITE1sR2x2UVB2Qlpyckpsd1EzXzltT0JETllDMFBjIiwidG9rZW5fc2VjcmV0IjoiNzZhNzVkMDMtMTVmYy00MDZjLWFhMjMtZmM0N2RkYmY3MDUxIn19.f7w_RV62VY4o058-vTK1mvkO-oVnzOnvydCgH9022U9KxspKmmXN2z-4wIauRKIc8nU74wpW3AccUYE0BqeNvQ",
  "token_type":"Bearer",
  "expires_in":7200,
  "created_at":1567537084
}

Authorization

The User/ApiClient can begin accessing the other API endpoints once they have been authenticated. They must pass along the JSON Web Token (access_token in the JSON response above) that they received from the authentication step to verify their identity in subsequent calls to the API.

The token should be sent in the HTTP Headers of each request:

{
  "Authorization": "Bearer [access_token]"
}

If any API request is missing the authorization token or the token has expired then an HTTP 401 Unauthorized will be returned along with the following JSON body:

{ 
  "application": "DMPRoadmap",
  "source": "POST /api/v1/plans/",
  "time": "2020-02-07T14:04:01-07:00",
  "caller": "User/ApiClient name",
  "code": 401, 
  "message": "UNAUTHORIZED", 
  "total_items": 0, 
  "items": [],
  "errors": ["Missing token || Invalid token"],
}

Pagination

By default all endpoints that return multiple items are paginated. You can specify the default and maximum allowable per_page in your config/initializers/dmproadmap.rb file.

The User/ApiClient can specify the page=[n] and per_page=[n] values in the query string when they call the endpoint.

Example:

curl -k -v -H "Authorization: Bearer <YOUR AUTHENTICATION TOKEN>" \
-H "Content-Type: application/json" \
http://localhost:3000/api/v1/plans\?created_after\=2017-12-31\&per_page\=50\&page\=1 

The JSON response will contain useful information for the caller who can then use them to step through the pages to retrieve the full set of information:

{
  "application": "DMPRoadmap",
  "source": "GET /api/v1/templates",
  "time": "2020-02-07T14:04:01-07:00",
  "caller": "User/ApiClient name",
  "code": 200,
  "message": "OK",
  "page": 2,
  "per_page": 20,
  "total_items": 58,
  "prev": "/api/v1/templates?page=1&per_page=20",
  "next": "/api/v1/templates?page=3&per_page=20",
  "items": [
    { "dmp_template": "<< See above for an example of the templates output >>" },
    { "dmp_template": "<< See above for an example of the templates output >>" }
  ]
}

Retrieve a list of DMP Templates

An authenticated request can be made to retrieve a list of templates. The templates it returns are either publicly visible funder templates or organizational templates that have been created for your organization. If your organization has customized a funder template, the customized version is returned.

GET /api/v1/templates

Format:

<BASE_URL>/api/v1/templates

Example using curl:

curl -v -H "Accept: application/json" -H "Authorization: Bearer [access_token]" http://localhost:3000/api/v1/templates

This endpoint accepts a page=[n] and per_page=[n] query parameter. See more about pagination above.

Response:

{
  "application": "DMPRoadmap",
  "source": "GET /api/v1/templates",
  "time": "2020-05-13T22:23:29-07:00",
  "caller": "your User/ApiClient name",
  "code": 200,
  "message": "OK",
  "page": 1,
  "per_page": 20,
  "total_items": 38,
  "next": "/api/v1/templates?page=2&per_page=20",
  "items": [
    {
      "dmp_template": {
        "title": "EF - Biochemistry",
        "description": "A biochemistry template for the Example Funder",
        "version": 3,
        "created": "2018-02-13T07:18:08Z",
        "modified": "2018-04-18T18:18:36Z",
        "affiliation": {
          "name": "Example Funder",
          "abbreviation": "EF",
          "affiliation_id": {
            "type": "ror",
            "identifier": "https://ror.org/000aaa0000"
          }
        },
        "template_id": {
          "type": "dmproadmap",
          "identifier": "437"
        }
      }
    }
  ]
}

Create a Plan

If the User is an Admin they can retrieve any plan for their Organization. If an ApiClient, then the plan must have been created by that client (this may change as we define further use cases).

When a plan is created via the API, the individual designated as the 'contact' becomes the owner of the plan. If the email address is already recognized by the system it will simply connect the plan to that user's account. If the email address is not recognized the system will send an invitation to the user and connect them to the new plan.

The default DCC template will be used if you do not specify a template in the extensions block.

POST /api/v1/plans

Format:

<BASE_URL>/api/v1/plans

Example using curl:

curl -v -X POST https://localhost:3000/api/v1/plans \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Server-Agent: Your Application Name" \
  -H "Authorization: Bearer [Your Authorization Token]"

This endpoint expects you to pass in a RDA Common Standard representation of a DMP as JSON:

{
  "total_items": 1,
  "items": [{
    "dmp": {
      "title": "Examination of some interesting topics in biochemistry",
      "contact": {
        "name": "Jane Doe",
        "mbox": "[email protected]",
        "affiliation": {
          "name": "Example University"
        },
        "contact_id": {
          "type": "orcid",
          "identifier": "0000-0000-0000-0000"
        }
      },
      "contributor": [
        {
          "name": "John Smith",
          "mbox": "[email protected]",
          "role": [
            "https://dictionary.casrai.org/Contributor_Roles/Data_curation",
            "https://dictionary.casrai.org/Contributor_Roles/Investigation"
          ],
          "affiliation": {
            "name": "University of Nowhere",
            "affiliation_id": {
              "type": "ror",
              "identifier": "https://ror.org/123abc45y"
            }
          },
          "contributor_id": {
            "type": "orcid",
            "identifier": "https://orcid.org/0000-0000-0000-0000"
          }
        }
      ],
      "extension": [
        {
          "dmproadmap": {
            "template": {
              "id": 3
            }
          }
        }
      ]
    }
  }]
}

The minimum amount of information required by DMPRoadmap is a DMP title and a contact email:

{
  "total_items": 1,
  "items": [{
    "dmp": {
      "title": "Examination of some interesting topics in biochemistry",
      "contact": {
        "mbox": "[email protected]"
      }
    }
  }]
}

Response

If a new DMP was successfully created an HTTP 201 Created is returned:

{ 
  "application": "DMPRoadmap",
  "source": "POST /api/v1/plans/",
  "time": "2020-02-07T14:04:01-07:00",
  "caller": "User/ApiClient name",
  "code": 201, 
  "message": "CREATED", 
  "total_items": 0, 
  "items": [{
    "dmp": "<< See an example of the full DMP JSON below in the 'Retrieve a Plan' section >>"
  }],
  "errors": [],
}

If the request’s JSON was not properly formatted or does not contain the minimum amount of information to create a new DMP then an HTTP 400 Bad Request will be returned along with the following JSON body:

{ 
  "application": "DMPRoadmap",
  "source": "POST /api/v1/plans/",
  "time": "2020-02-07T14:04:01-07:00",
  "caller": "User/ApiClient name",
  "code": 400,
  "message": "BAD REQUEST", 
  "total_items": 0, 
  "items": [],
  "errors": ["dmp title cannot be blank", "invalid JSON format", "contact email cannot be blank", "etc."],
}

Retrieve a List of Plans

An OrgAdmin can retrieve a list of plans for their organization and any plans that are publicly visible. An ApiClient can retrieve a list of plans created that they have created and any plans that are publicly visible.

The list contains an abbreviated version of the RDA common standard schema. Subsequent requests for a specific plan can be made to retrieve the full metadata for the plan.

GET /api/v1/plans

Format:

<BASE_URL>/api/v1/plans

Example using curl:

curl -v -H "Accept: application/json" -H "Authorization: Bearer [access token]" https://localhost:3000/api/v1/plans

Response:

If the request was successful an HTTP 200 is returned along with the following JSON content (only displaying the 1st of 20 results):

{
  "application": "DMPRoadmap",
  "source": "GET /api/v1/plans",
  "time": "2020-06-11T15:32:04-07:00",
  "caller": "User/ApiClient name",
  "code": 200,
  "message": "OK",
  "page": 1,
  "per_page": 20,
  "total_items": 248,
  "next": "/api/v1/plans?page=2&per_page=20",
  "items": [
    {
      "dmp": {
        "schema": "https://github.com/RDA-DMP-Common/RDA-DMP-Common-Standard/tree/master/examples/JSON/JSON-schema/1.0",
        "title": "USGS CDR/ECV DMP",
        "language": "eng",
        "created": "2013-01-07T19:06:11Z",
        "modified": "2020-04-27T22:38:23Z",
        "ethical_issues_exist": "unknown",
        "dmp_id": {
          "type": "url",
          "identifier": "http://localhost:3000/api/v1/plans/5119"
        },
        "contact": {
          "name": "Jane Doe",
          "mbox": "[email protected]",
          "affiliation": {
            "name": "Example University",
            "abbreviation": "EU",
            "affiliation_id": {
              "type": "ror",
              "identifier": "https://ror.org/124abc0000a"
            }
          }
        }
      }
    }
  ]
}

Retrieve a Plan

An OrgAdmin can retrieve any plan for their organization. An ApiClient can retrieve any plan created by that client. A User/ApiClient can also retrieve any plan whose visibility is set to 'publicly_visible'.

GET /api/v1/plans/[:id] (Retrieve a DMP)

Format:

<BASE_URL>/api/v1/plans/[:id]

Example using curl:

curl -v -H "Accept: application/json" -H "Authorization: Bearer [access_token]" http://localhost:3000/api/v1/plans/123

Response:

If the request was successful an HTTP 200 is returned along with the following JSON content:

{
  "application": "DMPRoadmap",
  "source": "GET /api/v1/plans/123",
  "time": "2020-02-07 14:04:01 UTC",
  "caller": "User/ApiClient name",
  "code": 200,
  "message": "OK",
  "total_items": 1,
  "items": [{
    "dmp": {
      "schema": "https://github.com/RDA-DMP-Common/RDA-DMP-Common-Standard/tree/master/examples/JSON/JSSON-schema/1.0",
      "title": "Examination of some interesting topics in biochemistry",
      "language": "eng",
      "created": "2019-08-19T15:45:07Z",
      "modified": "2020-04-24T22:28:22Z",
      "ethical_issues_exist": "unknown",
      "dmp_id": {
        "type": "url",
        "identifier": "http://localhost:3000/api/v1/plans/123"
      },
      "contact": {
        "name": "Jane Doe",
        "mbox": "[email protected]",
        "affiliation": {
          "name": "Example University",
          "abbreviation": "EU",
          "affiliation_id": {
            "type": "ror",
            "identifier": "https://ror.org/124abc0000a"
          }
        },
        "contact_id": {
          "type": "orcid",
          "identifier": "0000-0000-0000-0000"
        }
      },
      "contributor": [
        {
          "name": "John Smith",
          "mbox": "[email protected]",
          "role": [
            "https://dictionary.casrai.org/Contributor_Roles/Data_curation",
            "https://dictionary.casrai.org/Contributor_Roles/Investigation"
          ],
          "affiliation": {
            "name": "University of Nowhere",
            "abbreviation": "UofN",
            "affiliation_id": {
              "type": "ror",
              "identifier": "https://ror.org/123abc45y"
            }
          },
          "contributor_id": {
            "type": "orcid",
            "identifier": "https://orcid.org/0000-0000-0000-0000"
          }
        }
      ],
      "project": [
        {
          "title": "Examination of some interesting topics in biochemistry"
        }
      ],
      "dataset": [
        {
          "title": "DMP Dataset",
          "personal_data": "unknown",
          "sensitive_data": "unknown",
          "dataset_id": {
            "type": "url",
            "identifier": "http://localhost:3000/api/v1/plans/123"
          },
          "distribution": [
            {
              "title": "PDF - Testing our maDMP JSON export",
              "data_access": "open",
              "download_url": "http://localhost:3000/plans/123/export.pdf",
              "format": ["application/pdf"]
            }
          ]
        }
      ],
      "extension": [
        {
          "dmproadmap": {
            "template": {
              "id": 437,
              "title": "EF - Biochemistry"
            }
          }
        }
      ]
    }
  }]
}

In situations where the plan does not exist or the caller does not have permission to access the plan, an HTTP 404 is returned:

{ 
  "application": "DMPRoadmap",
  "source": "GET /api/v1/plans/123",
  "time": "2020-02-07T14:04:01-07:00",
  "caller": "User/ApiClient name",
  "code": 404,
  "message": "NOT FOUND", 
  "total_items": 0, 
  "items": [],
  "errors": ["dmp could not be found"],
}

Internal DMPRoadmap errors

In the event that a fatal error is thrown from any of the API endpoints an HTTP 500 Internal Server Error will be returned along with the following JSON body:

{  
  "application": "DMPRoadmap",
  "source": "POST /api/v1/plans/",
  "time": "2020-02-07T14:04:01-07:00",
  "caller": "User/ApiClient name",
  "code": 500,
  "message": "INTERNAL SERVER ERROR", 
  "total_items": 0, 
  "items": [],
  "errors": [], 
}

You will need to check your logs to determine the root cause of the fatal error.

Testing

The following Ruby code is provided as an example of how one can programmatically authenticate and then access the API. Feel free to use it as a reference.

    DMPROADMAP_URL = 'https://my.site.org/api/v1'.freeze
    CLIENT_ID = 12345.freeze
    CLIENT_SECRET = '1234567890abcdefg'.freeze
    DEFAULT_HEADERS = headers = {
      'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
      'Accept': 'application/json',
      'Server-Agent': 'My external system'
    }.freeze

    def retrieve_auth_token
      payload = {
        grant_type: 'client_credentials',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET
      }
      target = "#{DMPROADMAP_URL}/authenticate"
      resp = HTTParty.post(target, body: payload, headers: DEFAULT_HEADERS)
      response = JSON.parse(resp.body)
      return nil unless resp.code == 200 
      
      token = response
      {
        'Authorization': "#{token['token_type']} #{token['access_token']}"
      }
    end

    def retrieve_templates(token:, page: 1)
      payload = {
        grant_type: 'client_credentials',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET
      }
      target = "#{DMPROADMAP_URL}/templates?page=#{page}"
      resp = HTTParty.get(target, headers: DEFAULT_HEADERS.merge(token))
      resp.code == 200 ? JSON.parse(resp.body) : nil
    end

    # Authenticate
    token = retrieve_auth_token
    puts token.inspect

    # Retrieve the public templates
    templates = retrieve_templates(token: token)
    puts templates.inspect

    # Retrieve page 2 of the public templates
    templates retrieve_templates(token: token, page: 2).inspect

    # You can then add further tasks

API Use Cases

Follow the link to view use cases: https://github.com/DMPRoadmap/roadmap/wiki/API-use-cases

Clone this wiki locally