Skip to content

API Design

Joseph Azevedo edited this page Nov 19, 2020 · 19 revisions

This page contains information on the API design.

Authentication

In general, most routes other than /auth/login and /auth/token-exchange require authentication. This is done using the Authorization HTTP header and a bearer token. Once a fully-exchanged token has been issued to a user, it can be used as follows:

Authorization: Bearer <token>

Routes

Locations

Get all locations

GET /locations

Response

{
  "locations": [
    {
      "name": string,
      "id": string,
      "location": {
        "latitude": number,
        "longitude": number,
      },
    },
    {...},
    ...,
  ],
}

Get all products at location by ID with optional search

GET /locations/{id}/products?search=string

Response

{
  "products": [
    {
      "name": string,
      "id": string,
      "thumbnail": string[url] | null,
      "amount": number,
    },
    ...,
  ],
}

Get product by ID at location by ID

GET /locations/{id}/products/{id}

Response

{
  "name": string,
  "id": string,
  "thumbnail": string[url] | null,
  "nutritional_facts": string[url] | null,
  "amount": number,
}

Create new location

POST /locations

Request

{
  "name": string,
  "transact_identifier": string,
  "location": {
    "latitude": number,
    "longitude": number,
  },
}

Response

{
  "id": string,
  "name": string,
  "transact_identifier": string,
  "location": {
    "latitude": number,
    "longitude": number,
  },
}

Update location by ID

PATCH /locations/{id}

Request

{
  "name": string | null,
  "transact_identifier": string | null,
  "location": {
    "latitude": number,
    "longitude": number,
  } | null,
}

Response

{
  "id": string,
  "name": string,
  "transact_identifier": string,
  "location": {
    "latitude": number,
    "longitude": number,
  },
}

Delete location by ID

DELETE /locations/{id}

Returns 204 (no content) upon success

Products

Get all products with optional search

GET /products?search=string

Response

{
  "products": [
    {
      "name": string,
      "id": string,
      "thumbnail": string[url] | null,
      "nutritional_facts": string[url] | null,
    },
    ...,
  ],
}

Get product by ID

GET /products/{id}

Response

{
  "name": string,
  "id": string,
  "thumbnail": string[url] | null,
  "nutritional_facts": string[url] | null,
  "amounts": {
    "string(id)": number | null,
  },
}

Update product metadata by ID

PATCH /products/{id}

Request

This route includes new values for the modifiable fields ("id" and "name" are read-only).

{
  "nutrition"?: string[url] | null,
  "thumbnail"?: string[url] | null,
}

Response

{
  "id": string,
  "thumbnail": string[url] | null,
  "nutritional_facts": string[url] | null,
}

Announcements

Get all announcements

GET /announcements

Response

{
  "announcements": [
    {
      "id": string,
      "title": string,
      "body": string,
      "timestamp": string,
    },
    ...,
  ],
}

Create new announcement

POST /announcements

Request

{
  "title": string,
  "body": string,
  "timestamp": string,
}

Response

{
  "id": string,
  "title": string,
  "body": string,
  "timestamp": string,
}

Update announcement by ID

PATCH /announcements/{id}

Request

{
  "title"?: string | null,
  "body"?: string | null,
}

Response

{
  "id": string,
  "title": string,
  "body": string,
  "timestamp": string,
}

Delete announcement by ID

DELETE /announcements/{id}

Returns 204 (no content) upon success

Memberships

Get all memberships

GET /memberships

Response

{
  "memberships": [
    {
      "username": string,
      "admin_access": bool,
    },
    ...,
  ],
}

Get membership by username

GET /memberships/{username}

Response

{
  "username": string,
  "admin_access": bool,
}

Create new membership

POST /memberships

Request

{
  "username": string,
  "admin_access": bool,
}

Response

{
  "username": string,
  "admin_access": bool,
}

Delete membership by username

DELETE /memberships/{username}

Returns 204 (no content) upon success

Update membership by username

PATCH /memberships/{username}

Request

{
  "admin_access?": bool,
}

Response

{
  "username": string,
  "admin_access": bool,
}

Upload

Upload file

POST /upload

Request

The request body is a multi-part form data:

file: <file binary data>

Response

{
  "url": string,
}

Authentication

Login via CAS

GET /auth/login?redirect_uri=string

The login route redirects to the GT Single Sign-On service and authenticates via CAS. After the user signs in, they are redirected back to whatever URL they originally gave in the redirect_uri query parameter, with the addition that ?code=XXXX is added to the URI (which is used during token exchange). This imitates the OAuth 2 Authorization Code grant flow, although it doesn't exactly conform to the spec.

Exchange auth code for token

POST /auth/token-exchange

Request

<auth_code_from_login>

Response

{
  "token": string,
  "session": {
    "username": string,
    "first_name": string,
    "last_name": string,
    "issued_at": string[rfc 3339],
    "expires_after": number | null,
  },
  "permissions": {
    "admin_access": boolean,
  },
}

Get session

GET /auth/session

Response

{
  "session": {
    "username": string,
    "first_name": string,
    "last_name": string,
    "issued_at": string[rfc 3339],
    "expires_after": number | null,
  },
  "permissions": {
    "admin_access": boolean,
  },
}

Uncategorized

Perform health check

GET /health

Returns a 204 (no content)