Skip to content

API V1 Documentation

Brian Riley edited this page Apr 18, 2021 · 11 revisions

DMPTool API - Version 1

This version of the DMPTool 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

This is documentation is for the latest version of the DMPTool API (version 1). If you are interested in the original version of the API (version 0) that is only accessible to Organizational Administrators and primarily provides usage statistics, then please see the API overview documentation.

Table of Contents

  1. Notes
  2. Requesting API Access
  3. Heartbeat
  4. Authentication
  5. Authorization
  6. Retrieve a list of DMP templates
  7. Create a plan
  8. Retrieve a list of plans
  9. Retrieve a plan
  10. Testing

Notes

  • The dmp_id is currently returned as either a URL or DMP ID depending on whether or not the corresponding template supports the registration of DMP IDs.
  • DMPTool does not currently support a separate and distinct Project. The Project title and DMP title are the same for now.
  • Not all templates within the DMPTool currently support multiple datasets. This means that some exported DMPs will contain a 'Generic Dataset'.
  • DMPTool does not yet support the ethical_issues entries.
  • The new extensions section in the dmp element contains the locations of any JSON schemas. It current contains a placeholder for the forthcoming schema specific to DMPRoadmap based systems.
  • The dmproadmap-[variable]-[name] entries are defined within the forthcoming dmproadmap schema

Requesting API Access

If you are an Organizational Administrator then you may use your existing API credentials found on the Edit Profile page when logged into the system. If you would like to build an integration between your application and the DMPTool then you will need to contact us at [email protected]. Please note that you would like API credentials for the DMPTool in the subject line. Once approved you will be sent your API credentials.

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:

Example using curl:

curl -v GET https://dmptool.org/api/v1/heartbeat 

Response:

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

{
  "application": "dmptool",
  "source": "GET /api/v1/heartbeat",
  "time": "2020-09-04T09:39:13-07:00",
  "caller": "73.35.243.89",
  "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 for both Administrative Users and API Client applications.

Organizational Admins 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. API Clients 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.

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 User login:

curl -v -X POST https://dmptool.org/api/v1/authenticate \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "{\"grant_type\":\"authorization_code\",\"email\":\"[user email]\",\"code\":\"[user api_token]\"}"

Response

The DMPTool 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": "eyJhbGciOiJIUzI1NiJ9.eyJjbGllbhrf6GHabfJmMTM1Y2RhMS1kZjEwLTRkNjctOTRlMy04YjNjNjI4Yzc0NTUiLCJleHAiOjE1OTkzMjQ2NTl9.6WAiCmHHj3wTOGK_EHNrm7jc1zRM38cb04VQxazSR_s",
  "token_type": "Bearer",
  "expires_in": 1599324659,
  "created_at": "2020-09-04T09:50:59-07:00"
}

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": "dmptool",
  "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"],
}

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

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

Example curl:

curl -v -H "Authorization: Bearer [access_token]" -H "Accept: application/json" https://dmptool.org/api/v1/templates

Response:

