# API

We use [serverless Next.js API
functions](https://nextjs.org/docs/api-routes/introduction) deployed on
[Vercel](https://vercel.com) to power most of our back-end work:

1. Creating new user accounts and updating existing user accounts when the
   [for students1](https://tutorbook.org/pupils) or [for
   volunteers](https://tutorbook.org/tutors) are submitted.
2. Signing users in via [custom Firebase Authentication
   tokens](https://firebase.google.com/docs/auth/admin/create-custom-tokens)
   (also when a user fills out a sign-up form).
3. Scheduling tutoring appointments (e.g. creating lesson requests, sending an
   approval email to get parental consent, creating Bramble rooms, sending the
   appointment emails, and creating Firestore NoSQL Database records).

Each of these API functions catches all known errors and sends them back to the
client in plain English (i.e. you can display `res.data` to the user when you
receive a `400` or `500` HTTP error code).

## Data Model

All data model references can be found in [this
README](https://github.com/tutorbookapp/tutorbook/tree/develop/src/model#readme)
and are defined in [the `src/model`
directory](https://github.com/tutorbookapp/tutorbook/tree/develop/src/model).

## REST API

Included below are the specifications of our REST API (which strive to follow
the guidelines [outlined here](https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9))
endpoints (accessible from `https://tutorbook.org/api/{functionName}`).

The Firebase Authentication JWT (JSON Web Tokens) referred to below can be
retrieved via the client-side [Firebase Authentication JavaScript
SDK](https://firebase.google.com/docs/auth/web/start) like so:

```typescript
const token: string = await firebase.auth.currentUser.getIdToken();
const { data } = await axios({
  method: 'get',
  url: `/api/users/${firebase.auth.currentUser.uid}`,
  headers: { Authorization: `Bearer ${token}` },
});
const user: User = new User(data);
```

### `/api/users`

#### `GET`

Lists all of Tutorbook's users (to a certain extent; and typically with
truncated data).

##### Authentication

You may optionally pass a JWT that gives full access to some data (i.e. the data
will not be truncated and will include contact information). If given a JWT, we
will un-truncate data belonging to the owner of the JWT and any of the
organizations that owner is a part of.

Note that passing a JWT **does not** apply any filters to the data; they are
still filtered as before and thus can be a mix of both truncated and
un-truncated data. Thus, it is recommended that you filter by `orgs` using the
parameters described below to ensure that you're receiving un-truncated data.

##### Parameters

We support all of the `Query` fields as URL query parameters. These parameters
are entirely optional and only serve to further filter the data.

#### `POST`

Creates a new user's profile document and Firebase Authentication account.

**Note:** This will fail if a user with the given email address already exists
(in which case, one should use a `PUT` request to the `/api/users/[user]`
endpoint instead).

##### Authentication

No authentication is required to create a new user. This request will fail
however, if a user with the same email address already exists (in which case,
one should use the `/api/user/update` endpoint to update an existing profile).

##### Data

The following data fields should be sent in the HTTP request body.

| Field     | Type     | Description                                                                        |
| --------- | -------- | ---------------------------------------------------------------------------------- |
| `user`    | `User`   | The user to create (i.e. output of one of the two sign-up forms).                  |
| `parents` | `User[]` | The user's parents (i.e. output of the "parent" fields in the pupil sign-up form). |

##### Actions

Upon request, the `/api/user` serverless API function:

1. Creates and signs-in a new Firebase Authentication user.
2. (Optional) Creates a new Firesbase Authentication user for the parents.
3. (Optional) Creates a new Firestore profile document for the parents.
4. Creates a new Firestore profile document for the given user.
5. Sends an email verification link to the new user (and his/her parents).

##### Response

Responds with the created `User` document in JSON form (i.e. `UserJSON` that
exactly matches what is now in our Firestore document-based NoSQL database).

### `/api/users/[id]`

#### `GET`

Fetches the user's profile document.

##### Authentication

Requires a JWT that belongs to either:

1. The user whose profile document we're retrieving.
2. A member of an organization who owns the profile document we're retrieving
   (i.e. the organization's ID is listed in the profile's `orgs` field).

##### Response

Responds with a `User` object in JSON form (i.e. `UserJSON`).

#### `PUT`

Updates the user's profile document.

##### Authentication

As usual, this requires a JWT that belongs to either:

1. The user whose profile document we're retrieving.
2. A member of an organization who owns the profile document we're retrieving
   (i.e. the organization's ID is listed in the profile's `orgs` field).

##### Data

Accepts a `User` object (in the form of JSON) in the request body.

**Note:** That `User` object must contain certain fields or the request will
fail:

- A valid email address; one cannot remove the account's associated email
  address.
- A valid `id` (i.e the unique Firebase Authentication uID assigned during
  account creation).

#### `DELETE`

Deletes the user's profile document and it's associated Firebase Authentication
account.

##### Authentication

As usual, this requires a JWT that belongs to either:

1. The user whose profile document we're retrieving.
2. A member of an organization who owns the profile document we're retrieving
   (i.e. the organization's ID is listed in the profile's `orgs` field).

### `/api/users/[id]/parents`

#### `POST`

Creates a new parent profile and adds it to the user's `parents` field.

If the parent profile already exists, this merely ensures that it is included in
the user's `parents` field.

##### Authentication

As usual, this requires a JWT that belongs to either:

1. The user whose parents we're creating (or updating).
2. A member of an organization who owns the user whose parents we're creating or
   updating (i.e. the organization's ID is listed in the user's `orgs` field).

##### Data

Accepts the same `User` object that the `/api/users` POST endpoint accepts
(because this endpoint essentially just relays to that endpoint).

##### Response

Responds with the parent's profile data.

### `/api/requests`

#### `POST`

The `/api/request` endpoint creates a new lesson request that is awaiting
parental approval (each time a pupil sends a lesson request, the parent must
first approve of the requested tutor before the appointment emails (with a link
to the Bramble room) are sent).

##### Data

The following data fields should be sent in the HTTP request body.

| Field   | Type     | Description                                               |
| ------- | -------- | --------------------------------------------------------- |
| `appt`  | `Appt`   | The tutoring appointment to create a pending request for. |
| `token` | `string` | The logged in user's Firebase Authentication `idToken`.   |

##### Actions

Upon request, the `/api/request` serverless API function:

1. Performs the following verifications (sends a `400` error code and an
   accompanying human-readable error message if any of them fail):
   - Verifies the correct request body was sent (e.g. all parameters are there
     and are all of the correct types).
   - Verifies that the `attendees` all have user IDs and profile documents.
   - Verifies that the appointment creator (the owner of the given `token` JWT)
     is an `attendee`.
   - Verifies that the requested `Timeslot` is within all of the `attendee`'s
     availability (by reading each `attendee`'s Firestore profile document).
   - Verifies that the requested `subjects` are included in each of the tutors'
     Firestore profile documents (where a tutor is defined as an `attendee` whose
     `roles` include `tutor`).
   - Verifies that the given `token` belongs to one of the `appt`'s `attendees`.
2. Creates [the Bramble tutoring lesson room](https://about.bramble.io/api.html)
   (so that the parent can preview the venue that their child will be using to
   connect with their tutor).
3. Creates a new `request` document containing the given `appt`'s data in the
   pupil's (the owner of the given JWT `token`) Firestore sub-collections.
4. Sends an email to the pupil's parent(s) asking for parental approval of the
   tutoring match.
5. Sends an email to the pupil (the sender of the lesson request) telling them
   that we're awaiting parental approval.

##### Response

The created request object (that includes the ID of it's Firestore document).

### `/api/appts`

#### `POST`

The `/api/appt` endpoint approves a pending lesson request and creates a
tutoring appointment.

##### Data

The following parameters should be sent in the HTTP request body.

| Field     | Type     | Description                                                                                                                                                           |
| --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `request` | `string` | The path of the pending tutoring lesson's Firestore document to approve (e.g. `partitions/default/users/MKroB319GCfMdVZ2QQFBle8GtCZ2/requests/CEt4uGqTtRg17rZamCLC`). |
| `id`      | `string` | The user ID of the parent approving the lesson request (e.g. `MKroB319GCfMdVZ2QQFBle8GtCZ2`).                                                                         |

##### Actions

Upon request, the `/api/appt` serverless API function:

1. Verifies the correct request body was sent (e.g. all parameters are there and
   are all of the correct types).
2. Fetches the given pending request's data from our Firestore database.
3. Performs the following verifications (some of which are also included in the
   original `/api/request` endpoint):
   - Verifies that the `attendees` all have user IDs and profile documents.
   - Verifies that the pupil (the user whose profile document was referenced in
     the given `request` Firestore document path) is within the `attendees`.
   - Verifies that the parent (the owner of the given `id`) is actually the
     pupil's parent (i.e. the `attendee` whose profile document was referenced
     in the given `request` Firestore document path has the given `id` in their
     profile's `parents` field).
   - Verifies that the requested `Timeslot` is within all of the `attendee`'s
     availability (by reading each `attendee`'s Firestore profile document).
   - Verifies that the requested `subjects` are included in each of the tutors'
     Firestore profile documents (where a tutor is defined as an `attendee` whose
     `roles` include `tutor`).
4. Deletes the old `request` documents.
5. Creates a new `appt` document containing the request body in each of the
   `attendee`'s Firestore `appts` subcollection.
6. Updates each `attendee`'s availability (in their Firestore profile document)
   to reflect this appointment (i.e. remove the appointment's `time` from their
   availability).
7. Sends each of the `appt`'s `attendee`'s an email containing instructions for
   how to access their Bramble virtual-tutoring room.

##### Response

The created appointment object (that includes the ID of the Firestore
documents).
