diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 1168bd9..4b09480 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -5,7 +5,8 @@ name: Python application on: push: - branches: [ "main" ] + push: + branches: [ "main" ] pull_request: branches: [ "main" ] @@ -37,3 +38,9 @@ jobs: - name: Test with pytest run: | pytest + - name: Verify docker compose version + run: | + docker compose version + - name: Start app using Docker Compose + run: | + docker compose up -d diff --git a/backend/app/app.py b/backend/app/app.py index 73eeae8..0004c7e 100644 --- a/backend/app/app.py +++ b/backend/app/app.py @@ -3,7 +3,7 @@ import sys import os sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from db.db import db_connect, db_insert_user, db_update_user, db_delete_user, db_get_user_by_username, db_insert_snippet, db_update_snippet, db_delete_snippet, db_get_snippet_by_id, db_filter_snippets, db_insert_comment, User, Comment, Snippet +from db.db import db_connect, db_insert_user, db_update_user, db_delete_user, db_get_user_by_username, db_insert_snippet, db_update_snippet, db_delete_snippet, db_get_snippet_by_id, db_filter_snippets, db_insert_comment, db_get_comments_by_snippet_id, db_insert_project, db_get_project_by_id, db_update_project, db_insert_user_project, db_get_all_comments, db_get_all_projects, db_get_user_projects_by_project_id, User, Comment, Snippet, Project, UserProject app = Flask(__name__) CORS(app) # Enable CORS @@ -142,49 +142,67 @@ def get_comments(snippet_id): return jsonify([]), 200 return jsonify(comments[snippet_id]), 200 -# @app.route('/projects', methods=['POST']) -# def create_project(): -# conn = db_connect() -# project = Project( -# id=request.json.get('id'), -# name=request.json.get('name'), -# description=request.json.get('description') -# ) -# project_id = db_insert_project(conn, project) -# if project_id: -# return jsonify({'id': project_id, 'name': project.name, 'description': project.description}), 201 -# else: -# return jsonify({'error': 'Failed to create project'}), 500 - -# @app.route('/projects/', methods=['GET']) -# def get_project(project_id): -# conn = db_connect() -# project = db_get_project_by_id(conn, project_id) -# if project: -# return jsonify({'id': project.id, 'name': project.name, 'description': project.description}) -# else: -# return jsonify({'error': 'Project not found'}), 404 - -# @app.route('/projects/', methods=['PUT']) -# def update_project(project_id): -# conn = db_connect() -# project = Project( -# id=project_id, -# name=request.json.get('name'), -# description=request.json.get('description') -# ) -# db_update_project(conn, project) -# return jsonify({'id': project.id, 'name': project.name, 'description': project.description}) - -# @app.route('/projects//members', methods=['POST']) -# def add_project_member(project_id): -# conn = db_connect() -# project_member = ProjectMember( -# project_id=project_id, -# user_id=request.json.get('id') -# ) -# db_insert_project_member(conn, project_member) -# return jsonify({'project_id': project_id, 'user_id': project_member.user_id}), 201 +@app.route('/comments', methods=['GET']) +def get_all_comments(): + conn = db_connect() + comments = db_get_all_comments(conn) + return jsonify([{'id': comment.id, 'snippet_id': comment.snippet_id, 'user_name': comment.user_name, 'content': comment.content, 'created_at': comment.created_at} for comment in comments]) + +@app.route('/projects', methods=['POST']) +def create_project(): + conn = db_connect() + project = Project( + id=None, # ID will be generated by the database + name=request.json.get('name'), + description=request.json.get('description') + ) + project_id = db_insert_project(conn, project) + if project_id: + return jsonify({'id': project_id, 'name': project.name, 'description': project.description}), 201 + else: + return jsonify({'error': 'Failed to create project'}), 500 + +@app.route('/projects/', methods=['PUT']) +def update_project(project_id): + conn = db_connect() + project = Project( + id=project_id, + name=request.json.get('name'), + description=request.json.get('description') + ) + db_update_project(conn, project) + return jsonify({'id': project.id, 'name': project.name, 'description': project.description}) + +@app.route('/projects/', methods=['GET']) +def get_project(project_id): + conn = db_connect() + project = db_get_project_by_id(conn, project_id) + if project: + return jsonify({'id': project.id, 'name': project.name, 'description': project.description}) + else: + return jsonify({'error': 'Project not found'}), 404 + +@app.route('/projects', methods=['GET']) +def get_all_projects(): + conn = db_connect() + projects = db_get_all_projects(conn) + return jsonify([{'id': project.id, 'name': project.name, 'description': project.description} for project in projects]) + +@app.route('/projects//members', methods=['POST']) +def add_project_member(project_id): + conn = db_connect() + project_member = UserProject( + user_name=request.json.get('user_name'), + project_id=project_id + ) + db_insert_user_project(conn, project_member) + return jsonify({'project_id': project_id, 'user_name': project_member.user_name}), 201 + +@app.route('/projects//members', methods=['GET']) +def get_project_members(project_id): + conn = db_connect() + project_members = db_get_user_projects_by_project_id(conn, project_id) + return jsonify([{'user_name': member.user_name, 'project_id': member.project_id} for member in project_members]) if __name__ == '__main__': app.run(debug=True, port=55555) \ No newline at end of file diff --git a/backend/app/test_app.py b/backend/app/test_app.py index 4b5d238..139c308 100644 --- a/backend/app/test_app.py +++ b/backend/app/test_app.py @@ -3,7 +3,7 @@ import sys import os sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from db.db import db_connect, db_insert_user, db_update_user, db_delete_user, db_get_user_by_username, db_insert_snippet, db_update_snippet, db_delete_snippet, db_get_snippet_by_id, db_filter_snippets, db_insert_comment, User, Comment, Snippet +from db.db import db_connect, db_insert_user, db_update_user, db_delete_user, db_get_user_by_username, db_insert_snippet, db_update_snippet, db_delete_snippet, db_get_snippet_by_id, db_filter_snippets, db_insert_comment, db_insert_project, User, Comment, Snippet, Project, UserProject @pytest.fixture def client(): @@ -136,35 +136,34 @@ def test_get_comments(client): assert response.status_code == 200 assert isinstance(response.json, list) -# def test_create_project(client): -# response = client.post('/projects', json={ -# 'id': 'project1', -# 'name': 'Project One', -# 'description': 'A sample project' -# }) -# assert response.status_code == 201 -# assert response.json['name'] == 'Project One' +def test_create_project(client): + response = client.post('/projects', json={ + 'name': 'Project One', + 'description': 'A sample project' + }) + assert response.status_code == 201 + assert response.json['name'] == 'Project One' -# def test_get_project(client): -# conn = db_connect() -# project = Project('project1', 'Project One', 'A sample project') -# db_insert_project(conn, project) -# response = client.get('/projects/project1') -# assert response.status_code == 200 -# assert response.json['name'] == 'Project One' +def test_get_project(client): + conn = db_connect() + project = Project(None, 'Project One', 'A sample project') + project_id = db_insert_project(conn, project) + response = client.get(f'/projects/{project_id}') + assert response.status_code == 200 + assert response.json['name'] == 'Project One' -# def test_update_project(client): -# conn = db_connect() -# project = Project('project1', 'Project One', 'A sample project') -# db_insert_project(conn, project) -# response = client.put('/projects/project1', json={'name': 'Updated Project One'}) -# assert response.status_code == 200 -# assert response.json['name'] == 'Updated Project One' +def test_update_project(client): + conn = db_connect() + project = Project(None, 'Project One', 'A sample project') + project_id = db_insert_project(conn, project) + response = client.put(f'/projects/{project_id}', json={'name': 'Updated Project One'}) + assert response.status_code == 200 + assert response.json['name'] == 'Updated Project One' -# def test_add_project_member(client): -# conn = db_connect() -# project = Project('project1', 'Project One', 'A sample project') -# db_insert_project(conn, project) -# response = client.post('/projects/project1/members', json={'id': 'member1'}) -# assert response.status_code == 201 -# assert 'member1' in response.json['user_id'] \ No newline at end of file +def test_add_project_member(client): + conn = db_connect() + project = Project(None, 'Project One', 'A sample project') + project_id = db_insert_project(conn, project) + response = client.post(f'/projects/{project_id}/members', json={'user_name': 'johndoe'}) + assert response.status_code == 201 + assert response.json['user_name'] == 'johndoe' \ No newline at end of file diff --git a/backend/app/test_data.py b/backend/app/test_data.py deleted file mode 100644 index 8128e74..0000000 --- a/backend/app/test_data.py +++ /dev/null @@ -1,55 +0,0 @@ -import sys -import os -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from db.db import db_connect, db_insert_user, db_update_user, db_delete_user, db_get_user_by_username, db_insert_snippet, db_update_snippet, db_delete_snippet, db_get_snippet_by_id, db_filter_snippets, db_insert_comment, User, Comment, Snippet -import datetime - -def generate_test_data(): - conn = db_connect() - - # Create users - users = [ - User('johndoe', 'john@example.com', 'password123', 'John', 'Doe', 'A software developer with 10 years of experience.', ['Python', 'Flask', 'JavaScript']), - User('janedoe', 'jane@example.com', 'password123', 'Jane', 'Doe', 'A front-end developer.', ['HTML', 'CSS', 'JavaScript']), - ] - for user in users: - db_insert_user(conn, user) - - # Create snippets - snippets = [ - Snippet(None, 'print("Hello, World!")', ['example', 'hello world'], 'Python', 'johndoe'), - Snippet(None, '