{
  "application": "dmptool",
  "source": "GET /api/v1/templates",
  "time": "2020-09-04T09:52:44-07:00",
  "caller": "[user/apiclient name]",
  "code": 200,
  "message": "OK",
  "page": 1,
  "per_page": 20,
  "total_items": 37,
  "next": "/api/v1/templates?page=2&per_page=20",
  "items": [
    {
      "dmp_template": {
        "title": "Alfred P. Sloan Foundation",
        "description": "",
        "version": 3,
        "created": "2018-02-13T07:18:08Z",
        "modified": "2018-04-18T18:18:36Z",
        "affiliation": {
          "name": "Alfred P. Sloan Foundation",
          "abbreviation": "Sloan",
          "affiliation_id": {
            "type": "ror",
            "identifier": "https://ror.org/052csg198"
          }
        },
        "template_id": {
          "type": "other",
          "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

Example curl:

curl -v -X POST https://dmptool.org/api/v1/plans
  -H "Content-Type: application/json"
  -H "Accept: application/json"
  -H "Server-Agent: RAMS"
  -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": {
      "extensions":[
        { "uri":"https://github.com/DMPRoadmap/api-json-schema", "name":"dmproadmap" }
      ],
      "title": "Examination of some interesting topics in biochemistry",
      "description": "This is the full abstract for the DMP/Project.",
      "ethical_issues_exist": "unknown",
      "contact": {
        "name": "Jane Doe",
        "mbox": "[email protected]",
        "affiliation": {
          "name": "Example University",
          "affiliation_id": {
            "type": "ror", "identifier": "https://ror.org/1234567890"
          }
        },
        "contact_id": {
          "type": "orcid", "identifier": "0000-0000-0000-0000"
        }
      },
      "contributor": [
        {
          "name": "John Smith",
          "mbox": "[email protected]",
          "role": [
            "http://credit.niso.org/contributor-roles/data_curation",
            "http://credit.niso.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"
          }
        }
      ],
      "dmproadmap_template": {
        "id": 1181,
        "title": "Generic Template"
      }
    }
  }]
}

The minimum amount of information required by DMPTool 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": "dmptool",
  "source": "GET /api/v1/templates",
  "time": "2020-09-04T09:52:44-07:00",
  "caller": "[user/apiclient name]",
  "code": 200,
  "message": "OK",
  "page": 1,
  "per_page": 20,
  "total_items": 1
  "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": "dmptool",
  "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

Example curl:

curl -v -H "Accept: application/json" -H "Authorization: Bearer [access token]" https://dmptool.org/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": "dmptool",
  "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": {
        "extensions":[
          { "uri":"https://github.com/DMPRoadmap/api-json-schema", "name":"dmproadmap" }
        ],
        "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 Organizational Admin 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)

Example curl:

curl -v -H "Accept: application/json" -H "Authorization: Bearer [access_token]" https://dmptool.org/api/v1/plans/123

Response:

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

{
  "total_items": 1,
  "items": [{
    "dmp": {
      "extensions":[
        { "uri":"https://github.com/DMPRoadmap/api-json-schema", "name":"dmproadmap" }
      ],
      "title": "Examination of some interesting topics in biochemistry",
      "description": "This is the full abstract for the DMP/Project.",
      "language": "eng",
      "created": "2021-02-01T21:57:17Z",
      "modified": "2021-02-05T18:13:05Z,
      "ethical_issues_exist": "unknown",
      "dmp_id": {
        "type": "doi", "identifier": "https://doi.org/10.5072/FK27M0CP3T"
      }
      "contact": {
        "name": "Jane Doe",
        "mbox": "[email protected]",
        "affiliation": {
          "name": "Example University",
          "abbreviation": "EU",
          "affiliation_id": {
            "type": "ror", "identifier": "https://ror.org/1234567890"
          }
        },
        "contact_id": {
          "type": "orcid",
          "identifier": "0000-0000-0000-0000"
        }
      },
      "contributor": [
        {
          "name": "John Smith",
          "mbox": "[email protected]",
          "role": [
            "http://credit.niso.org/contributor-roles/data_curation",
            "http://credit.niso.org/contributor-roles/investigation"
          ],
          "affiliation": {
            "name": "University of Nowhere",
            "abbreviation": "UN",
            "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",
        "description": "This is the full abstract for the DMP/Project.",
        "start": "2021-09-03T00:00:00Z",
        "end": "2024-09-13T00:00:00Z",
        "funding": [
          "name": "Funding Agency",
          "funder_id": {
            type": "fundref", "identifier": "https://api.crossref.org/funders/000000000"
          },
          "grant_id": {
            "type": "other", "identifier": "AWARDNBR-12345"
          },
          "funding_status": "granted",
          "dmproadmap_funding_opportunity_id": {
            "type": "other", "identifier": "OPPNBR-999"
          },
          "dmproadmap_funded_affiliations": [
            "name": "University of Nowhere",
            "abbreviation": "UN",
            "affiliation_id": {
              "type": "ror",
              "identifier": "https://ror.org/123abc45y"
            }
          ]
        ]
      ],
      "dataset": [
        {
          "title": "Test research output",
          "description": "This is our sample",
          "personal_data": "yes",
          "sensitive_data": "unknown",
          "issued": "2023-10-06T00:00:00Z",
          "preservation_statement": "Question: Which data are of long-term value and should be retained?\r\nAnswer: Testing",
          "security_and_privacy": [
            {
              "title": "Ethics \u0026 privacy",
              "description": [
                "Question: Will your project involve sensitive data?\r\nAnswer: Quite possibly!"
              ]
            }
          ],
          "data_quality_assurance": "Question: What data outputs will your research generate?\r\nAnswer: Testing",
          "dataset_id": {
            "type":"other","identifier":"7"
          },"
          distribution":[
            {
              "title": "Anticipated distribution for Test research output", 
              "byte_size": 608811614208,
              "data_access": "open",
              "format": "text/ecmascript",
              "host": {
                "title": "Dryad",
                "description": "Dryad is an international repository of data underlying peer-reviewed scientific ...",
                "url": "https://datadryad.org/stash",
                "dmproadmap_host_id": {
                  "type": "url", "identifier":"https://www.re3data.org/api/v1/repository/r3d100000044"
                }
              }
            }
          ],
          "metadata": [],
          "technical_resource": []
        }
      ],
      "dmproadmap_template": {
        "id": 1181,
        "title": "Generic Template"
      },
      "dmproadmap_links": {
        "download":"http://dmptool-stg.cdlib.org/plans/58890/export.pdf?export%5Bform%5D=true"
      }
    }
  }]
}

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": "dmptool",
  "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"],
}

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.

    DMPTOOL_URL = 'https://dmptool.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 = "#{DMPTOOL_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 = "#{DMPTOOL_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
Clone this wiki locally