diff --git a/AUTHORS b/AUTHORS index 64b26acdc14..7a9ff72f4e4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,4 +3,5 @@ Jennifer Huang <133@holbertonschool.com> Alexa Orrico <210@holbertonschool.com> -Joann Vuong <130@holbertonschool.com> +Joann Vuong <130@holbertonschool.com> +Mark Manani diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/api/v1/__init__.py b/api/v1/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/api/v1/app.py b/api/v1/app.py new file mode 100644 index 00000000000..fbfabf8820f --- /dev/null +++ b/api/v1/app.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +"""Flask application""" + +from flask import Flask, jsonify +from models import storage +from api.v1.views import app_views +import os + +app = Flask(__name__) +app.register_blueprint(app_views) + + +@app.teardown_appcontext +def teardown(exception): + """Method to handle teardown, closing the storage""" + storage.close() + + +@app.errorhandler(404) +def not_found(error): + """Handler for 404 errors that returns a JSON response""" + return jsonify({"error": "Not found"}), 404 + + +if __name__ == "__main__": + host = os.getenv('HBNB_API_HOST', '0.0.0.0') + port = int(os.getenv('HBNB_API_PORT', 5000)) + app.run(host=host, port=port, threaded=True) diff --git a/api/v1/views/__init__.py b/api/v1/views/__init__.py new file mode 100644 index 00000000000..7ffc6db7ce1 --- /dev/null +++ b/api/v1/views/__init__.py @@ -0,0 +1,10 @@ +#!/usr/bin/python3 +"""Blueprint setup""" + +from flask import Blueprint + +app_views = Blueprint('app_views', __name__, url_prefix='/api/v1') + +# Import routes so they are registered with the Blueprint +from api.v1.views.index import * +from api.v1.views.states import * diff --git a/api/v1/views/index.py b/api/v1/views/index.py new file mode 100644 index 00000000000..f5df235282e --- /dev/null +++ b/api/v1/views/index.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +"""Index route for the API""" + +from flask import jsonify +from models import storage +from api.v1.views import app_views + + +@app_views.route('/status', methods=['GET']) +def status(): + """Returns the status of the API""" + return jsonify({"status": "OK"}) + + +@app_views.route('/stats', methods=['GET'], strict_slashes=False) +def stats(): + """Retrieves the number of each objects by type""" + stats = { + "amenities": storage.count("Amenity"), + "cities": storage.count("City"), + "places": storage.count("Place"), + "reviews": storage.count("Review"), + "states": storage.count("State"), + "users": storage.count("User") + } + return jsonify(stats) diff --git a/api/v1/views/states.py b/api/v1/views/states.py new file mode 100644 index 00000000000..c5c74c843c3 --- /dev/null +++ b/api/v1/views/states.py @@ -0,0 +1,64 @@ +#!/usr/bin/python3 +"""State view for handling all default RESTful API actions""" +from flask import jsonify, abort, request, make_response +from models import storage +from models.state import State +from api.v1.views import app_views + + +@app_views.route('/states', methods=['GET'], strict_slashes=False) +def get_states(): + """Retrieves the list of all State objects""" + states = storage.all(State).values() + return jsonify([state.to_dict() for state in states]) + + +@app_views.route('/states/', methods=['GET'], strict_slashes=False) +def get_state(state_id): + """Retrieves a State object""" + state = storage.get(State, state_id) + if not state: + abort(404) + return jsonify(state.to_dict()) + + +@app_views.route('states/', methods=['DELETE'], strict_slashes=False) +def delete_state(state_id): + """Deletes a State object""" + state = storage.get(State, state_id) + if not state: + abort(404) + storage.delete(state) + storage.save() + return jsonify({}), 200 + + +@app_views.route('/states', methods=['POST'], strict_slashes=False) +def create_state(): + """Creates a State""" + if not request.json: + abort(400, description="Not a JSON") + if 'name' not in request.json(force=True, silent=True): + abort(400, description="Missing name") + new_state = State(**request.get_json()) + storage.new(new_state) + storage.save() + return jsonify(new_state.to_dict()), 201 + + +@app_views.route('/states/', methods=['PUT'], strict_slashes=False) +def update_state(state_id): + """Updates a State object""" + state = storage.get(State, state_id) + if not state: + abort(404) + if not request.json(): + abort(400, description="Not a JSON") + + ignore_keys = ['id', 'created_at', 'updated_at'] + data = request.get_json() + for key, value in data.items(): + if key not in ignore_keys: + setattr(state, key, value) + storage.save() + return jsonify(state.to_dict()), 200 diff --git a/models/engine/db_storage.py b/models/engine/db_storage.py index b8e7d291e6f..91565dd820c 100755 --- a/models/engine/db_storage.py +++ b/models/engine/db_storage.py @@ -74,3 +74,14 @@ def reload(self): def close(self): """call remove() method on the private session attribute""" self.__session.remove() + + def get(self, cls, id): + """Retrieve one object""" + if cls and id: + key = "{}.{}".format(cls.__name__, id) + return self.all(cls).get(key, None) + return None + + def count(self, cls=None): + """Count the number of objects in storage""" + return len(self.all(cls)) diff --git a/models/engine/file_storage.py b/models/engine/file_storage.py index c8cb8c1764d..acfa988bba7 100755 --- a/models/engine/file_storage.py +++ b/models/engine/file_storage.py @@ -68,3 +68,14 @@ def delete(self, obj=None): def close(self): """call reload() method for deserializing the JSON file to objects""" self.reload() + + def get(self, cls, id): + """Retrieve one object""" + if cls and id: + key = "{}.{}".format(cls.__name__, id) + return self.all(cls).get(key, None) + return None + + def count(self, cls=None): + """Count the number of objects in storage""" + return len(self.all(cls)) diff --git a/tests/test_models/test_engine/test_db_storage.py b/tests/test_models/test_engine/test_db_storage.py index 766e625b5af..2a8aa9060b9 100755 --- a/tests/test_models/test_engine/test_db_storage.py +++ b/tests/test_models/test_engine/test_db_storage.py @@ -67,6 +67,35 @@ def test_dbs_func_docstrings(self): self.assertTrue(len(func[1].__doc__) >= 1, "{:s} method needs a docstring".format(func[0])) + def test_count(self): + """Test the count method.""" + all_objects_count = storage.count() + state_objects_count = storage.count(State) + city_objects_count = storage.count(City) + + self.assertEqual(all_objects_count, 2) + self.assertEqual(state_objects_count, 1) + self.assertEqual(city_objects_count, 1) + + # Adding another state and testing the count again + new_state = State(name="Nevada") + new_state.save() + self.assertEqual(storage.count(State), 2) + new_state.delete() + + def test_get(self): + """Test the get method.""" + state = storage.get(State, self.state.id) + self.assertIsNotNone(state) + self.assertEqual(state.id, self.state.id) + + city = storage.get(City, self.city.id) + self.assertIsNotNone(city) + self.assertEqual(city.id, self.city.id) + + non_existent = storage.get(State, "non-existent-id") + self.assertIsNone(non_existent) + class TestFileStorage(unittest.TestCase): """Test the FileStorage class""" diff --git a/tests/test_models/test_engine/test_file_storage.py b/tests/test_models/test_engine/test_file_storage.py index 1474a34fec0..88f9ee4f37b 100755 --- a/tests/test_models/test_engine/test_file_storage.py +++ b/tests/test_models/test_engine/test_file_storage.py @@ -67,6 +67,32 @@ def test_fs_func_docstrings(self): self.assertTrue(len(func[1].__doc__) >= 1, "{:s} method needs a docstring".format(func[0])) + def test_count(self): + """Test the count method.""" + all_objects_count = storage.count() + state_objects_count = storage.count(State) + city_objects_count = storage.count(City) + + self.assertEqual(all_objects_count, 2) + self.assertEqual(state_objects_count, 1) + self.assertEqual(city_objects_count, 1) + + # Adding another state and testing the count again + new_state = State(name="Nevada") + new_state.save() + self.assertEqual(storage.count(State), 2) + new_state.delete() + + def test_get(self): + """Test the get method.""" + state = storage.get(State, self.state.id) + self.assertIsNotNone(state) + self.assertEqual(state.id, self.state.id) + + city = storage.get(City, self.city.id) + self.assertIsNotNone(city) + self.assertEqual(city.id, self.city.id) + class TestFileStorage(unittest.TestCase): """Test the FileStorage class"""