-
Notifications
You must be signed in to change notification settings - Fork 61
C-17 Sharks Raha-Jamie #29
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
base: main
Are you sure you want to change the base?
Changes from all commits
7591102
e255899
158388d
830bb71
d064ba1
3a1b6bd
5ea6ee6
7cac49b
4b15180
2e95f91
b36bad8
07e2456
764d2e4
3442e0b
6a75029
1afac30
5d750cd
92d2c42
3335311
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 |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| web: gunicorn 'app:create_app()' |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,35 @@ | ||||||
| from flask import Flask | ||||||
| from flask_sqlalchemy import SQLAlchemy | ||||||
| from flask_migrate import Migrate | ||||||
| from dotenv import load_dotenv | ||||||
| import os | ||||||
|
|
||||||
| db = SQLAlchemy() | ||||||
| migrate = Migrate() | ||||||
| load_dotenv() | ||||||
|
|
||||||
| def create_app(test_config=None): | ||||||
| app = Flask(__name__) | ||||||
|
|
||||||
| if not test_config: | ||||||
| app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | ||||||
| app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get("SQLALCHEMY_DATABASE_URI") | ||||||
|
|
||||||
| else: | ||||||
| app.config["TESTING"] = True | ||||||
| app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False | ||||||
| app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( | ||||||
| "SQLALCHEMY_TEST_DATABASE_URI") | ||||||
|
|
||||||
|
|
||||||
| from app.models.planet import Planet | ||||||
|
|
||||||
| db.init_app(app) | ||||||
| migrate.init_app(app, db) | ||||||
|
|
||||||
| from .routes.routes import planet_bp | ||||||
|
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. We may want to change the import to And
Suggested change
|
||||||
| app.register_blueprint(planet_bp) | ||||||
|
|
||||||
| return app | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| from app import db | ||
|
|
||
| class Moon(db.Model): | ||
| id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
| name = db.Column(db.String) | ||
| planet_id = db.Column(db.Integer, db.ForeignKey('planet.id')) | ||
| planet = db.relationship("Planet", back_populates="moons") | ||
|
|
||
| def to_json(self): | ||
| return { | ||
| "id": self.id, | ||
| "name": self.name | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from app import db | ||
|
|
||
| class Planet(db.Model): | ||
|
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. 👍 |
||
| id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||
| name = db.Column(db.String) | ||
| description = db.Column(db.String) | ||
| moons = db.Column(db.Integer) | ||
|
Comment on lines
+5
to
+7
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. Should we be able to make planets with no name? To prevent our API from creating a planet with a Additionally, shouldn't name = db.Column(db.String, nullable=False)
description = db.Column(db.String)
moons = db.Relationship('Moon', back_populates='planet') |
||
|
|
||
| def to_json(self): | ||
| return { | ||
| "id": self.id, | ||
| "name": self.name, | ||
| "description": self.description, | ||
| "moons": self.moons | ||
| } | ||
|
Comment on lines
+9
to
+15
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 file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Helper function: | ||
| from flask import abort, make_response, jsonify | ||
| from app.models.planet import Planet | ||
|
|
||
| def validate_planet(planet_id): | ||
| try: | ||
| planet_id = int(planet_id) | ||
| except: | ||
| abort(make_response(jsonify(f"planet {planet_id} invalid"), 400)) | ||
|
|
||
| planet = Planet.query.get(planet_id) | ||
| if not planet: | ||
| abort(make_response(jsonify(f"planet {planet_id} not found"), 404)) | ||
| return planet | ||
|
Comment on lines
+1
to
+14
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. Helpers look great! 👍 We may want to consider making the validation code as generic as possible to process any class and id or renaming this file to A more generic validation helper method would look like: from flask import make_response, abort
def validate_object(cls, id):
try:
id = int(id)
except:
return abort(make_response({"message": f"{cls} {id} is invalid"}, 400))
obj = cls.query.get(id)
if not obj:
abort(make_response({"message": f"{cls} {id} not found"},404))
return obj |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # from app import db | ||
| # from app.models.planet import Planet | ||
| # from app.models.moon import Moon | ||
| # from flask import Blueprint, jsonify, abort, make_response, request | ||
| # from .helper import validate_planet | ||
|
|
||
| # moons_bp = Blueprint("moons", __name__, url_prefix="/moons") | ||
|
|
||
| # do we even need this | ||
|
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. Nope, you don't need this file unless you're planning on adding future enhancements for moons 🌚 ✨ |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| from app import db | ||
| from app.models.planet import Planet | ||
| from flask import Blueprint, jsonify, abort, make_response, request | ||
| from .helper import validate_planet | ||
| from app.models.moon import Moon | ||
|
|
||
| planets_bp = Blueprint("planets_bp", __name__, url_prefix="/planets") | ||
|
|
||
| @planets_bp.route("", methods=["POST"]) | ||
| def create_planets(): | ||
| request_body = request.get_json() | ||
|
|
||
| try: | ||
| new_planet = Planet(name = request_body["name"], description = request_body["description"]) | ||
| db.session.add(new_planet) | ||
| db.session.commit() | ||
| except: | ||
| abort(make_response(jsonify({"message":f"invalid input"}), 400)) | ||
| return make_response(jsonify(f"Planet {new_planet.name} successfully created"), 201) | ||
|
Comment on lines
+9
to
+19
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. 👍 There are a few parts to this function:
We can move the try/except into a helper method and create the planet instance through a |
||
|
|
||
|
|
||
| @planets_bp.route("", methods=["GET"]) | ||
| def read_all_planets(): | ||
|
|
||
|
|
||
| name_query = request.args.get("name") | ||
| # moons_query = request.args.get("moons") | ||
| if name_query: | ||
| planets = Planet.query.filter_by(name = name_query) | ||
| # elif moons_query: | ||
| # planets = Planet.query.filter_by(moons = moons_query) | ||
| else: | ||
| planets = Planet.query.all() | ||
| planets_response = [] | ||
| try: | ||
| for planet in planets: | ||
| planets_response.append(planet.to_json()) | ||
|
|
||
| except: | ||
| abort(make_response(jsonify(f"planet not found"), 404)) | ||
|
Comment on lines
+39
to
+40
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. Will this |
||
| return make_response(jsonify(planets_response),200) | ||
|
|
||
|
|
||
|
|
||
|
|
||
| @planets_bp.route("/<planet_id>", methods=["GET"]) | ||
| def read_one_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
| return make_response(planet.to_json(), 200) | ||
|
Comment on lines
+46
to
+49
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. Nice helper! Also, if a view function returns a dictionary ( @planets_bp.route("/<planet_id>", methods=["GET"])
def read_one_planet(planet_id):
planet = validate_planet(planet_id)
return planet.to_json(), 200Here's the documentation with more info: |
||
|
|
||
|
|
||
| @planets_bp.route("/<planet_id>", methods=["PUT"]) | ||
| def update_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
|
|
||
| request_body = request.get_json() | ||
| try: | ||
| planet.name = request_body["name"] | ||
| planet.description = request_body["description"] | ||
| # planet.moons = request_body["moons"] | ||
| except: | ||
| abort(make_response(jsonify(f"invalid data"), 400)) | ||
|
Comment on lines
+57
to
+62
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. It'd be helpful to also include what error would occur in the except clause. I imagine it'd be a keyError. |
||
|
|
||
| db.session.commit() | ||
|
|
||
| return jsonify(planet.to_json(), 200) | ||
|
|
||
| @planets_bp.route("/<planet_id>", methods=["DELETE"]) | ||
| def delete_a_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
|
|
||
| db.session.delete(planet) | ||
| db.session.commit() | ||
|
|
||
| return make_response(jsonify(f"planet{planet_id} successfully deleted"), 200) | ||
|
Comment on lines
+68
to
+75
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.
return jsonify(f"planet{planet_id} successfully deleted"), 200 |
||
|
|
||
| @planets_bp.route("/<planet_id>/moons", methods=["GET"]) | ||
| def read_moons_for_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
| moons_response = [] | ||
| for moon in planet.moons: | ||
| moons_response.append(moon.to_json()) | ||
| return make_response(jsonify(moons_response), 200) | ||
|
Comment on lines
+77
to
+83
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. We can use list comprehension to create moons_response = [moon.to_json() for moon in planet.moons] |
||
|
|
||
| @planets_bp.route("/<planet_id>/moons", methods=["POST"]) | ||
| def write_moon_to_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
| request_body = request.get_json() | ||
| new_moon = Moon(name=request_body["name"], planet=planet) | ||
|
|
||
| db.session.add(new_moon) | ||
| db.session.commit() | ||
|
|
||
| return make_response(jsonify(f"Moon {new_moon.name} of the planet {new_moon.planet.name} successfully created"), 201) | ||
|
Comment on lines
+85
to
+94
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. 👍 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. The previous comment about not needing |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| from app import db | ||
|
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. Comments from |
||
| from app.models.planet import Planet | ||
| from flask import Blueprint, jsonify, abort, make_response, request | ||
| from .helper import validate_planet | ||
|
|
||
| planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") | ||
|
|
||
| @planet_bp.route("", methods=["POST"]) | ||
| def create_planets(): | ||
| request_body = request.get_json() | ||
|
|
||
| try: | ||
| new_planet = Planet(name = request_body["name"], description = request_body["description"], moons = request_body["moons"]) | ||
| db.session.add(new_planet) | ||
| db.session.commit() | ||
| except: | ||
| abort(make_response(jsonify({"message":f"invalid input"}), 400)) | ||
| return make_response(jsonify(f"Planet {new_planet.name} successfully created"), 201) | ||
|
|
||
|
|
||
| @planet_bp.route("", methods=["GET"]) | ||
| def read_all_planets(): | ||
|
|
||
|
|
||
| name_query = request.args.get("name") | ||
| moons_query = request.args.get("moons") | ||
| if name_query: | ||
| planets = Planet.query.filter_by(name = name_query) | ||
| elif moons_query: | ||
| planets = Planet.query.filter_by(moons = moons_query) | ||
| else: | ||
| planets = Planet.query.all() | ||
| planets_response = [] | ||
| try: | ||
| for planet in planets: | ||
| planets_response.append(planet.to_json()) | ||
|
|
||
| except: | ||
| abort(make_response(jsonify(f"planet not found"), 404)) | ||
| return make_response(jsonify(planets_response),200) | ||
|
|
||
|
|
||
|
|
||
|
|
||
| @planet_bp.route("/<planet_id>", methods=["GET"]) | ||
| def read_one_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
| return make_response(planet.to_json(), 200) | ||
|
|
||
|
|
||
| @planet_bp.route("/<planet_id>", methods=["PUT"]) | ||
| def update_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
|
|
||
| request_body = request.get_json() | ||
| try: | ||
| planet.name = request_body["name"] | ||
| planet.description = request_body["description"] | ||
| planet.moons = request_body["moons"] | ||
| except: | ||
| abort(make_response(jsonify(f"invalid data"), 400)) | ||
|
|
||
| db.session.commit() | ||
|
|
||
| return jsonify(planet.to_json(), 200) | ||
|
|
||
| @planet_bp.route("/<planet_id>", methods=["DELETE"]) | ||
| def delete_a_planet(planet_id): | ||
| planet = validate_planet(planet_id) | ||
|
|
||
| db.session.delete(planet) | ||
| db.session.commit() | ||
|
|
||
| return make_response(jsonify(f"planet{planet_id} successfully deleted"), 200) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import pytest | ||
| from app import create_app | ||
| from app import db | ||
| from flask.signals import request_finished | ||
| from app.models.planet import Planet | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def app(): | ||
| 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 | ||
|
|
||
| with app.app_context(): | ||
| db.drop_all() | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def client(app): | ||
| return app.test_client() | ||
|
|
||
| @pytest.fixture | ||
| def two_saved_planets(app): | ||
| Mercury = Planet(name = "Mercury", description = "This is the first planet", moons = 2) | ||
| Venus = Planet(name = "Venus", description = "named after the Roman goddess of love and beauty", moons = 0) | ||
|
|
||
| db.session.add_all([Mercury,Venus]) | ||
| db.session.commit() | ||
|
Comment on lines
+29
to
+34
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. 👍 Nice work! My one critique is about |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
|
|
||
|
|
||
| def test_get_all_planets_with_no_records(client): | ||
| # Act | ||
| response = client.get("/planets") | ||
| response_body = response.get_json() | ||
|
|
||
| # Assert | ||
| assert response.status_code == 200 | ||
| assert response_body == [] | ||
|
|
||
| def test_get_book_by_id(client, two_saved_planets): | ||
|
|
||
| response = client.get('/planets/2') | ||
| response_body = response.get_json() | ||
|
|
||
| assert response.status_code == 200 | ||
| assert response_body == { | ||
| "id": 2, | ||
| "name": "Venus", | ||
| "description": "named after the Roman goddess of love and beauty", | ||
| "moons" : 0 | ||
| } | ||
|
|
||
|
|
||
| def test_create_one_planet(client): | ||
| # Act | ||
| response = client.post("/planets", json={ | ||
| "name": "Asimov", | ||
| "description": "This planet named after Isaac Asimov, the other of Lucky Starr book series", | ||
| "moons" : 4 | ||
| }) | ||
| response_body = response.get_json() | ||
|
|
||
| # Assert | ||
| assert response.status_code == 201 | ||
| assert response_body == "Planet Asimov successfully created" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
|
|
||
|
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. 👍 |
||
|
|
||
| def test_get_all_planets_with_no_records(client): | ||
| # Act | ||
| response = client.get("/planets") | ||
| response_body = response.get_json() | ||
|
|
||
| # Assert | ||
| assert response.status_code == 200 | ||
| assert response_body == [] | ||
|
|
||
| def test_get_book_by_id(client, two_saved_planets): | ||
|
|
||
| response = client.get('/planets/2') | ||
| response_body = response.get_json() | ||
|
|
||
| assert response.status_code == 200 | ||
| assert response_body == { | ||
| "id": 2, | ||
| "name": "Venus", | ||
| "description": "named after the Roman goddess of love and beauty", | ||
| "moons" : 0 | ||
| } | ||
|
|
||
|
|
||
| def test_create_one_planet(client): | ||
| # Act | ||
| response = client.post("/planets", json={ | ||
| "name": "Asimov", | ||
| "description": "This planet named after Isaac Asimov, the other of Lucky Starr book series", | ||
| "moons" : 4 | ||
| }) | ||
| response_body = response.get_json() | ||
|
|
||
| # Assert | ||
| assert response.status_code == 201 | ||
| assert response_body == "Planet Asimov successfully created" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Generic single-database configuration. |
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.
👍