Hello, World!

', ['example', 'hello world'], 'HTML', 'janedoe'), - ] - for snippet in snippets: - db_insert_snippet(conn, snippet) - - # Create comments - comments = [ - Comment(None, 1, 'janedoe', 'Great snippet!', datetime.datetime.now()), - Comment(None, 2, 'johndoe', 'Nice HTML example!', datetime.datetime.now()), - ] - for comment in comments: - db_insert_comment(conn, comment) - - # Create projects - # projects = [ - # Project(None, 'Project One', 'A sample project'), - # Project(None, 'Project Two', 'Another sample project'), - # ] - # for project in projects: - # db_insert_project(conn, project) - - # # Create project members - # project_members = [ - # ProjectMember(1, 'johndoe'), - # ProjectMember(1, 'janedoe'), - # ProjectMember(2, 'johndoe'), - # ] - # for project_member in project_members: - # db_insert_project_member(conn, project_member) - - conn.close() - -if __name__ == "__main__": - generate_test_data() - print("Test data generated successfully.") \ No newline at end of file diff --git a/backend/db/db.py b/backend/db/db.py index c0b59f6..8e24f62 100644 --- a/backend/db/db.py +++ b/backend/db/db.py @@ -259,6 +259,20 @@ def db_get_comments_by_snippet_id(conn, snippet_id): print(f"Error fetching comments: {e}") return [] +def db_get_all_comments(conn): + try: + with conn.cursor() as cursor: + select_query = sql.SQL(""" + SELECT id, snippet_id, user_name, content, created_at + FROM comments + """) + cursor.execute(select_query) + comments_data = cursor.fetchall() + return [Comment(*comment) for comment in comments_data] + except Exception as e: + print(f"Error fetching comments: {e}") + return [] + def db_insert_project(conn, project): try: with conn.cursor() as cursor: @@ -306,6 +320,40 @@ def db_delete_project(conn, project_id): print(f"Error deleting project: {e}") conn.rollback() +def db_get_all_projects(conn): + try: + with conn.cursor() as cursor: + select_query = sql.SQL(""" + SELECT id, name, description + FROM projects + """) + cursor.execute(select_query) + projects_data = cursor.fetchall() + return [Project(*project) for project in projects_data] + except Exception as e: + print(f"Error fetching projects: {e}") + return [] + +def db_get_project_by_id(conn, project_id): + try: + with conn.cursor() as cursor: + select_query = sql.SQL(""" + SELECT id, name, description + FROM projects + WHERE id = %s + """) + cursor.execute(select_query, (project_id,)) + project_data = cursor.fetchone() + if project_data: + project = Project(*project_data) + return project + else: + print("Project not found") + return None + except Exception as e: + print(f"Error fetching project: {e}") + return None + def db_insert_user_project(conn, user_project): try: with conn.cursor() as cursor: diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..2bdf050 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,6 @@ +Flask==2.0.1 +werkzeug==2.0.1 +flask-swagger-ui==4.11.1 +pytest==8.3.4 +uvicorn==0.34.0 +flask-cors==3.0.10 \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 2307839..3cac80e 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,5 +1,5 @@ # Use the official Node.js 14 image as the base image for the frontend build stage -FROM node:14.17.0 as frontend +FROM node:20 as frontend # Set the working directory inside the container to /app WORKDIR /app @@ -8,7 +8,7 @@ WORKDIR /app COPY package*.json ./ # Install the dependencies specified in package.json -RUN npm ci +RUN npm install # Copy the rest of the application code to the working directory COPY . . diff --git a/requirements.txt b/requirements.txt index 87a041f..fce4a26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ werkzeug==2.0.1 flask-swagger-ui==4.11.1 pytest==8.3.4 psycopg2==2.9.3 -flask-cors==3.0.10 \ No newline at end of file +flask-cors==3.0.10 +requests \ No newline at end of file