UNPKG

tutorbook

Version:

Web app connecting students with expert mentors and tutors.

290 lines (204 loc) 11.8 kB
# 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).