-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kunzite 2 - Angie, Danica, Alejandra #7
base: main
Are you sure you want to change the base?
Changes from all commits
27917fa
f450ac9
7fd194c
dbe51e1
e8c5f48
8bf346c
b4567de
1aacfbd
18cfa14
4c80fed
d09388c
c007434
0e55047
0c8b2b3
355fd4e
9d8f406
2b35970
dafe8c5
be7a3cb
301e9bd
72432cf
23508a1
bf2252c
5a6b121
e975fd3
260ae01
a5fae3f
1fdaabb
879bf46
a59ad8e
b1b1429
61551bb
8927913
cc13383
28f8026
a114936
a26580d
fe545c9
07483c1
607eb78
1e117b5
e8a3280
8906eef
f2cb975
841aeea
8d54545
37f1f74
0f0eccb
1a54aa2
f342c28
55f2d69
a589073
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,34 @@ | ||
from app import db | ||
from flask import abort, make_response, jsonify | ||
|
||
class Board(db.Model): | ||
board_id = db.Column(db.Integer, primary_key=True) | ||
title = db.Column(db.String, nullable=False) | ||
owner = db.Column(db.String, nullable=False) | ||
cards = db.relationship("Card", back_populates="board") | ||
|
||
@classmethod | ||
def from_dict(cls, board_data): | ||
try: | ||
if board_data["title"] == "": | ||
raise ValueError | ||
|
||
if board_data["owner"] == "": | ||
raise ValueError | ||
|
||
new_board = cls( | ||
title=board_data["title"], | ||
owner=board_data["owner"] | ||
) | ||
except (ValueError, KeyError): | ||
abort(make_response(jsonify({"details": "Invalid data"}), 400)) | ||
|
||
return new_board | ||
|
||
def to_dict(self): | ||
board_as_dict = {} | ||
board_as_dict["board_id"]=self.board_id | ||
board_as_dict["title"]=self.title | ||
board_as_dict["owner"]=self.owner | ||
|
||
return board_as_dict |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,28 @@ | ||
from app import db | ||
from flask import abort, make_response, jsonify | ||
|
||
class Card(db.Model): | ||
card_id = db.Column(db.Integer, primary_key=True) | ||
message = db.Column(db.String, nullable=False) | ||
likes_count = db.Column(db.Integer, default=0) | ||
board_id = db.Column(db.Integer, db.ForeignKey("board.board_id")) | ||
board = db.relationship("Board", back_populates="cards") | ||
|
||
@classmethod | ||
def from_dict(cls, card_data): | ||
try: | ||
if card_data["message"] == "" or len(card_data["message"]) > 40: | ||
raise ValueError | ||
new_card = cls(message=card_data["message"]) | ||
except (ValueError, TypeError, KeyError): | ||
abort(make_response(jsonify({"details": "Invalid data"}), 400)) | ||
|
||
return new_card | ||
|
||
def to_dict(self): | ||
return dict( | ||
card_id=self.card_id, | ||
message=self.message, | ||
likes_count=self.likes_count, | ||
board_id=self.board_id, | ||
) |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
from flask import Blueprint, request, jsonify, make_response | ||
from app import db | ||
from app.models.card import Card | ||
from app.models.board import Board | ||
from .routes_helpers import validate_model | ||
|
||
board_bp = Blueprint("boards", __name__, url_prefix="/boards") | ||
|
||
@board_bp.route("", methods=["POST"]) | ||
def create_board(): | ||
request_body = request.get_json() | ||
|
||
new_board = Board.from_dict(request_body) | ||
|
||
db.session.add(new_board) | ||
db.session.commit() | ||
|
||
return make_response(jsonify(new_board.to_dict()), 201) | ||
|
||
|
||
@board_bp.route("/<board_id>/cards", methods=["POST"]) | ||
def add_card_to_board(board_id): | ||
board = validate_model(Board, board_id) | ||
|
||
request_body = request.get_json() | ||
|
||
new_card = Card.from_dict(request_body) | ||
|
||
new_card.board_id = board.board_id | ||
|
||
db.session.add(new_card) | ||
db.session.commit() | ||
|
||
return make_response(jsonify(new_card.to_dict()), 201) | ||
|
||
|
||
@board_bp.route("", methods=["GET"]) | ||
def get_all_boards(): | ||
|
||
boards = Board.query.all() | ||
|
||
boards_response = [board.to_dict() for board in boards] | ||
|
||
def get_id(entry): | ||
return entry['board_id'] | ||
|
||
boards_response.sort(key=get_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than using |
||
|
||
return jsonify(boards_response), 200 | ||
|
||
|
||
@board_bp.route("/<board_id>", methods=["GET"]) | ||
def get_one_board(board_id): | ||
board = validate_model(Board, board_id) | ||
|
||
response_body = { | ||
"board_id": board.board_id, | ||
"title": board.title, | ||
"owner": board.owner, | ||
} | ||
|
||
return jsonify(response_body), 200 | ||
|
||
|
||
@board_bp.route("/<board_id>/cards", methods=["GET"]) | ||
def get_cards_of_one_board(board_id): | ||
board = validate_model(Board, board_id) | ||
|
||
cards_response = [] | ||
for card in board.cards: | ||
cards_response.append(card.to_dict()) | ||
|
||
def get_id(entry): | ||
return entry['card_id'] | ||
|
||
cards_response.sort(key=get_id) | ||
|
||
return jsonify(cards_response), 200 | ||
|
||
|
||
@board_bp.route("/<board_id>", methods=["DELETE"]) | ||
def delete_board(board_id): | ||
|
||
board = validate_model(Board, board_id) | ||
|
||
db.session.delete(board) | ||
db.session.commit() | ||
|
||
return make_response(jsonify({"message":f"Board {board_id} successfully deleted"}), 200) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from flask import Blueprint, jsonify, make_response, request | ||
from app import db | ||
from app.models.card import Card | ||
from .routes_helpers import validate_model | ||
|
||
cards_bp = Blueprint("cards", __name__, url_prefix="/cards") | ||
|
||
|
||
@cards_bp.route("/<card_id>/like", methods=["PUT"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels more like a PATCH method to me, since we're not replacing the entire resource. |
||
def update_likes_card(card_id): | ||
card = validate_model(Card, card_id) | ||
|
||
card.likes_count += 1 | ||
|
||
db.session.commit() | ||
|
||
return make_response(jsonify(card.to_dict()), 200) | ||
|
||
|
||
@cards_bp.route("/<card_id>", methods=["DELETE"]) | ||
def delete_card(card_id): | ||
card = validate_model(Card, card_id) | ||
|
||
db.session.delete(card) | ||
db.session.commit() | ||
|
||
return make_response( | ||
jsonify({"message": f"Card {card_id} successfully deleted"}), 200 | ||
) | ||
|
||
|
||
@cards_bp.route("/<card_id>", methods=["PUT"]) | ||
def update_card_msg(card_id): | ||
card = validate_model(Card, card_id) | ||
request_body = request.get_json() | ||
|
||
card.message = request_body["message"] | ||
|
||
db.session.commit() | ||
|
||
return jsonify({"message": f"Card {card_id} successfully updated"}), 200 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from flask import abort, make_response | ||
|
||
def validate_model(cls, model_id): | ||
try: | ||
model_id = int(model_id) | ||
except: | ||
abort(make_response({"message":f"{cls.__name__} {model_id} invalid"}, 400)) | ||
|
||
model = cls.query.get(model_id) | ||
|
||
if not model: | ||
abort(make_response({"message":f"{cls.__name__} {model_id} not found"}, 404)) | ||
|
||
return model |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Generic single-database configuration. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conceptually, the three flask functions here are at a different "layer" of the backend logic. Models are at a pretty low layer (they're far from the incoming request) while the endpoints are at a higher layer (they are aware of flask and endpoints and all that). We should try to minimize unnecessary dependencies where possible, with special emphasis on avoiding cases where a lower layer depends on a higher layer.
Remember that one of the key benefits of errors is that they allow us to separate the concerns of reporting that something has gone wrong, and resolving that situation. This lets us move the resolution code to places that have more context.
Here, we should avoid importing
abort
,make_response
, andjsonify
fromflask
and not hanlde the error here. We should let the errors escape the function, and put the error handling closer to the endpoint code. This needn't be directly in and endpoint. Rather, it could be in a helper function that "lives" at the same layer as endpoints.