Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,34 @@ If we have access to the device's unique `device_token`, we can identify which d
}
```


### /api/session/initialize/v2/• POST

**Body:**

```json
{
"token": "<TOKEN received from Google>",
"device_type": "ANDROID" || "IOS" || "WEB",
"device_token": "123abc456def" || null
}
```

**Example Response:**

```json
{
"success": true,
"data": {
"session_expiration": 1581435566,
"session_token": "3c9e0ee538eaa570b7bc0847f18eab66703cc41f",
"update_token": "d9c3427bd6537131a5d0e8c8fa1d59e764644c2c",
"notification_mode": "MOBILE" || "EMAIL"
},
"timestamp": 1581335566
}
```

### /api/session/update/• POST

**Headers:**
Expand All @@ -158,6 +186,31 @@ If we have access to the device's unique `device_token`, we can identify which d
}
```

### /api/session/update/v2/• POST

**Headers:**

```json
{
"Authorization": "Bearer <update_token>"
}
```

**Example Response:**

```json
{
"success": true,
"data": {
"session_expiration": 1581435566,
"session_token": "3c9e0ee538eaa570b7bc0847f18eab66703cc41f",
"update_token": "d9c3427bd6537131a5d0e8c8fa1d59e764644c2c",
"notification_mode": "MOBILE" || "EMAIL"
},
"timestamp": 1581335566
}
```

### /api/users/tracking/ • GET

**Headers:**
Expand Down
2 changes: 1 addition & 1 deletion envrc.template
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export ANDROID_CLIENT_ID=FILL_IN
export FIREBASE_CLIENT_ID=FILL_IN
export APNS_KEY_ID=FILL_IN
export APNS_AUTH_KEY_PATH=FILL_IN
export APNS_TEAM_ID=FILL_IN
Expand Down
4 changes: 4 additions & 0 deletions src/app/coursegrab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from app.coursegrab.controllers.get_section_controller import *
from app.coursegrab.controllers.hello_world_controller import *
from app.coursegrab.controllers.initialize_session_controller import *
from app.coursegrab.controllers.initialize_session_v2_controller import *
from app.coursegrab.controllers.retrieve_tracking_controller import *
from app.coursegrab.controllers.search_course_controller import *
from app.coursegrab.controllers.send_android_notification_controller import *
Expand All @@ -11,6 +12,7 @@
from app.coursegrab.controllers.update_device_token_controller import *
from app.coursegrab.controllers.update_notification_controller import *
from app.coursegrab.controllers.update_session_controller import *
from app.coursegrab.controllers.update_session_v2_controller import *

# CourseGrab Blueprint
coursegrab = Blueprint("coursegrab", __name__, url_prefix="/api")
Expand All @@ -19,6 +21,7 @@
GetSectionController(),
HelloWorldController(),
InitializeSessionController(),
InitializeSessionV2Controller(),
RetrieveTrackingController(),
SearchCourseController(),
SendAndroidNotificationController(),
Expand All @@ -28,6 +31,7 @@
UpdateDeviceTokenController(),
UpdateNotificationController(),
UpdateSessionController(),
UpdateSessionV2Controller(),
]

for controller in controllers:
Expand Down
15 changes: 7 additions & 8 deletions src/app/coursegrab/controllers/initialize_session_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from google.auth.transport import requests
from google.oauth2 import id_token
from . import *
from ..utils.constants import ANDROID, IOS
from ..utils.constants import ANDROID, IOS, MOBILE


class InitializeSessionController(AppDevController):
Expand All @@ -17,16 +17,12 @@ def content(self, **kwargs):
token = data.get("token")
device_type = data.get("device_type")
device_token = data.get("device_token")
notification = None # temporary fix
try:
if device_type == IOS:
client_id = environ["IOS_CLIENT_ID"]
notification = IOS # temporary fix
elif device_type == ANDROID:
client_id = environ["ANDROID_CLIENT_ID"]
notification = ANDROID # temporary fix
# else: # device_type == WEB
# client_id =
client_id = environ["FIREBASE_CLIENT_ID"]

id_info = id_token.verify_oauth2_token(token, requests.Request(), client_id)

if id_info["iss"] not in ["accounts.google.com", "https://accounts.google.com"]:
Expand All @@ -39,7 +35,10 @@ def content(self, **kwargs):

user = users_dao.create_user(email, first_name, last_name)
session = sessions_dao.create_session(user.id, device_type, device_token)
user = users_dao.update_notification(user.id, notification) # temporary fix

# temporary fix: force update user's notification preference
# (only old ios/android apps should be using this endpoint. no web interaction)
user = users_dao.update_notification(user.id, MOBILE)
return session.serialize_session()

except ValueError:
Expand Down
42 changes: 42 additions & 0 deletions src/app/coursegrab/controllers/initialize_session_v2_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from os import environ
from google.auth.transport import requests
from google.oauth2 import id_token
from . import *
from ..utils.constants import IOS


