From 2a41cf6ee6efb324cc8783ddaa76322367f80577 Mon Sep 17 00:00:00 2001 From: Bianca Date: Mon, 2 Jan 2023 20:24:12 -0500 Subject: [PATCH 01/20] backend setup --- app/__init__.py | 4 + app/models/__init__.py | 3 + app/models/board.py | 7 ++ app/models/card.py | 13 +++ migrations/README | 1 + migrations/alembic.ini | 45 +++++++++ migrations/env.py | 96 +++++++++++++++++++ migrations/script.py.mako | 24 +++++ ...536838de9fe0_adds_board_and_card_models.py | 42 ++++++++ 9 files changed, 235 insertions(+) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/536838de9fe0_adds_board_and_card_models.py diff --git a/app/__init__.py b/app/__init__.py index 1c821436..18d13c41 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -19,6 +19,10 @@ def create_app(): # Import models here for Alembic setup # from app.models.ExampleModel import ExampleModel + from app.models.card import Card + from app.models.board import Board + + db.init_app(app) migrate.init_app(app, db) diff --git a/app/models/__init__.py b/app/models/__init__.py index e69de29b..b28b04f6 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -0,0 +1,3 @@ + + + diff --git a/app/models/board.py b/app/models/board.py index 147eb748..b4d1de57 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -1 +1,8 @@ from app import db + +class Board(db.Model): + board_id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String) + owner = db.Column(db.String) + cards = db.relationship("Card", back_populates="board", lazy=True) + \ No newline at end of file diff --git a/app/models/card.py b/app/models/card.py index 147eb748..4097b350 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -1 +1,14 @@ from app import db + +class Card(db.Model): + card_id = db.Column(db.Integer, primary_key=True) + message = db.Column(db.String) + board = db.relationship("Board", back_populates="cards", lazy=True) + # an object from the card class can accept a relationship from an object + # that's from the Board class. backpopulates signals that the specific board + # object will now display this specific card in its board.cards attribute + board_id = db.Column(db.Integer, db.ForeignKey('board.board_id'), nullable=True) + likes_count = db.Column(db.Integer) + + + \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 00000000..f8ed4801 --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 00000000..8b3fb335 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/536838de9fe0_adds_board_and_card_models.py b/migrations/versions/536838de9fe0_adds_board_and_card_models.py new file mode 100644 index 00000000..290cedac --- /dev/null +++ b/migrations/versions/536838de9fe0_adds_board_and_card_models.py @@ -0,0 +1,42 @@ +"""adds Board and Card models + +Revision ID: 536838de9fe0 +Revises: +Create Date: 2023-01-02 14:01:34.785223 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '536838de9fe0' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('board', + sa.Column('board_id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(), nullable=True), + sa.Column('owner', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('board_id') + ) + op.create_table('card', + sa.Column('card_id', sa.Integer(), nullable=False), + sa.Column('message', sa.String(), nullable=True), + sa.Column('board_id', sa.Integer(), nullable=True), + sa.Column('likes_count', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['board_id'], ['board.board_id'], ), + sa.PrimaryKeyConstraint('card_id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('card') + op.drop_table('board') + # ### end Alembic commands ### From 8b67963c78db9688da6d1173addf864a4009df71 Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Tue, 3 Jan 2023 13:44:43 -0500 Subject: [PATCH 02/20] Adds the from_dict_to_object and to_dict functions to Card model and creates the create_card route in card_routes --- app/__init__.py | 4 ++-- app/{routes.py => board_routes.py} | 0 app/card_routes.py | 33 ++++++++++++++++++++++++++++++ app/models/card.py | 13 +++++++++++- 4 files changed, 47 insertions(+), 3 deletions(-) rename app/{routes.py => board_routes.py} (100%) create mode 100644 app/card_routes.py diff --git a/app/__init__.py b/app/__init__.py index 18d13c41..b41a340e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -28,8 +28,8 @@ def create_app(): migrate.init_app(app, db) # Register Blueprints here - # from .routes import example_bp - # app.register_blueprint(example_bp) + from .card_routes import cards_bp + app.register_blueprint(cards_bp) CORS(app) return app diff --git a/app/routes.py b/app/board_routes.py similarity index 100% rename from app/routes.py rename to app/board_routes.py diff --git a/app/card_routes.py b/app/card_routes.py new file mode 100644 index 00000000..780e8256 --- /dev/null +++ b/app/card_routes.py @@ -0,0 +1,33 @@ +from flask import Blueprint, request, jsonify, make_response, abort +from app.models.card import Card +from app import db + +cards_bp = Blueprint('cards_bp', __name__, url_prefix= '/cards') + +def validate_model(cls, model_id): + try: + model_id = int(model_id) + except: + abort(make_response({"details": "Invalid Data"}, 400)) + model = cls.query.get(model_id) + + if not model: + abort(make_response({"details": f"{cls.__name__} {model_id} not found"}, 404)) + return model + +@cards_bp.route("", methods=["POST"]) +def create_card(): + request_body = request.get_json() + if len(request_body) != 1: + return {"details": "Invalid Data"}, 400 + new_card = Card.from_dict_to_object(request_body) + + db.session.add(new_card) + db.session.commit() + + return make_response(jsonify({"card": new_card.to_dict()}), 201) + + + + + diff --git a/app/models/card.py b/app/models/card.py index 4097b350..cd2fdef5 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -8,7 +8,18 @@ class Card(db.Model): # that's from the Board class. backpopulates signals that the specific board # object will now display this specific card in its board.cards attribute board_id = db.Column(db.Integer, db.ForeignKey('board.board_id'), nullable=True) - likes_count = db.Column(db.Integer) + likes_count = db.Column(db.Integer, default=0) + + def to_dict(self): + card_as_dict = {} + card_as_dict["card_id"] = self.card_id + card_as_dict["message"] = self.message + card_as_dict["likes_count"] = self.likes_count + return card_as_dict + @classmethod + def from_dict_to_object(cls,data_dict): + return cls(message=data_dict["message"]) + \ No newline at end of file From 2169b3ba5ada878cba3d2f2bb2c1c71c40948cd4 Mon Sep 17 00:00:00 2001 From: Bianca Date: Tue, 3 Jan 2023 14:18:37 -0500 Subject: [PATCH 03/20] created class method and to dict function for Board object, and finished post route for board --- app/__init__.py | 3 +++ app/board_routes.py | 41 ++++++++++++++++++++++++++++++++++++++++- app/card_routes.py | 2 ++ app/models/board.py | 21 ++++++++++++++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index b41a340e..86026239 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -30,6 +30,9 @@ def create_app(): # Register Blueprints here from .card_routes import cards_bp app.register_blueprint(cards_bp) + + from .board_routes import board_bp + app.register_blueprint(board_bp) CORS(app) return app diff --git a/app/board_routes.py b/app/board_routes.py index 480b8c4b..d5ee46b6 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -1,4 +1,43 @@ from flask import Blueprint, request, jsonify, make_response +from .card_routes import validate_model +from app.models.card import Card +from app.models.board import Board from app import db -# example_bp = Blueprint('example_bp', __name__) +board_bp = Blueprint('board_bp', __name__, url_prefix='/boards') + + + + +@board_bp.route("", methods=["POST"]) +def create_board(): + request_body = request.get_json() + if len(request_body) != 2: + return {"details": "Invalid Data"}, 400 + new_board = Board.from_dict_to_object(request_body) + + db.session.add(new_board) + db.session.commit() + + return make_response(jsonify({"board": new_board.to_dict()}), 201) + +# @tasks_bp.route("", methods=["GET"]) +# def read_all_tasks(): +# title_query = request.args.get('title') +# sort_query = request.args.get('sort') + +# if title_query: +# tasks = Task.query.filter_by(title=title_query) + +# if sort_query == "asc": +# tasks = Task.query.order_by(Task.title.asc()) + +# if sort_query == "desc": +# tasks = Task.query.order_by(Task.title.desc()) + +# if not title_query and not sort_query: +# tasks = Task.query.all() +# tasks_response = [task.to_dict() for task in tasks] +# return jsonify(tasks_response) + + diff --git a/app/card_routes.py b/app/card_routes.py index 780e8256..af879745 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -31,3 +31,5 @@ def create_card(): + + diff --git a/app/models/board.py b/app/models/board.py index b4d1de57..0fb1b18b 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -1,8 +1,27 @@ from app import db +from app.models.card import Card class Board(db.Model): board_id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String) owner = db.Column(db.String) cards = db.relationship("Card", back_populates="board", lazy=True) - \ No newline at end of file + + + def to_dict(self, cards=False): + if not self.cards and cards==False: + return { + 'board_id': self.board_id, + 'title': self.title, + 'owner': self.owner + } + else: + return { + 'board_id': self.board_id, + 'title': self.title, + 'cards': [card.to_dict() for card in self.cards], + 'owner': self.owner + } + @classmethod + def from_dict_to_object(cls,data_dict): + return cls(title=data_dict["title"], owner=data_dict['owner']) \ No newline at end of file From 5ff4a3ff950538630b458d923db01e0232de982a Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Tue, 3 Jan 2023 14:36:34 -0500 Subject: [PATCH 04/20] Adds the read_all_boards route to board_routes --- app/board_routes.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index d5ee46b6..5740fdae 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -21,23 +21,11 @@ def create_board(): return make_response(jsonify({"board": new_board.to_dict()}), 201) -# @tasks_bp.route("", methods=["GET"]) -# def read_all_tasks(): -# title_query = request.args.get('title') -# sort_query = request.args.get('sort') - -# if title_query: -# tasks = Task.query.filter_by(title=title_query) - -# if sort_query == "asc": -# tasks = Task.query.order_by(Task.title.asc()) - -# if sort_query == "desc": -# tasks = Task.query.order_by(Task.title.desc()) - -# if not title_query and not sort_query: -# tasks = Task.query.all() -# tasks_response = [task.to_dict() for task in tasks] -# return jsonify(tasks_response) +@board_bp.route("", methods=["GET"]) +def read_all_boards(): + boards = Board.query.all() + board_response = [board.to_dict() for board in boards] + + return make_response(jsonify(board_response), 200) From 291fa5aa2da116006d3f715b9501d86b830a478a Mon Sep 17 00:00:00 2001 From: Bianca Date: Tue, 3 Jan 2023 14:56:09 -0500 Subject: [PATCH 05/20] added read all card in the board routes --- app/board_routes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/board_routes.py b/app/board_routes.py index 5740fdae..ca422f4a 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -28,4 +28,8 @@ def read_all_boards(): return make_response(jsonify(board_response), 200) - +@board_bp.route("//cards", methods=["GET"]) +def read_all_cards(board_id): + board = validate_model(Board, board_id) + boards_response = [card.to_dict() for card in board.cards] + return(jsonify(boards_response)) \ No newline at end of file From cdf40a6effc9b61f5a17c7a8e2bde7b38b49c0c9 Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Tue, 3 Jan 2023 15:57:39 -0500 Subject: [PATCH 06/20] Adds add_card_to_board route, still testing --- app/board_routes.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/app/board_routes.py b/app/board_routes.py index ca422f4a..99947d39 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -32,4 +32,35 @@ def read_all_boards(): def read_all_cards(board_id): board = validate_model(Board, board_id) boards_response = [card.to_dict() for card in board.cards] - return(jsonify(boards_response)) \ No newline at end of file + return(jsonify(boards_response)) + + +@board_bp.route("//cards", methods=["POST"]) +def create_card(board_id): + request_body = request.get_json() + if len(request_body) != 1: + return {"details": "Invalid Data"}, 400 + new_card = Card.from_dict_to_object(request_body) + + board = validate_model(Board,board_id) + board = board.to_dict(cards=True) + board.cards.append(new_card) + + db.session.add(new_card) + db.session.commit() + + return make_response(jsonify({{'board_id': board.board_id, 'messages': [card.message for card in board['cards']]}})) + + # return make_response(jsonify({"card": new_card.to_dict()}), 201) + +# @board_bp.route("//cards", methods=["POST"]) +# def add_card_to_board(board_id): + +# board = validate_model(Board, board_id) +# new_card = create_card() + +# request_body = request.get_json() +# board.cards += Card.query.get(card_id) + +# db.session.commit() +# return make_response(jsonify({{'board_id': board.board_id, 'messages': [card.message for card in board.cards]}})) From cf1bad3a0d5777ab9510c48deca47d0b130b2d6b Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Wed, 4 Jan 2023 13:03:16 -0500 Subject: [PATCH 07/20] creates the add_card_to_board function in board_routes to link the cards to boards --- app/board_routes.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index 99947d39..4df6955c 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -36,20 +36,20 @@ def read_all_cards(board_id): @board_bp.route("//cards", methods=["POST"]) -def create_card(board_id): +def add_card_to_board(board_id): request_body = request.get_json() if len(request_body) != 1: return {"details": "Invalid Data"}, 400 - new_card = Card.from_dict_to_object(request_body) + # new_card = Card.from_dict_to_object(request_body) board = validate_model(Board,board_id) - board = board.to_dict(cards=True) - board.cards.append(new_card) + card_id = request_body["card_id"] + # board = board.to_dict(cards=True) + board.cards.append(Card.query.get(card_id)) - db.session.add(new_card) db.session.commit() - return make_response(jsonify({{'board_id': board.board_id, 'messages': [card.message for card in board['cards']]}})) + return make_response(jsonify({'board_id': board.board_id, 'card': [card.message for card in board.cards]}), 200) # return make_response(jsonify({"card": new_card.to_dict()}), 201) From a24667cb6553f08a83541743a8269744635354fa Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Wed, 4 Jan 2023 13:31:15 -0500 Subject: [PATCH 08/20] Adds the delete_card and delete_board functions to card_routes and board_routes and successfully deletes a card and a board --- app/board_routes.py | 22 ++++++++-------------- app/card_routes.py | 8 ++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index 4df6955c..735e0a89 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -40,27 +40,21 @@ def add_card_to_board(board_id): request_body = request.get_json() if len(request_body) != 1: return {"details": "Invalid Data"}, 400 - # new_card = Card.from_dict_to_object(request_body) board = validate_model(Board,board_id) card_id = request_body["card_id"] - # board = board.to_dict(cards=True) + board.cards.append(Card.query.get(card_id)) db.session.commit() - return make_response(jsonify({'board_id': board.board_id, 'card': [card.message for card in board.cards]}), 200) - - # return make_response(jsonify({"card": new_card.to_dict()}), 201) + return make_response(jsonify({'board_id': board.board_id, 'cards': [card.message for card in board.cards]}), 200) -# @board_bp.route("//cards", methods=["POST"]) -# def add_card_to_board(board_id): +@board_bp.route("/", methods=["DELETE"]) +def delete_board(id): + board = validate_model(Board, id) -# board = validate_model(Board, board_id) -# new_card = create_card() - -# request_body = request.get_json() -# board.cards += Card.query.get(card_id) + db.session.delete(board) + db.session.commit() -# db.session.commit() -# return make_response(jsonify({{'board_id': board.board_id, 'messages': [card.message for card in board.cards]}})) + return make_response(jsonify({"details": f"Board {id} '{board.title}' successfully deleted"}),200) \ No newline at end of file diff --git a/app/card_routes.py b/app/card_routes.py index af879745..df802e91 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -27,7 +27,15 @@ def create_card(): return make_response(jsonify({"card": new_card.to_dict()}), 201) +@cards_bp.route("/", methods=["DELETE"]) +def delete_card(id): + card = validate_model(Card, id) + + db.session.delete(card) + db.session.commit() + + return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}),200) From 2ba4f99fa51c70f5d4bf27b35c6161536394f27e Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Thu, 5 Jan 2023 13:00:03 -0500 Subject: [PATCH 09/20] Adds the read_specific_board route to board_routes, working succesfully on postman --- app/board_routes.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/board_routes.py b/app/board_routes.py index 735e0a89..11f226a9 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -21,6 +21,12 @@ def create_board(): return make_response(jsonify({"board": new_board.to_dict()}), 201) +@board_bp.route("/", methods=["GET"]) +def read_specific_board(id): + board = validate_model(Board, id) + response_body = board.to_dict() + return make_response(jsonify(response_body), 200) + @board_bp.route("", methods=["GET"]) def read_all_boards(): boards = Board.query.all() From da4217b99f021ac5e2ed0708676d2403b0aec598 Mon Sep 17 00:00:00 2001 From: Bianca Date: Thu, 5 Jan 2023 13:09:40 -0500 Subject: [PATCH 10/20] added character limit to our message post requests --- app/card_routes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/card_routes.py b/app/card_routes.py index df802e91..e981a9fa 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -18,8 +18,11 @@ def validate_model(cls, model_id): @cards_bp.route("", methods=["POST"]) def create_card(): request_body = request.get_json() - if len(request_body) != 1: + message = request_body["message"] + if not message or len(request_body)!= 1: return {"details": "Invalid Data"}, 400 + if len(message) > 40: + return {"details": "You have gone over the 40 character message limit."} new_card = Card.from_dict_to_object(request_body) db.session.add(new_card) From 3feb8f6c1830d50c879d8ef8479586c009a23fbe Mon Sep 17 00:00:00 2001 From: Bianca Date: Thu, 5 Jan 2023 13:44:10 -0500 Subject: [PATCH 11/20] created a one_card fixture --- app/__init__.py | 17 ++++++++++++++--- tests/conftest.py | 17 +++++++++++++++++ tests/test_routes.py | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 86026239..6f88dc90 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -10,12 +10,21 @@ load_dotenv() -def create_app(): +def create_app(test_config=None): app = Flask(__name__) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False - app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( + + if test_config is None: + app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SQLALCHEMY_DATABASE_URI") + else: + # used for running testing routes + app.config["TESTING"] = True + app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( + "SQLALCHEMY_TEST_DATABASE_URI") + + # Import models here for Alembic setup # from app.models.ExampleModel import ExampleModel @@ -30,9 +39,11 @@ def create_app(): # Register Blueprints here from .card_routes import cards_bp app.register_blueprint(cards_bp) - + from .board_routes import board_bp app.register_blueprint(board_bp) + + app.config['CORS_HEADERS'] = 'Content-Type' CORS(app) return app diff --git a/tests/conftest.py b/tests/conftest.py index 2b7296d5..c1ce2036 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,8 @@ import pytest from app import create_app +from app.models.card import Card from app import db +from flask.signals import request_finished @pytest.fixture @@ -8,6 +10,10 @@ def app(): # create the app with a test config dictionary app = create_app({"TESTING": True}) + @request_finished.connect_via(app) + def expire_session(sender, response, **extra): + db.session.remove() + with app.app_context(): db.create_all() yield app @@ -20,3 +26,14 @@ def app(): @pytest.fixture def client(app): return app.test_client() + + +# This fixture gets called in every test that +# references "one_card" +# This fixture creates a card and saves it in the database +@pytest.fixture +def one_card(app): + new_card= Card( + message="Go on my daily walk 🏞") + db.session.add(new_card) + db.session.commit() \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py index e69de29b..9010fb52 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -0,0 +1,15 @@ +from app.models.card import Card +import pytest + + +def test_delete_one_card(client, one_card): + response = client.delete("/cards/1") + response_body = response.get_json() + + assert response.status_code == 200 + assert "details" in response_body + assert response_body == { + "details": 'Card 1 "Go on my daily walk 🏞" successfully deleted' + } + assert Card.query.get(1) == None + From 11cd9a7abc29e182e3db1ac9ef0e7517cdca4607 Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Thu, 5 Jan 2023 14:51:02 -0500 Subject: [PATCH 12/20] Adds the test routes and test fixtures for necessary card and board routes --- requirements.txt | 4 +++ tests/conftest.py | 18 ++++++++++ tests/test_routes.py | 85 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 76f1f000..fc6d2b5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,11 @@ alembic==1.5.4 attrs==21.2.0 autopep8==1.5.5 +blinker==1.5 certifi==2020.12.5 chardet==4.0.0 click==7.1.2 +coverage==7.0.3 Flask==1.1.2 Flask-Cors==3.0.10 Flask-Migrate==2.6.0 @@ -22,6 +24,7 @@ py==1.11.0 pycodestyle==2.6.0 pyparsing==2.4.7 pytest==7.1.1 +pytest-cov==4.0.0 python-dateutil==2.8.1 python-dotenv==0.15.0 python-editor==1.0.4 @@ -29,5 +32,6 @@ requests==2.25.1 six==1.15.0 SQLAlchemy==1.3.23 toml==0.10.2 +tomli==2.0.1 urllib3==1.26.4 Werkzeug==1.0.1 diff --git a/tests/conftest.py b/tests/conftest.py index c1ce2036..e538a107 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ import pytest from app import create_app from app.models.card import Card +from app.models.board import Board from app import db from flask.signals import request_finished @@ -36,4 +37,21 @@ def one_card(app): new_card= Card( message="Go on my daily walk 🏞") db.session.add(new_card) + db.session.commit() + + +@pytest.fixture +def one_board(app): + new_board= Board( + title="workout", + owner="bianca" + ) + db.session.add(new_board) + db.session.commit() + +@pytest.fixture +def add_card_to_board(app, one_card, one_board): + card = Card.query.first() + board = Board.query.first() + board.cards.append(card) db.session.commit() \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py index 9010fb52..1ad53d1d 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,7 +1,25 @@ from app.models.card import Card +from app.models.board import Board import pytest +# @pytest.mark.skip(reason="No way to test this feature yet") +def test_create_card(client): + response = client.post("/cards", json={ + "message": "test message" + }) + response_body = response.get_json() + assert response_body == { + "card": { + "card_id": 1, + "message": "test message", + "likes_count": 0 + } + } + new_card = Card.query.get(1) + assert new_card + assert new_card.message == "test message" +# @pytest.mark.skip(reason="No way to test this feature yet") def test_delete_one_card(client, one_card): response = client.delete("/cards/1") response_body = response.get_json() @@ -9,7 +27,72 @@ def test_delete_one_card(client, one_card): assert response.status_code == 200 assert "details" in response_body assert response_body == { - "details": 'Card 1 "Go on my daily walk 🏞" successfully deleted' + "details": "Card 1 'Go on my daily walk 🏞' successfully deleted" } assert Card.query.get(1) == None +# @pytest.mark.skip(reason="No way to test this feature yet") +def test_create_board(client): + response= client.post("/boards", json={ + "title": "workout", + "owner": "bianca" + }) + response_body = response.get_json() + + assert response.status_code == 201 + assert "board" in response_body + assert response_body == { + "board":{ + "board_id": 1, + "title": "workout", + "owner": "bianca" + } + } +def test_get_one_board(client, one_board): + response = client.get("/boards/1") + response_body = response.get_json() + + assert response.status_code == 200 + assert response_body == { + "board_id": 1, + "title": "workout", + "owner": "bianca" + } + +def test_delete_one_board(client,one_board): + response = client.delete("/boards/1") + response_body = response.get_json() + + assert response.status_code == 200 + assert response_body == { + "details": "Board 1 'workout' successfully deleted" + } + assert Board.query.get(1) == None + +def test_add_card_to_board(client, one_board, one_card): + response = client.post("/boards/1/cards", json={ + "card_id": 1 + }) + response_body = response.get_json() + + assert response.status_code == 200 + assert "board_id" in response_body + assert response_body == { + "board_id": 1, + "cards": ['Go on my daily walk 🏞' + ] + } + +def test_delete_card_from_board(client,one_board, one_card): + new_card = client.post("/boards/1/cards", json={ + "card_id": 1 + }) + response = client.delete("/cards/1", json={ + "card_id": 1 + }) + response_body = response.get_json() + + assert response.status_code == 200 + assert response_body == { + "details": "Card 1 'Go on my daily walk 🏞' successfully deleted" + } \ No newline at end of file From 098ee89ead2459fd32cbc0642c5aa992a6548bec Mon Sep 17 00:00:00 2001 From: Bianca Date: Mon, 16 Jan 2023 18:21:19 -0500 Subject: [PATCH 13/20] modified add_card_to_board to post and link a card under one post request --- app/board_routes.py | 44 +++++++++++++++++++++++++++++++++++++++----- app/card_routes.py | 36 ++++++++++++++++++------------------ 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index 11f226a9..e5566e5e 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -41,21 +41,54 @@ def read_all_cards(board_id): return(jsonify(boards_response)) + + @board_bp.route("//cards", methods=["POST"]) def add_card_to_board(board_id): request_body = request.get_json() - if len(request_body) != 1: + message = request_body["message"] + if not message or len(request_body)!= 1: return {"details": "Invalid Data"}, 400 + if len(message) > 40: + return {"details": "You have gone over the 40 character message limit."} + new_card = Card.from_dict_to_object(request_body) - board = validate_model(Board,board_id) - card_id = request_body["card_id"] + db.session.add(new_card) - board.cards.append(Card.query.get(card_id)) + board = validate_model(Board,board_id) + board.cards.append(new_card) db.session.commit() return make_response(jsonify({'board_id': board.board_id, 'cards': [card.message for card in board.cards]}), 200) +# old version below +# @board_bp.route("//cards", methods=["POST"]) +# def add_card_to_board(board_id): +# request_body = request.get_json() +# if len(request_body) != 1: +# return {"details": "Invalid Data"}, 400 + +# board = validate_model(Board,board_id) +# card_id = request_body["card_id"] + +# board.cards.append(Card.query.get(card_id)) + +# db.session.commit() + +# return make_response(jsonify({'board_id': board.board_id, 'cards': [card.message for card in board.cards]}), 200) + +# THIS CODE BELOW IS TAKEN FROM CARD_ROUTES AND MODIFIED +@board_bp.route("//cards/", methods=["DELETE"]) +def delete_card(board_id, card_id): + board = validate_model(Board, board_id) + card = validate_model(Card, card_id) + + db.session.delete(card) + db.session.commit() + + return make_response(jsonify({"details": f"Card {card_id} '{card.message}' successfully deleted"}),200) + @board_bp.route("/", methods=["DELETE"]) def delete_board(id): board = validate_model(Board, id) @@ -63,4 +96,5 @@ def delete_board(id): db.session.delete(board) db.session.commit() - return make_response(jsonify({"details": f"Board {id} '{board.title}' successfully deleted"}),200) \ No newline at end of file + return make_response(jsonify({"details": f"Board {id} '{board.title}' successfully deleted"}), 200) + diff --git a/app/card_routes.py b/app/card_routes.py index e981a9fa..1fcbdc53 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -15,30 +15,30 @@ def validate_model(cls, model_id): abort(make_response({"details": f"{cls.__name__} {model_id} not found"}, 404)) return model -@cards_bp.route("", methods=["POST"]) -def create_card(): - request_body = request.get_json() - message = request_body["message"] - if not message or len(request_body)!= 1: - return {"details": "Invalid Data"}, 400 - if len(message) > 40: - return {"details": "You have gone over the 40 character message limit."} - new_card = Card.from_dict_to_object(request_body) +# @cards_bp.route("", methods=["POST"]) +# def create_card(): +# request_body = request.get_json() +# message = request_body["message"] +# if not message or len(request_body)!= 1: +# return {"details": "Invalid Data"}, 400 +# if len(message) > 40: +# return {"details": "You have gone over the 40 character message limit."} +# new_card = Card.from_dict_to_object(request_body) - db.session.add(new_card) - db.session.commit() +# db.session.add(new_card) +# db.session.commit() - return make_response(jsonify({"card": new_card.to_dict()}), 201) +# return make_response(jsonify({"card": new_card.to_dict()}), 201) -@cards_bp.route("/", methods=["DELETE"]) -def delete_card(id): +# @cards_bp.route("/", methods=["DELETE"]) +# def delete_card(id): - card = validate_model(Card, id) +# card = validate_model(Card, id) - db.session.delete(card) - db.session.commit() +# db.session.delete(card) +# db.session.commit() - return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}),200) +# return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}),200) From e718e3de37c8a226cb63910717e41aa343e10fc7 Mon Sep 17 00:00:00 2001 From: Bianca Date: Tue, 17 Jan 2023 13:14:06 -0500 Subject: [PATCH 14/20] modified models to delete on cascade --- app/board_routes.py | 2 ++ app/models/board.py | 2 +- app/models/card.py | 2 +- ..._adds_board_and_card_models.py => 84d051d20b4c_.py} | 10 +++++----- 4 files changed, 9 insertions(+), 7 deletions(-) rename migrations/versions/{536838de9fe0_adds_board_and_card_models.py => 84d051d20b4c_.py} (83%) diff --git a/app/board_routes.py b/app/board_routes.py index e5566e5e..6ddc5bf9 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -91,6 +91,8 @@ def delete_card(board_id, card_id): @board_bp.route("/", methods=["DELETE"]) def delete_board(id): + #delete cards first + #cascading delete board = validate_model(Board, id) db.session.delete(board) diff --git a/app/models/board.py b/app/models/board.py index 0fb1b18b..c419e9a7 100644 --- a/app/models/board.py +++ b/app/models/board.py @@ -5,7 +5,7 @@ class Board(db.Model): board_id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String) owner = db.Column(db.String) - cards = db.relationship("Card", back_populates="board", lazy=True) + cards = db.relationship("Card", back_populates="board", lazy=True, passive_deletes=True) def to_dict(self, cards=False): diff --git a/app/models/card.py b/app/models/card.py index cd2fdef5..09365f0b 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -7,7 +7,7 @@ class Card(db.Model): # an object from the card class can accept a relationship from an object # that's from the Board class. backpopulates signals that the specific board # object will now display this specific card in its board.cards attribute - board_id = db.Column(db.Integer, db.ForeignKey('board.board_id'), nullable=True) + board_id = db.Column(db.Integer, db.ForeignKey('board.board_id', ondelete='CASCADE'), nullable=True) likes_count = db.Column(db.Integer, default=0) def to_dict(self): diff --git a/migrations/versions/536838de9fe0_adds_board_and_card_models.py b/migrations/versions/84d051d20b4c_.py similarity index 83% rename from migrations/versions/536838de9fe0_adds_board_and_card_models.py rename to migrations/versions/84d051d20b4c_.py index 290cedac..0e98aa89 100644 --- a/migrations/versions/536838de9fe0_adds_board_and_card_models.py +++ b/migrations/versions/84d051d20b4c_.py @@ -1,8 +1,8 @@ -"""adds Board and Card models +"""empty message -Revision ID: 536838de9fe0 +Revision ID: 84d051d20b4c Revises: -Create Date: 2023-01-02 14:01:34.785223 +Create Date: 2023-01-17 13:13:29.346053 """ from alembic import op @@ -10,7 +10,7 @@ # revision identifiers, used by Alembic. -revision = '536838de9fe0' +revision = '84d051d20b4c' down_revision = None branch_labels = None depends_on = None @@ -29,7 +29,7 @@ def upgrade(): sa.Column('message', sa.String(), nullable=True), sa.Column('board_id', sa.Integer(), nullable=True), sa.Column('likes_count', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['board_id'], ['board.board_id'], ), + sa.ForeignKeyConstraint(['board_id'], ['board.board_id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('card_id') ) # ### end Alembic commands ### From 4c1491a556c607c54b0f0ca92fd6c4e6c52de65a Mon Sep 17 00:00:00 2001 From: Bianca Date: Tue, 17 Jan 2023 13:21:13 -0500 Subject: [PATCH 15/20] deleted commented code and moved delete card to card routes --- app/board_routes.py | 26 -------------------------- app/card_routes.py | 26 ++++++-------------------- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index 6ddc5bf9..298f7bc7 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -62,32 +62,6 @@ def add_card_to_board(board_id): return make_response(jsonify({'board_id': board.board_id, 'cards': [card.message for card in board.cards]}), 200) -# old version below -# @board_bp.route("//cards", methods=["POST"]) -# def add_card_to_board(board_id): -# request_body = request.get_json() -# if len(request_body) != 1: -# return {"details": "Invalid Data"}, 400 - -# board = validate_model(Board,board_id) -# card_id = request_body["card_id"] - -# board.cards.append(Card.query.get(card_id)) - -# db.session.commit() - -# return make_response(jsonify({'board_id': board.board_id, 'cards': [card.message for card in board.cards]}), 200) - -# THIS CODE BELOW IS TAKEN FROM CARD_ROUTES AND MODIFIED -@board_bp.route("//cards/", methods=["DELETE"]) -def delete_card(board_id, card_id): - board = validate_model(Board, board_id) - card = validate_model(Card, card_id) - - db.session.delete(card) - db.session.commit() - - return make_response(jsonify({"details": f"Card {card_id} '{card.message}' successfully deleted"}),200) @board_bp.route("/", methods=["DELETE"]) def delete_board(id): diff --git a/app/card_routes.py b/app/card_routes.py index 1fcbdc53..31a3b954 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -15,30 +15,16 @@ def validate_model(cls, model_id): abort(make_response({"details": f"{cls.__name__} {model_id} not found"}, 404)) return model -# @cards_bp.route("", methods=["POST"]) -# def create_card(): -# request_body = request.get_json() -# message = request_body["message"] -# if not message or len(request_body)!= 1: -# return {"details": "Invalid Data"}, 400 -# if len(message) > 40: -# return {"details": "You have gone over the 40 character message limit."} -# new_card = Card.from_dict_to_object(request_body) -# db.session.add(new_card) -# db.session.commit() +@cards_bp.route("/", methods=["DELETE"]) +def delete_card(id): -# return make_response(jsonify({"card": new_card.to_dict()}), 201) + card = validate_model(Card, id) -# @cards_bp.route("/", methods=["DELETE"]) -# def delete_card(id): + db.session.delete(card) + db.session.commit() -# card = validate_model(Card, id) - -# db.session.delete(card) -# db.session.commit() - -# return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}),200) + return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}),200) From 38da985d7b88ce120486e09c964bb9575758f4a0 Mon Sep 17 00:00:00 2001 From: Jaime Mitchell Date: Wed, 18 Jan 2023 12:32:10 -0500 Subject: [PATCH 16/20] Clair's suggestions after we spoke about ssome logic that could be holding up the front?? --- app/board_routes.py | 21 +++++++++------------ app/card_routes.py | 31 ++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index 298f7bc7..60780249 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -7,8 +7,6 @@ board_bp = Blueprint('board_bp', __name__, url_prefix='/boards') - - @board_bp.route("", methods=["POST"]) def create_board(): request_body = request.get_json() @@ -21,12 +19,14 @@ def create_board(): return make_response(jsonify({"board": new_board.to_dict()}), 201) + @board_bp.route("/", methods=["GET"]) def read_specific_board(id): board = validate_model(Board, id) response_body = board.to_dict() return make_response(jsonify(response_body), 200) + @board_bp.route("", methods=["GET"]) def read_all_boards(): boards = Board.query.all() @@ -34,6 +34,7 @@ def read_all_boards(): return make_response(jsonify(board_response), 200) + @board_bp.route("//cards", methods=["GET"]) def read_all_cards(board_id): board = validate_model(Board, board_id) @@ -41,13 +42,11 @@ def read_all_cards(board_id): return(jsonify(boards_response)) - - @board_bp.route("//cards", methods=["POST"]) def add_card_to_board(board_id): request_body = request.get_json() message = request_body["message"] - if not message or len(request_body)!= 1: + if not message: return {"details": "Invalid Data"}, 400 if len(message) > 40: return {"details": "You have gone over the 40 character message limit."} @@ -55,22 +54,20 @@ def add_card_to_board(board_id): db.session.add(new_card) - board = validate_model(Board,board_id) +# this add commit seperated?? + board = validate_model(Board, board_id) board.cards.append(new_card) - - db.session.commit() - return make_response(jsonify({'board_id': board.board_id, 'cards': [card.message for card in board.cards]}), 200) + db.session.commit() +# do we need to changet to card + return make_response(jsonify({'board_id': board.board_id, 'cards': [card.to_dict() for card in board.cards]}), 200) @board_bp.route("/", methods=["DELETE"]) def delete_board(id): - #delete cards first - #cascading delete board = validate_model(Board, id) db.session.delete(board) db.session.commit() return make_response(jsonify({"details": f"Board {id} '{board.title}' successfully deleted"}), 200) - diff --git a/app/card_routes.py b/app/card_routes.py index 31a3b954..ed53fa3b 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -2,7 +2,8 @@ from app.models.card import Card from app import db -cards_bp = Blueprint('cards_bp', __name__, url_prefix= '/cards') +cards_bp = Blueprint('cards_bp', __name__, url_prefix='/cards') + def validate_model(cls, model_id): try: @@ -12,8 +13,25 @@ def validate_model(cls, model_id): model = cls.query.get(model_id) if not model: - abort(make_response({"details": f"{cls.__name__} {model_id} not found"}, 404)) - return model + abort(make_response( + {"details": f"{cls.__name__} {model_id} not found"}, 404)) + return model + + +@cards_bp.route("", methods=["POST"]) +def create_card(): + request_body = request.get_json() + message = request_body["message"] + if not message: + return {"details": "Invalid Data"}, 400 + if len(message) > 40: + return {"details": "You have gone over the 40 character message limit."} + new_card = Card.from_dict_to_object(request_body) + + db.session.add(new_card) + db.session.commit() + + return make_response(jsonify({"card": new_card.to_dict()}), 201) @cards_bp.route("/", methods=["DELETE"]) @@ -24,9 +42,4 @@ def delete_card(id): db.session.delete(card) db.session.commit() - return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}),200) - - - - - + return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}), 200) From 7d4ab4336b430f707cf375e8ec79326407e818ef Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Wed, 18 Jan 2023 13:42:05 -0500 Subject: [PATCH 17/20] Adds the like_card_with_id function to the PATCH route for liking cards --- app/card_routes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/card_routes.py b/app/card_routes.py index ed53fa3b..d9d0b8cd 100644 --- a/app/card_routes.py +++ b/app/card_routes.py @@ -43,3 +43,11 @@ def delete_card(id): db.session.commit() return make_response(jsonify({"details": f"Card {id} '{card.message}' successfully deleted"}), 200) + +@cards_bp.route("/", methods=["PATCH"]) +def like_card_with_id(id): + card = validate_model(Card, id) + card.likes_count += 1 + + db.session.commit() + return jsonify(card.to_dict()) From 460885774f531a88c3ac56e82170af04524d6ab2 Mon Sep 17 00:00:00 2001 From: Bianca Date: Wed, 18 Jan 2023 16:38:53 -0500 Subject: [PATCH 18/20] added error message for when either owner or title is left blank --- app/board_routes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/board_routes.py b/app/board_routes.py index 60780249..a96e3d99 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -10,8 +10,13 @@ @board_bp.route("", methods=["POST"]) def create_board(): request_body = request.get_json() + title = request_body['title'] + owner = request_body['owner'] if len(request_body) != 2: return {"details": "Invalid Data"}, 400 + + if not owner or not title: + return {"details": "Title and/or Owner was left blank"} new_board = Board.from_dict_to_object(request_body) db.session.add(new_board) From d2611beb9bf10bcc6abc89a37f54a532e533b0e5 Mon Sep 17 00:00:00 2001 From: Alia Athman Date: Thu, 19 Jan 2023 13:13:29 -0500 Subject: [PATCH 19/20] Refactored our tests to pass with our new changes in routes --- app/board_routes.py | 4 ++-- tests/test_routes.py | 19 ++----------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/app/board_routes.py b/app/board_routes.py index a96e3d99..24de8946 100644 --- a/app/board_routes.py +++ b/app/board_routes.py @@ -59,12 +59,12 @@ def add_card_to_board(board_id): db.session.add(new_card) -# this add commit seperated?? + board = validate_model(Board, board_id) board.cards.append(new_card) db.session.commit() -# do we need to changet to card + return make_response(jsonify({'board_id': board.board_id, 'cards': [card.to_dict() for card in board.cards]}), 200) diff --git a/tests/test_routes.py b/tests/test_routes.py index 1ad53d1d..447ec62d 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -71,7 +71,7 @@ def test_delete_one_board(client,one_board): def test_add_card_to_board(client, one_board, one_card): response = client.post("/boards/1/cards", json={ - "card_id": 1 + "message": 'Go on my daily walk 🏞' }) response_body = response.get_json() @@ -79,20 +79,5 @@ def test_add_card_to_board(client, one_board, one_card): assert "board_id" in response_body assert response_body == { "board_id": 1, - "cards": ['Go on my daily walk 🏞' - ] - } - -def test_delete_card_from_board(client,one_board, one_card): - new_card = client.post("/boards/1/cards", json={ - "card_id": 1 - }) - response = client.delete("/cards/1", json={ - "card_id": 1 - }) - response_body = response.get_json() - - assert response.status_code == 200 - assert response_body == { - "details": "Card 1 'Go on my daily walk 🏞' successfully deleted" + "cards": [{'card_id': 2, "message": "Go on my daily walk 🏞", "likes_count": 0}] } \ No newline at end of file From b27820046b1e62e58157e64d4413223cd4e4d9fc Mon Sep 17 00:00:00 2001 From: Jaime Mitchell Date: Sat, 21 Jan 2023 14:47:00 -0500 Subject: [PATCH 20/20] adding card_as_dict boardId --- app/models/card.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/card.py b/app/models/card.py index 09365f0b..b215572a 100644 --- a/app/models/card.py +++ b/app/models/card.py @@ -15,6 +15,7 @@ def to_dict(self): card_as_dict["card_id"] = self.card_id card_as_dict["message"] = self.message card_as_dict["likes_count"] = self.likes_count + card_as_dict["board_id"] = self.board_id return card_as_dict @classmethod def from_dict_to_object(cls,data_dict):