Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Backend/feature/user endpoint #30

Merged
merged 40 commits into from
Mar 9, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c4eca1c
start setup
warreprovoost Feb 24, 2024
c9b49e2
simple post
warreprovoost Feb 24, 2024
774601b
dockerfile requirements fixed
warreprovoost Feb 24, 2024
de11124
users update added
warreprovoost Feb 25, 2024
7b3f7f9
users get added
warreprovoost Feb 25, 2024
4cb75d9
delete added
warreprovoost Feb 25, 2024
958ea14
confest changed
warreprovoost Feb 25, 2024
c6a2e99
confest changed
warreprovoost Feb 25, 2024
f072cfc
cleaned up the code
warreprovoost Feb 25, 2024
3006e43
fixed linter
warreprovoost Feb 25, 2024
69c95ad
fixed linter
warreprovoost Feb 25, 2024
1f25ba2
requested changes
warreprovoost Feb 25, 2024
0aad08f
requested changes
warreprovoost Feb 26, 2024
5f4a612
linter complaints
warreprovoost Feb 26, 2024
46b0db5
requested style change added
warreprovoost Feb 29, 2024
d2f3a2b
pylnt complaints
warreprovoost Feb 29, 2024
2c7322e
moved fixture
warreprovoost Feb 29, 2024
cb4823c
disabled pylint message
warreprovoost Mar 2, 2024
b262dd2
disabled str
warreprovoost Mar 2, 2024
5c3dbc5
pylint
warreprovoost Mar 2, 2024
ad864fc
Merge remote-tracking branch 'origin/development' into backend/featur…
warreprovoost Mar 2, 2024
1cf7dac
merged with dev
warreprovoost Mar 2, 2024
4025e55
typo
warreprovoost Mar 2, 2024
3dff6ba
pylint changes
warreprovoost Mar 2, 2024
2583ce4
pylint changes
warreprovoost Mar 2, 2024
5b567f3
pylint changes
warreprovoost Mar 2, 2024
8ca7d82
typo
warreprovoost Mar 3, 2024
19cb424
Merge branch 'development' into backend/feature/user-endpoint
warreprovoost Mar 7, 2024
907c1f4
Message changes
warreprovoost Mar 7, 2024
fdc4e25
added data to requests
warreprovoost Mar 7, 2024
fb556fb
pylint complaints
warreprovoost Mar 7, 2024
2afa822
placed return under try
warreprovoost Mar 8, 2024
34399b5
pylint
warreprovoost Mar 8, 2024
2114668
Merge branch 'development' into backend/feature/user-endpoint
warreprovoost Mar 8, 2024
c125718
query get users
warreprovoost Mar 8, 2024
60e0183
small comment
warreprovoost Mar 8, 2024
09f1325
pylint
warreprovoost Mar 8, 2024
66c74f9
requested changes
warreprovoost Mar 9, 2024
0c6a35b
Merge branch 'development' into backend/feature/user-endpoint
warreprovoost Mar 9, 2024
4f3fbdb
merge conflict
warreprovoost Mar 9, 2024
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
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ docs/_build/
dist/
venv/
.env
.run/
7 changes: 5 additions & 2 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
FROM python:3.9
RUN mkdir /app
WORKDIR /app/
WORKDIR /app
ADD ./project /app/
COPY requirements.txt /app/requirements.txt
RUN pip3 install -r requirements.txt
CMD ["python3", "/app"]
COPY . /app
ENTRYPOINT ["python"]
CMD ["__main__.py"]
2 changes: 2 additions & 0 deletions backend/project/__init__.py
warreprovoost marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from flask import Flask
from .db_in import db
from .endpoints.index.index import index_bp
from .endpoints.users import users_bp


def create_app():
Expand All @@ -16,6 +17,7 @@ def create_app():

app = Flask(__name__)
app.register_blueprint(index_bp)
app.register_blueprint(users_bp)

return app

Expand Down
12 changes: 11 additions & 1 deletion backend/project/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@
from sys import path
from os import getenv
from dotenv import load_dotenv
from sqlalchemy import URL
from project import create_app_with_db

path.append(".")

if __name__ == "__main__":
load_dotenv()
app = create_app_with_db(getenv("DB_HOST"))

url = URL.create(
drivername="postgresql",
username=getenv("POSTGRES_USER"),
password=getenv("POSTGRES_PASSWORD"),
host=getenv("POSTGRES_HOST"),
database=getenv("POSTGRES_DB")
)