class InitializeSessionV2Controller(AppDevController):
def get_path(self):
return "/session/initialize/v2/"

def get_methods(self):
return ["POST"]

def content(self, **kwargs):
data = request.get_json()
token = data.get("token")
device_type = data.get("device_type")
device_token = data.get("device_token")
given_name = data.get("given_name")
family_name = data.get("family_name")
try:
if device_type == IOS:
client_id = environ["IOS_CLIENT_ID"]
else: # device_type is ANDROID or WEB
client_id = environ["FIREBASE_CLIENT_ID"]

id_info = id_token.verify_oauth2_token(token, requests.Request(), client_id)
if id_info["iss"] not in ["accounts.google.com", "https://accounts.google.com"]:
raise ValueError("Wrong issuer.")

# ID token is valid. Get the user's Google Account information.
email, first_name, last_name = id_info.get("email"), id_info.get("given_name", given_name), id_info.get("family_name", family_name)
if email != "coursegrabappstore@gmail.com" and email[email.find("@") + 1 :] != "cornell.edu":
raise Exception("You must use a Cornell email")

user = users_dao.create_user(email, first_name, last_name) # Default notification mode = EMAIL
session = sessions_dao.create_session(user.id, device_type, device_token)
return session.serialize_session_v2()

except ValueError:
raise Exception("Invalid token")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm wouldnt people debugging want to know the reason behind the invalid token

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def content(self, **kwargs):
data = request.get_json()
user = kwargs.get("user")

# DEPRECATED!!!!
# NONE is a constant imported from constants.py with string value "None"
# Following line of code converts the NONE="None" string value into Python's null value None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting i did not know this haha

notification = None if data.get("notification") == NONE else data.get("notification")
Expand Down
15 changes: 15 additions & 0 deletions src/app/coursegrab/controllers/update_session_v2_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from . import *


class UpdateSessionV2Controller(AppDevController):
def get_path(self):
return "/session/update/v2/"

def get_methods(self):
return ["POST"]

@extract_bearer
def content(self, **kwargs):
update_token = kwargs.get("bearer_token")
session = sessions_dao.refresh_session(update_token)
return session.serialize_session_v2()
6 changes: 6 additions & 0 deletions src/app/coursegrab/models/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ def serialize_session(self):
"session_expiration": round(self.session_expiration.timestamp()),
"update_token": self.update_token
}

def serialize_session_v2(self):
return {
**self.serialize_session(),
"notification": self.user.notification
}
3 changes: 2 additions & 1 deletion src/app/coursegrab/models/user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from app import db
from ..utils.constants import EMAIL


users_to_sections = db.Table(
Expand All @@ -23,7 +24,7 @@ def __init__(self, **kwargs):
self.email = kwargs.get("email")
self.first_name = kwargs.get("first_name")
self.last_name = kwargs.get("last_name")
self.notification = None # Default notifications set to None
self.notification = EMAIL # Default notifications set to EMAIL

def serialize(self):
return {
Expand Down
3 changes: 1 addition & 2 deletions src/app/coursegrab/notifications/push_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@ def notify_users(section):
emails = []

for user in users:
if user.notification == ANDROID:
if user.notification == MOBILE:
android_tokens.extend(get_user_device_tokens(user.id, ANDROID))
elif user.notification == IOS:
ios_tokens.extend(get_user_device_tokens(user.id, IOS))
elif user.notification == EMAIL:
emails.append(user.email)
Expand Down
9 changes: 5 additions & 4 deletions src/app/coursegrab/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
INVALID = "INVALID"

# Possible values for notification
ANDROID = "ANDROID"
IOS = "IOS"
MOBILE = "MOBILE"
EMAIL = "EMAIL"
NONE = "NONE"
NONE = "NONE" # DEPRECATED!!!!

# Possible values for device type
WEB = "WEB" # + ANDROID, IOS
WEB = "WEB"
ANDROID = "ANDROID"
IOS = "IOS"

# Push Notification
ALGORITHM = "ES256"
Expand Down
19 changes: 4 additions & 15 deletions src/app/migrations/versions/3cfb8cf58e98_update_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

"""
from alembic import op
from datetime import datetime
import sqlalchemy as sa


Expand All @@ -18,24 +19,12 @@

def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('sessions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('device_token', sa.String(), nullable=True),
sa.Column('device_type', sa.String(), nullable=True),
sa.Column('session_token', sa.String(), nullable=False),
sa.Column('session_expiration', sa.DateTime(), nullable=False),
sa.Column('update_token', sa.String(), nullable=False),
sa.Column('last_used', sa.DateTime(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('session_token'),
sa.UniqueConstraint('update_token')
)
op.add_column("sessions", sa.Column('last_used', sa.DateTime(), nullable=False, server_default=str(datetime.now())))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('sessions')
with op.batch_alter_table("sessions") as batch_op:
batch_op.drop_column("last_used")
# ### end Alembic commands ###