diff --git a/app/api/items.py b/app/api/items.py index 7caa5cfa0599cb16fbf934ea48266958c9ea95a1..1eb8471a3cdafc538f849ad6e2ab27ec4d9bce51 100644 --- a/app/api/items.py +++ b/app/api/items.py @@ -9,11 +9,13 @@ This module implements the items API. :license: BSD 2-Clause, see LICENSE for more details. """ +import sqlalchemy as sa from flask import (current_app, Blueprint, jsonify, request) from flask_jwt_extended import create_access_token, jwt_required from flask_ldap3_login import AuthenticationResponseStatus from ..extensions import ldap_manager, db -from ..models import User +from ..models import User, Item +from .. import utils bp = Blueprint('api', __name__) @@ -42,13 +44,29 @@ def login(): @bp.route('/items') @jwt_required def get_items(): - sql = """SELECT item.id, item.name, manufacturer.name, model.name, location.name, status.name - FROM item - INNER JOIN manufacturer ON manufacturer.id = item.manufacturer_id - INNER JOIN model ON model.id = item.model_id - INNER JOIN location ON location.id = item.location_id - INNER JOIN status ON status.id = item.status_id - """ - result = db.engine.execute(sql) - data = [[item for item in row] for row in result] - return jsonify(data=data) + # TODO: add pagination + items = Item.query.order_by(Item._created) + data = [item.to_dict() for item in items] + return jsonify({'items': data}) + + +@bp.route('/items', methods=['POST']) +@jwt_required +def create_item(): + data = request.get_json() + if data is None: + raise utils.InventoryError('Body should be a JSON object') + if 'serial_number' not in data: + raise utils.InventoryError('serial_number is mandatory') + try: + item = Item(**data) + except TypeError as e: + message = str(e).replace('__init__() got an ', '') + raise utils.InventoryError(message) + db.session.add(item) + try: + db.session.commit() + except sa.exc.IntegrityError as e: + db.session.rollback() + raise utils.InventoryError('IntegrityError', status_code=409) + return jsonify(item.to_dict()), 201 diff --git a/app/models.py b/app/models.py index 02f31736690e674698c8c569549e6cc1b529d123..055de442b51dafcaea00f14e3487e4d3424a8d44 100644 --- a/app/models.py +++ b/app/models.py @@ -184,11 +184,25 @@ class Item(db.Model): # All arguments must be optional for this class to work with flask-admin! self.serial_number = serial_number self.name = name - self.manufacturer = manufacturer - self.model = model - self.location = location - self.status = status + self.manufacturer = utils.convert_to_model(manufacturer, Manufacturer) + self.model = utils.convert_to_model(model, Model) + self.location = utils.convert_to_model(location, Location) + self.status = utils.convert_to_model(status, Status) self.hash = utils.compute_hash(serial_number) def __str__(self): return self.serial_number + + def to_dict(self): + return { + 'id': self.id, + 'serial_number': self.serial_number, + 'name': self.name, + 'hash': self.hash, + 'manufacturer': utils.format_field(self.manufacturer), + 'model': utils.format_field(self.model), + 'location': utils.format_field(self.location), + 'status': utils.format_field(self.status), + 'updated': utils.format_field(self._updated), + 'created': utils.format_field(self._created), + } diff --git a/app/utils.py b/app/utils.py index dddef7b632a53dd158d71c91d7343fc71c32d8c9..f67893de7e450167f71fb7de8100ef88001a4249 100644 --- a/app/utils.py +++ b/app/utils.py @@ -65,3 +65,21 @@ def format_field(field): if isinstance(field, datetime.datetime): return field.strftime('%Y-%m-%d %H:%M') return str(field) + + +def convert_to_model(item, model): + """Convert item to an instance of model + + Allow to convert a string to an instance of model + Raise an exception if the given name is not found + + :returns: an instance of model + """ + if item is None: + return None + if not isinstance(item, model): + instance = model.query.filter_by(name=item).first() + if instance is None: + raise InventoryError(f'{item} is not a valid {model.__name__.lower()}') + return instance + return item