app = create_app_with_db(url)
app.run(debug=True)
186 changes: 185 additions & 1 deletion backend/project/endpoints/index/OpenAPI_Object.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,189 @@
}
]
},
"paths": []
"paths": {
"/users": {
"get": {
"summary": "Get all users",
"responses": {
"200": {
"description": "A list of users",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"uid": {
"type": "string"
},
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
}
},
"required": ["uid", "is_teacher", "is_admin"]
}
}
}
}
}}},
"post": {
"summary": "Create a new user",
"requestBody": {
"required": true,
"content":{
"application/json": {
"schema": {
"type": "object",
"properties": {
"uid": {
"type": "string"
},
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
}
},
"required": ["uid", "is_teacher", "is_admin"]
}
}
}
},
"responses": {
"201": {
"description": "User created successfully"
},
"400": {
"description": "Invalid request data"
},
"415": {
"description": "Unsupported Media Type. Expected JSON."
},
"500": {
"description": "An error occurred while creating the user"
}
}

},
"/users/{user_id}": {
"get": {
"summary": "Get a user by ID",
"parameters": [
{
"name": "user_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "A user",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"uid": {
"type": "string"
},
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
}
},
"required": ["uid", "is_teacher", "is_admin"]
}
}
}
},
"404": {
"description": "User not found"
}
}
},
"patch": {
"summary": "Update a user's information",
"parameters": [
{
"name": "user_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"is_teacher": {
"type": "boolean"
},
"is_admin": {
"type": "boolean"
}
},
"required": ["is_teacher", "is_admin"]
}
}
}
},
"responses": {
"200": {
"description": "User updated successfully"
},
"404": {
"description": "User not found"
},
"415": {
"description": "Unsupported Media Type. Expected JSON."
},
"500": {
"description": "An error occurred while patching the user"
}
}
},
"delete": {
"summary": "Delete a user",
"parameters": [
{
"name": "user_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "User deleted successfully"
},
"404": {
"description": "User not found"
},
"500": {
"description": "An error occurred while deleting the user"
}
}
}
}
}
}
}

123 changes: 123 additions & 0 deletions backend/project/endpoints/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""Users api endpoint"""
from flask import Blueprint, request, jsonify
from flask_restful import Resource, Api
from sqlalchemy.exc import SQLAlchemyError

from project import db
from project.models.users import Users as userModel

users_bp = Blueprint("users", __name__)
users_api = Api(users_bp)


class Users(Resource):
"""Api endpoint for the /users route"""

def get(self):
"""
This function will respond to get requests made to /users.
It should return all users from the database.
"""
users = userModel.query.all()

return jsonify(users)

def post(self):
"""
This function will respond to post requests made to /users.
It should create a new user and return a success message.
"""
uid = request.json.get('uid')
is_teacher = request.json.get('is_teacher')
is_admin = request.json.get('is_admin')

if is_teacher is None or is_admin is None or uid is None:
return {
"Message": "Invalid request data!",
"Correct Format": {
warreprovoost marked this conversation as resolved.
Show resolved Hide resolved
"uid": "User ID (string)",
"is_teacher": "Teacher status (boolean)",
"is_admin": "Admin status (boolean)"
}
}, 400
try:
user = db.session.get(userModel, uid)
if user is not None:
# bad request, error code could be 409 but is rarely used
return {"Message": f"User {uid} already exists"}, 400
# Code to create a new user in the database using the uid, is_teacher, and is_admin
new_user = userModel(uid=uid, is_teacher=is_teacher, is_admin=is_admin)
db.session.add(new_user)
db.session.commit()

except SQLAlchemyError:
# every exception should result in a rollback
db.session.rollback()
return {"Message": "An error occurred while creating the user"}, 500

return {"Message": "User created successfully!"}, 201


class User(Resource):
"""Api endpoint for the /users/{user_id} route"""

def get(self, user_id):
"""
This function will respond to GET requests made to /users/<user_id>.
It should return the user with the given user_id from the database.
"""
user = db.session.get(userModel, user_id)
if user is None:
return {"Message": "User not found!"}, 404
warreprovoost marked this conversation as resolved.
Show resolved Hide resolved

return jsonify(user)

def patch(self, user_id):
"""
Update the user's information.

Returns:
dict: A dictionary containing the message indicating the success
or failure of the update.
"""
is_teacher = request.json.get('is_teacher')
is_admin = request.json.get('is_admin')
try:
user = db.session.get(userModel, user_id)
if user is None:
return {"Message": "User not found!"}, 404

if is_teacher is not None:
Gerwoud marked this conversation as resolved.
Show resolved Hide resolved
user.is_teacher = is_teacher
if is_admin is not None:
user.is_admin = is_admin

# Save the changes to the database
db.session.commit()
except SQLAlchemyError:
# every exception should result in a rollback
db.session.rollback()
return {"Message": "An error occurred while patching the user"}, 500
return {"Message": "User updated successfully!"}

def delete(self, user_id):
"""
This function will respond to DELETE requests made to /users/<user_id>.
It should delete the user with the given user_id from the database.
"""
try:
user = db.session.get(userModel, user_id)
if user is None:
return {"Message": "User not found!"}, 404

db.session.delete(user)
db.session.commit()
except SQLAlchemyError:
# every exception should result in a rollback
db.session.rollback()
return {"Message": "An error occurred while deleting the user"}, 500
return {"Message": "User deleted successfully!"}


users_api.add_resource(Users, "/users")
users_api.add_resource(User, "/users/<string:user_id>")
11 changes: 6 additions & 5 deletions backend/project/models/users.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
"""Model for users"""
import dataclasses

from sqlalchemy import Boolean, Column, String
from project import db


@dataclasses.dataclass
class Users(db.Model):
"""This class defines the users table,
a user has an uid,
a user has a uid,
is_teacher and is_admin booleans because a user
can be either a student,admin or teacher"""

__tablename__ = "users"
uid = Column(String(255), primary_key=True)
is_teacher = Column(Boolean)
is_admin = Column(Boolean)
uid:str = Column(String(255), primary_key=True)
is_teacher:bool = Column(Boolean)
is_admin:bool = Column(Boolean)
Loading