A flask application with REST endpoints for the 2019 registration backend
First make sure pipenv
is installed
Set REG_DEBUG
to run flask in debug mode (uses a local database under /tmp/registration_2019.db
):
REG_DEBUG=true ./bootstrap
To disable authentication (for testing purposes), the environment variable LAH_DISABLE_AUTHENTICATION
can also be set:
LAH_DISABLE_AUTHENTICATION=true REG_DEBUG=true ./bootstrap
If authentication is disabled, the JWT still must be provided however it is not used, so the header can just contain an arbitrary string
To deploy (not in debug mode), the following environment variables must be set:
LAH_REGISTRATION_DB
: DB URI (e.g.mysql+pymysql://<user>:<password>@<host>:<port>/<db-name>
)LAH_JWT_SECRET
: Secret for JWT authenticationLAH_GOOGLE_CLIENT_ID
: Client ID for google oauthLAH_API_ENDPOINT
: The URI of the API endpoint to use when sending verification emailsAWS_ACCESS_KEY_ID
: Amazon access key to send emails through SESAWS_SECRET_ACCESS_KEY
: Amazon secret key to send emails through SES
LAH_REGISTRATION_DB="..." LAH_JWT_SECRET="*******" LAH_GOOGLE_CLIENT_ID="<...>.apps.googleusercontent.com" ./bootstrap.sh
Note that any endpoint with JWT authentication must contain the token in the header as a Bearer token
To authenticate from curl
, simple add the following:
-H "Authorization: Bearer <token>"
Request body:
{
"token": "[string]"
}
Verifies a login through oauth with Google
Response:
401
:{"message": "Could not authenticate"}
200
:{"jwt": "..."}
On 200
response, the "jwt"
should be stored and provided upon further requests
Note that the JWT will expire after 1 day of its creation
Request body:
{
"email": "[string]"
}
Response will be among:
200
:{"status": "ok"}
400
:{"message": {...}}
(detailedreqparse
error if parameters are incorrect)
Returns a list of emails on the email list:
200
: ["[email protected]", "[email protected]", ...]
Request body:
{
# required:
"first_name": "...",
"surname": "...",
"email": "[email protected]",
"age": 18,
"school": "...",
"grade": 12,
"student_phone_number": "5555555555"
"gender": "...",
"ethnicity": "..."
"tshirt_size": "S", # can be "S", "M", "L", or "XL"
"previous_hackathons": 0,
# required if age is under 18:
"guardian_name": "...",
"guardian_email": "...",
"guardian_phone_number": "5555555555",
# optional:
"github_username": "...",
"linkedin_profile": "...",
"dietary_restrictions": "..."
}
Responses will be among the following:
200
:{"status": "ok"}
400
:{"message": "Minors must provide guardian information"}
400
:{"message": {...}}
(detailedreqparse
error if parameters are incorrect)
When implementing a frontend that calls this endpoint, it is recommended to preprocess some data, e.g.:
- Sanitize phone numbers from (XXX) XXX-XXXX or XXX.XXX.XXXX, etc to XXXXXXXXXX (for ease of searching), and display them back in a consistent (more readable) format
- Verify the format of emails
- If applicant is under 18 years old, ensure that guardian info is inputted (maybe even hide said fields if age is 18+)
- Provide male and female options for gender, along with an "other" field for custom inputs
- Provide a list of common dietary restrictions as checkboxes (vegetarian, vegan, peanut allergy, etc) as well as an "other" for custom input
Note that these recommendations are not necessarily enforced on the server level, but they will help maintain some consistency in the database
Will attempt to verify the user's email
Responses will be among the following:
422
:{"message": "Could not verify"}
200
:{"status": "ok"}
Request body:
Same as to signup
endpoint, except:
- all fields are optional
- can also accept
"acceptance_status"
field, with possible values:"none"
"waitlisted"
"rejected"
"queue"
"accepted"
- can also accept
"email_verified"
field, with a boolean value
Will update the user's data with the changes provided in the request
Response will be among:
200
:{"status": "ok"}
200
:{"status": "ok", "message": "unchanged"}
400
:{"message": "Minors must provide guardian information"}
400
:{"message": "Email already in use"}
400
:{"message": {...}}
(detailedreqparse
error if parameters are incorrect)
Lists all signups (only the most recent and up-to-date data, where outdated=False
)
Response will be a list of JSON objects, each corresponding to a signup:
[
{
"user_id": "f12e9cb1-25a4-4ebc-bcaf-0d79a7f95a9f",
"first_name": "FirstName",
"surname": "LastName",
"email": "[email protected]",
"age": 18,
"school": "SomeSchool",
"grade": 12,
"student_phone_number": "555-555-5555",
"gender": "male",
"ethnicity": "Ethnicity",
"tshirt_size": "L",
"previous_hackathons": 0,
"guardian_name": "",
"guardian_email": "",
"guardian_phone_number": "555-555-5555",
"github_username": null,
"linkedin_profile": null,
"dietary_restrictions": null,
"signed_waver": false,
"acceptance_status": "none",
"email_verified": true,
"timestamp": "2018-10-30 03:23:32",
"outdated": false
}, ...
]
Request body should be a JSON object with a single key "query"
The query can be a string, in which case it is checked against each of the following conditions:
user_id
equals queryfirst_name
contains querysurname
contains queryemail
contains querystudent_phone_number
contains queryguardian_name
contains queryguardian_email
contains queryguardian_phone_number
contains query
The query can also be a JSON object, accepting (optionally) each of the fields returned in the list
endpoint, except for timestamp
If outdated
is provided in the request, it will also be provided in the response (otherwise it will be assumed false and omitted)
outdated
can also have a value of *
, in which case both current and outdated signups will be returns
Gets the history (all outdated=True
and outdated=False
signups) for a user
Note that after a user is deleted (using the delete
endpoint), history will still find the data given user_id
Response will be among:
400
,{"message": "User does not exist"}
200
,[{}, ...]
(list of signups with same format as thelist
andsearch
endpoints, and theoutdated
field)
Deletes the user from the database (internally just sets outdated
to true for all of the user's signups)
Response will be among:
400
,{"message": "User does not exist"}
200
,{"status": "ok"}
Request body:
{
# required:
"name": "...",
"email": "[email protected]",
# optional:
"phone": "..."
}
Responses will be among the following:
200
:{"status": "ok"}
200
:{"status": "ok", "message": "Guest already added (by email)"}
400
:{"message": {...}}
(detailedreqparse
error if parameters are incorrect)
Request body:
Same as to guest signup
endpoint, except all fields are optional
Response will be among:
200
:{"status": "ok"}
200
:{"status": "ok", "message": "unchanged"}
400
:{"message": "Email already in use"}
400
:{"message": {...}}
(detailedreqparse
error if parameters are incorrect)
Lists all guests (only the most recent and up-to-date data, where outdated=True
)
Response will be a list of JSON objects, each corresponding to a guest
Request body should be a JSON object with a single key "query"
The query can be a string, in which case it is checked against each of the following conditions:
guest_id
equals queryname
contains queryemail
contains queryphone
contains query
The query can also be a JSON object, accepting (optionally) each of the fields returned in the guest list
endpoint, except for timestamp
If outdated
is provided in the request, it will also be provided in the response (otherwise it will be assumed false and omitted)
outdated
can also have a value of *
, in which case both current and outdated signups will be returns
Deletes the user from the database (internally just sets outdated
to true for all of the user's signups)
Response will be among:
400
,{"message": "Guest does not exist"}
200
,{"status": "ok"}