From 269923ba288d11ff81ee3efdb626909fc435f547 Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Thu, 8 Feb 2018 14:54:08 +0000 Subject: [PATCH] Add Organisations endpoints As part of this we also needed to add: - schemas for validation - serialize method for Organisation model --- app/__init__.py | 4 ++ app/models.py | 9 +++ app/organisation/__init__.py | 0 app/organisation/organisation_schema.py | 21 ++++++ app/organisation/rest.py | 57 ++++++++++++++++ tests/app/organisation/test_rest.py | 90 +++++++++++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 app/organisation/__init__.py create mode 100644 app/organisation/organisation_schema.py create mode 100644 app/organisation/rest.py create mode 100644 tests/app/organisation/test_rest.py diff --git a/app/__init__.py b/app/__init__.py index 51c98808d..79cc48cf3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -107,6 +107,7 @@ def register_blueprint(application): from app.authentication.auth import requires_admin_auth, requires_auth, requires_no_auth from app.letters.rest import letter_job from app.billing.rest import billing_blueprint + from app.organisation.rest import organisation_blueprint service_blueprint.before_request(requires_admin_auth) application.register_blueprint(service_blueprint, url_prefix='/service') @@ -177,6 +178,9 @@ def register_blueprint(application): service_callback_blueprint.before_request(requires_admin_auth) application.register_blueprint(service_callback_blueprint) + organisation_blueprint.before_request(requires_admin_auth) + application.register_blueprint(organisation_blueprint, url_prefix='/organisations') + def register_v2_blueprints(application): from app.v2.inbound_sms.get_inbound_sms import v2_inbound_sms_blueprint as get_inbound_sms diff --git a/app/models.py b/app/models.py index 6dbc64cd5..61d402414 100644 --- a/app/models.py +++ b/app/models.py @@ -311,6 +311,15 @@ class Organisation(db.Model): created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow) + def serialize(self): + serialized = { + "id": str(self.id), + "name": self.name, + "active": self.active, + } + + return serialized + class AnnualBilling(db.Model): __tablename__ = "annual_billing" diff --git a/app/organisation/__init__.py b/app/organisation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/organisation/organisation_schema.py b/app/organisation/organisation_schema.py new file mode 100644 index 000000000..8640167b9 --- /dev/null +++ b/app/organisation/organisation_schema.py @@ -0,0 +1,21 @@ +post_create_organisation_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "POST organisation schema", + "type": "object", + "properties": { + "name": {"type": "string"}, + "active": {"type": ["boolean", "null"]} + }, + "required": ["name"] +} + +post_update_organisation_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "POST organisation schema", + "type": "object", + "properties": { + "name": {"type": ["string", "null"]}, + "active": {"type": ["boolean", "null"]} + }, + "required": [] +} diff --git a/app/organisation/rest.py b/app/organisation/rest.py new file mode 100644 index 000000000..659c073ae --- /dev/null +++ b/app/organisation/rest.py @@ -0,0 +1,57 @@ +from flask import Blueprint, jsonify, request + +from app.dao.organisation_dao import ( + dao_create_organisation, + dao_get_organisations, + dao_get_organisation_by_id, + dao_update_organisation, +) +from app.errors import register_errors +from app.models import Organisation +from app.organisation.organisation_schema import ( + post_create_organisation_schema, + post_update_organisation_schema, +) +from app.schema_validation import validate + +organisation_blueprint = Blueprint('organisation', __name__) +register_errors(organisation_blueprint) + + +@organisation_blueprint.route('', methods=['GET']) +def get_organisations(): + organisations = [ + org.serialize() for org in dao_get_organisations() + ] + + return jsonify(organisations) + + +@organisation_blueprint.route('/', methods=['GET']) +def get_organisation_by_id(organisation_id): + organisation = dao_get_organisation_by_id(organisation_id) + return jsonify(organisation.serialize()) + + +@organisation_blueprint.route('', methods=['POST']) +def create_organisation(): + data = request.get_json() + + validate(data, post_create_organisation_schema) + + organisation = Organisation(**data) + dao_create_organisation(organisation) + + return jsonify(organisation.serialize()), 201 + + +@organisation_blueprint.route('/', methods=['POST']) +def update_organisation(organisation_id): + data = request.get_json() + + validate(data, post_update_organisation_schema) + + fetched_organisation = dao_get_organisation_by_id(organisation_id) + dao_update_organisation(fetched_organisation, **data) + + return jsonify(fetched_organisation.serialize()), 200 diff --git a/tests/app/organisation/test_rest.py b/tests/app/organisation/test_rest.py new file mode 100644 index 000000000..949f828ef --- /dev/null +++ b/tests/app/organisation/test_rest.py @@ -0,0 +1,90 @@ +from app.models import Organisation +from tests.app.db import create_organisation + + +def test_get_all_organisations(admin_request, notify_db_session): + create_organisation(name='inactive org', active=False) + create_organisation(name='active org') + + response = admin_request.get( + 'organisation.get_organisations', + _expected_status=200 + ) + + assert len(response) == 2 + assert response[0]['name'] == 'active org' + assert response[0]['active'] is True + assert response[1]['name'] == 'inactive org' + assert response[1]['active'] is False + + +def test_get_organisation_by_id(admin_request, notify_db_session): + org = create_organisation() + + response = admin_request.get( + 'organisation.get_organisation_by_id', + _expected_status=200, + organisation_id=org.id + ) + + assert set(response.keys()) == {'id', 'name', 'active'} + assert response['id'] == str(org.id) + assert response['name'] == 'test_org_1' + assert response['active'] is True + + +def test_post_create_organisation(admin_request, notify_db_session): + data = { + 'name': 'test organisation', + 'active': True + } + + response = admin_request.post( + 'organisation.create_organisation', + _data=data, + _expected_status=201 + ) + + organisation = Organisation.query.all() + + assert data['name'] == response['name'] + assert data['active'] == response['active'] + assert len(organisation) == 1 + + +def test_post_create_organisation_with_missing_name_gives_validation_error(admin_request, notify_db_session): + data = { + 'active': False + } + + response = admin_request.post( + 'organisation.create_organisation', + _data=data, + _expected_status=400 + ) + + assert len(response['errors']) == 1 + assert response['errors'][0]['error'] == 'ValidationError' + assert response['errors'][0]['message'] == 'name is a required property' + + +def test_post_update_organisation_updates_fields(admin_request, notify_db_session): + org = create_organisation() + data = { + 'name': 'new organisation name', + 'active': False + } + + admin_request.post( + 'organisation.update_organisation', + _data=data, + organisation_id=org.id, + _expected_status=200 + ) + + organisation = Organisation.query.all() + + assert len(organisation) == 1 + assert organisation[0].id == org.id + assert organisation[0].name == data['name'] + assert organisation[0].active == data['active']