def add_categories(): current_app.logger.info("Add category") request_body = request.get_json() try: validate(request_body, CATEGORY_SCHEMA) except ValidationError as e: current_app.logger.info("Create category - payload failed validation") raise ApplicationError(e.message, 400, 400) name = request_body["name"] display_name = request_body["display-name"] display_order = request_body["display-order"] permission = request_body["permission"] provisions = request_body["provisions"] instruments = request_body["instruments"] try: exists = Categories.query\ .filter(func.lower(Categories.name) == func.lower(name)) \ .filter(Categories.parent_id == None) \ .first() # noqa: E711 - Ignore "is None vs ==", is None does not produce valid sql in sqlAlchmey if exists is not None: message = "Category '{0}' already exists.".format(name) current_app.logger.info(message) raise ApplicationError(message, 409, 409) category = Categories(name, None, display_order, permission, display_name) db.session.add(category) db.session.flush() for prov in provisions: provision = StatutoryProvision.query\ .filter(func.lower(StatutoryProvision.title) == func.lower(prov)).first() if provision is None: message = "Statutory provision '{0}' does not exist.".format(prov) current_app.logger.info(message) raise ApplicationError(message, 404, 404) mapping = CategoryStatProvisionMapping(category.id, provision.id) db.session.add(mapping) for instrument in instruments: exists = Instruments.query.filter(func.lower(Instruments.name) == func.lower(instrument)).first() if exists is None: message = "Instrument '{0}' does not exist.".format(instrument) current_app.logger.info(message) raise ApplicationError(message, 404, 404) mapping = CategoryInstrumentsMapping(category.id, exists.id) db.session.add(mapping) db.session.commit() return "", 201 except Exception: current_app.logger.info("Rolling back transaction") db.session.rollback() raise
def add_to_register(add_land_charge_data): try: response = g.requests.post( MINT_API_URL, data=add_land_charge_data, headers={'Content-Type': 'application/json'}) except Exception as ex: error_message = 'Failed to send land charge to mint-api. Exception - {}'.format( ex) current_app.logger.exception(error_message) raise ApplicationError(error_message, 'ADD_TO_REGISTER') if response.status_code != 202: error_message = "Failed to send land charge to mint-api. Status '{}', Message '{}'"\ .format(response.status_code, response.text) current_app.logger.error(error_message) if response.status_code != 400: raise ApplicationError(error_message, 'ADD_TO_REGISTER') result = response.json() else: result = response.json() result['local-land-charge'] = encode_charge_id( result['local-land-charge']) current_app.audit_logger.info( "New charge created. entry number: {}".format( result['entry_number'])) return result, response.status_code
def add_instrument(): current_app.logger.info("Add instruments.") request_body = request.get_json() try: validate(request_body, INSTRUMENT_SCHEMA) except ValidationError as e: current_app.logger.info("Create instrument - payload failed validation") raise ApplicationError(e.message, 400, 400) name = request_body["name"] exists = Instruments.query.filter(func.lower(Instruments.name) == func.lower(name)).all() if exists is not None and len(exists) > 0: message = "Instrument '{0}' already exists.".format(name) current_app.logger.info(message) raise ApplicationError(message, 409, 409) instrument = Instruments(name) db.session.add(instrument) db.session.commit() return "", 201
def add_statutory_provisions(): current_app.logger.info("Add statutory provision.") request_body = request.get_json() try: validate(request_body, STAT_PROV_SCHEMA) except ValidationError as e: current_app.logger.info( "Add statutory provision - payload failed validation") raise ApplicationError(e.message, 400, 400) title = request_body["title"] selectable = request_body["selectable"] exists = StatutoryProvision.query.filter( func.lower(StatutoryProvision.title) == func.lower(title)).all() if exists is not None and len(exists) > 0: message = "Statutory provision '{0}' already exists.".format(title) current_app.logger.info(message) raise ApplicationError(message, 409, 409) stat_prov = StatutoryProvision(title, selectable) db.session.add(stat_prov) db.session.commit() return "", 201
def update_instrument(instrument_name): current_app.logger.info("Update instrument {0}.".format(instrument_name)) request_body = request.get_json() try: validate(request_body, INSTRUMENT_SCHEMA) except ValidationError as e: current_app.logger.info("Update instrument - payload failed validation") raise ApplicationError(e.message, 400, 400) name = request_body["name"] instrument = Instruments.query.filter(func.lower(Instruments.name) == func.lower(instrument_name)).first() if instrument is None: message = "Instrument '{0}' does not exist.".format(name) current_app.logger.info(message) raise ApplicationError(message, 404, 404) if instrument.name.lower() != name.lower(): inuse = Instruments.query.filter(func.lower(Instruments.name) == func.lower(name)).first() if inuse is not None: message = "Instrument with name '{0}' already exists.".format(name) current_app.logger.info(message) raise ApplicationError(message, 409, 409) instrument.name = name db.session.commit() return "", 204
def update_land_charge(land_charge_id): """Update a land charge""" current_app.logger.info("Endpoint called") payload = request.get_json() if str(payload['local-land-charge']) == land_charge_id: encoded_charge = encode_charge_id(payload['local-land-charge']) response = SearchApiService.get_by_charge_number(encoded_charge) if response.status_code != 200: current_app.logger.error("Charge does not exist") raise ApplicationError("Cannot find local land charge", "U101", 400) current_app.logger.info("Sending charge to mint") result, status = MintApiService.add_to_register( json.dumps(payload, sort_keys=True, separators=(',', ':'))) if status == 202: built_resp = { "entry_number": result['entry_number'], "land_charge_id": result['local-land-charge'], "registration_date": payload['registration-date'] } if 'end-date' in payload and payload['end-date']: # Used for calculating performance platform metrics current_app.logger.performance_platform( "Successfully cancelled charge '{}'".format( land_charge_id)) else: # Used for calculating performance platform metrics current_app.logger.performance_platform( "Successfully updated charge '{}'".format(land_charge_id)) else: # return result from Mint api built_resp = result else: current_app.logger.warning( "Cannot change local-land-charge field for charge '{}'".format( land_charge_id)) raise ApplicationError("Cannot change local-land-charge field", "U100", 400) current_app.logger.info( "Returning update response for charge '{}'".format(land_charge_id)) return json.dumps(built_resp, sort_keys=True, separators=(',', ':')), status, { 'Content-Type': 'application/json' }
def get_sub_category(category, sub_category): current_app.logger.info("Get category for {0}.".format(category)) category_obj = Categories.query \ .filter(func.lower(Categories.name) == func.lower(category)) \ .filter(Categories.parent_id == None) \ .first() # noqa: E711 - Ignore "is None vs ==", is None does not produce valid sql in sqlAlchmey if category_obj is None: raise ApplicationError("Category '{0}' not found.".format(category), 404, 404) sub_category_obj = Categories.query \ .filter(func.lower(Categories.name) == func.lower(sub_category)) \ .filter(Categories.parent_id == category_obj.id) \ .first() if sub_category_obj is None: raise ApplicationError("Sub-category '{0}' not found for parent '{1}'".format(sub_category, category), 404, 404) provisions = [] for provision_mapping in sub_category_obj.provisions: provisions.append(provision_mapping.provision.title) instruments = [] for instruments_mapping in sub_category_obj.instruments: instruments.append(instruments_mapping.instrument.name) children = [] for children_mapping in Categories.query \ .filter(Categories.parent_id == sub_category_obj.id) \ .order_by(Categories.display_order).all(): children.append( { "name": children_mapping.name, "display-name": children_mapping.display_name, "permission": children_mapping.permission, }) result = { "name": sub_category_obj.name, "display-name": sub_category_obj.display_name, "permission": sub_category_obj.permission, "statutory-provisions": provisions, "instruments": instruments, "sub-categories": children, "parent": category_obj.name} return Response(response=json.dumps(result), mimetype="application/json")
def delete_sub_category(category, sub_category): current_app.logger.info("Delete category for {0}.".format(category)) try: category = Categories.query \ .filter(func.lower(Categories.name) == func.lower(category)) \ .filter(Categories.parent_id == None) \ .first() # noqa: E711 - Ignore "is None vs ==", is None does not produce valid sql in sqlAlchmey if category is None: raise ApplicationError("Category '{0}' not found.".format(category), 404, 404) sub_category_obj = Categories.query \ .filter(func.lower(Categories.name) == func.lower(sub_category)) \ .filter(Categories.parent_id == category.id) \ .first() if sub_category_obj is None: raise ApplicationError("Sub-category '{0}' not found for parent '{1}'" .format(sub_category, category), 404, 404) sub_categories = Categories.query \ .filter(Categories.parent_id == sub_category_obj.id) \ .all() for sub_category in sub_categories: CategoryInstrumentsMapping.query \ .filter(CategoryInstrumentsMapping.category_id == sub_category.id).delete() CategoryStatProvisionMapping.query \ .filter(CategoryStatProvisionMapping.category_id == sub_category.id).delete() Categories.query \ .filter(Categories.id == sub_category.id).delete() CategoryInstrumentsMapping.query\ .filter(CategoryInstrumentsMapping.category_id == sub_category_obj.id).delete() CategoryStatProvisionMapping.query\ .filter(CategoryStatProvisionMapping.category_id == sub_category_obj.id).delete() Categories.query\ .filter(Categories.id == sub_category_obj.id).delete() db.session.commit() return "", 204 except Exception: current_app.logger.info("Rolling back transaction") db.session.rollback() raise
def test_add_charge_failed_application_error_400(self, mock_mint_api_service, mock_request, mock_current_app): request_data = '{"further-information-location": "123 main street","further-information-reference": "123",' \ '"expiry-date": "2020-01-01","charge-creation-date": "2017-01-01",' \ '"charge-geographic-description": "More information about ' \ 'location","geometry": {"features": [{"geometry": {"coordinates": [[[' \ '606371.1076853184,284813.8509437054],[544515.2768183555,217914.9341522617],' \ '[520410.0576399995,287147.0361743166],[606371.1076853184,284813.8509437054]]],' \ '"type": "Polygon"},"properties": {"id": 1},"type": "Feature"}],"type": "FeatureCollection"},' \ '"statutory-provision": "Building Act 1984 section 107","charge_reason": "deed",' \ '"originating-authority": "City of London", "registration-date": "2012-12-12"} ' mock_request.get_json.return_value = json.loads(request_data) mock_mint_api_service.add_to_register.side_effect = \ ApplicationError('Invalid request. For more information check the logs for TraceID', "test", 400) headers = { 'Content-Type': 'application/json', 'Authorization': 'NOTAREALJWT' } result = self.app.post('/v1.0/maintain/local-land-charge', data=request_data, headers=headers) self.assertEqual(result.status_code, 400) self.assertIn( 'Invalid request. For more information check the logs for TraceID', result.data.decode()) self.assertEqual( mock_current_app.logger.performance_platform.mock_calls, [])
def get_all_statutory_provisions(): current_app.logger.info("Get all statutory provisions.") selectable = request.args.get('selectable') if selectable is None: provisions = StatutoryProvision.query \ .distinct(StatutoryProvision.title) \ .order_by(StatutoryProvision.title) \ .all() else: provisions = StatutoryProvision.query \ .distinct(StatutoryProvision.title) \ .filter(StatutoryProvision.selectable == selectable) \ .order_by(StatutoryProvision.title) \ .all() if provisions is None or len(provisions) == 0: raise ApplicationError("No provisions found.", 404, 404) provisions_json = [] for provision in provisions: provisions_json.append(provision.title) return Response(response=json.dumps(provisions_json), mimetype="application/json")
def get_current_timestamp(): cur = None conn = None try: conn = psycopg2.connect(current_app.config['SQLALCHEMY_DATABASE_URI']) cur = conn.cursor() cur.execute("SELECT CURRENT_TIMESTAMP;") result = cur.fetchone() return result[0] except psycopg2.DataError as e: raise ApplicationError('Input data error: ' + str(e), 'DB', http_code=400) except (psycopg2.OperationalError, psycopg2.ProgrammingError) as e: raise ApplicationError('Database error: ' + str(e), 'DB', http_code=400) finally: if cur: cur.close() if conn: conn.close()
def update_statutory_provisions(stat_prov): current_app.logger.info( "Update statutory provision {0}.".format(stat_prov)) request_body = request.get_json() try: validate(request_body, STAT_PROV_SCHEMA) except ValidationError as e: current_app.logger.info( "Update statutory provision - payload failed validation") raise ApplicationError(e.message, 400, 400) title = request_body["title"] selectable = request_body["selectable"] provision = StatutoryProvision.query.filter( func.lower(StatutoryProvision.title) == func.lower(stat_prov)).first() if provision is None: message = "Statutory provision '{0}' does not exist.".format(stat_prov) current_app.logger.info(message) raise ApplicationError(message, 404, 404) if provision.title.lower() != title.lower(): inuse = StatutoryProvision.query.filter( func.lower(StatutoryProvision.title) == func.lower(title)).first() if inuse is not None: message = "Statutory provision with name '{0}' already exists.".format( title) current_app.logger.info(message) raise ApplicationError(message, 409, 409) provision.title = title provision.selectable = selectable db.session.commit() return "", 204
def delete_instrument(instrument_name): current_app.logger.info("Delete instruments.") instrument = Instruments.query.filter(func.lower(Instruments.name) == func.lower(instrument_name)).first() if instrument is None: message = "Instrument '{0}' does not exist.".format(instrument_name) current_app.logger.info(message) raise ApplicationError(message, 404, 404) Instruments.query.filter(Instruments.id == instrument.id).delete() db.session.commit() return "", 204
def before_request(): # Sets the transaction trace id into the global object if it has been provided in the HTTP header from the caller. # Generate a new one if it has not. We will use this in log messages. g.trace_id = request.headers.get('X-Trace-ID', uuid.uuid4().hex) # We also create a session-level requests object for the app to use with the header pre-set, so other APIs will # receive it. These lines can be removed if the app will not make requests to other LR APIs! g.requests = requests.Session() g.requests.headers.update({'X-Trace-ID': g.trace_id}) # Don't check for a JWT on health endpoints if '/health' in request.path: return if 'Authorization' not in request.headers: raise ApplicationError("Missing Authorization header", "AUTH1", 401) try: validate(app.config['AUTHENTICATION_API_URL'] + '/authentication/validate', request.headers['Authorization'], g.requests) except ValidationFailure as fail: raise ApplicationError(fail.message, "AUTH1", 401) g.requests.headers.update({'Authorization': request.headers['Authorization']})
def get_all_instruments(): current_app.logger.info("Get all instruments.") instruments = Instruments.query \ .distinct(Instruments.name) \ .order_by(Instruments.name) \ .all() if instruments is None or len(instruments) == 0: raise ApplicationError("No instruments found.", 404, 404) instruments_json = [] for instrument in instruments: instruments_json.append(instrument.name) return Response(response=json.dumps(instruments_json), mimetype="application/json")
def encode_base_31(charge_id): """Encodes the given base 10 charge_id into a base 31 string. Throws ApplicationError if charge_id is less than ChargeId.FLOOR or greater than ChargeId.CEILING. """ if charge_id < ChargeId.FLOOR or charge_id > ChargeId.CEILING: raise ApplicationError('The given charge id ({}) is less than the allowed floor ({}), or greater than the ' 'allowed ceiling ({})'.format(charge_id, ChargeId.FLOOR, ChargeId.CEILING), 500) encoded = '' while charge_id > 0: charge_id, remainder = divmod(charge_id, 31) encoded = ChargeId.CHARACTERS[remainder] + encoded return encoded
def delete_statutory_provisions(stat_prov): current_app.logger.info( "Delete statutory provision {0}.".format(stat_prov)) provision = StatutoryProvision.query.filter( func.lower(StatutoryProvision.title) == func.lower(stat_prov)).first() if provision is None: message = "Statutory provision '{0}' does not exist.".format(stat_prov) current_app.logger.info(message) raise ApplicationError(message, 404, 404) StatutoryProvision.query.filter( StatutoryProvision.id == provision.id).delete() db.session.commit() return "", 204
def update_sub_category(category, sub_category): current_app.logger.info("Update category") request_body = request.get_json() try: validate(request_body, CATEGORY_SCHEMA) except ValidationError as e: current_app.logger.info("Update category - payload failed validation") raise ApplicationError(e.message, 400, 400) name = request_body["name"] display_name = request_body["display-name"] display_order = request_body["display-order"] permission = request_body["permission"] provisions = request_body["provisions"] instruments = request_body["instruments"] try: parent = Categories.query \ .filter(func.lower(Categories.name) == func.lower(category)) \ .filter(Categories.parent_id == None) \ .first() # noqa: E711 - Ignore "is None vs ==", is None does not produce valid sql in sqlAlchmey if parent is None: message = "Category '{0}' does not exist.".format(category) current_app.logger.info(message) raise ApplicationError(message, 404, 404) sub_category_obj = Categories.query \ .filter(func.lower(Categories.name) == func.lower(sub_category)) \ .filter(Categories.parent_id == parent.id) \ .first() if sub_category_obj is None: raise ApplicationError("Sub-category '{0}' not found for parent '{1}'" .format(sub_category, category), 404, 404) if sub_category_obj.name.lower() != sub_category.lower(): inuse = Categories.query \ .filter(func.lower(Categories.name) == func.lower(sub_category)) \ .filter(Categories.parent_id == parent.id) \ .first() if inuse is not None: message = "sub-category with the name '{0}' already exists under {1}.".format(name, category) current_app.logger.info(message) raise ApplicationError(message, 409, 409) sub_category_obj.name = name sub_category_obj.display_name = display_name sub_category_obj.permission = permission sub_category_obj.display_order = display_order CategoryInstrumentsMapping.query.filter(CategoryInstrumentsMapping.category_id == sub_category_obj.id).delete() CategoryStatProvisionMapping.query.filter( CategoryStatProvisionMapping.category_id == sub_category_obj.id).delete() for prov in provisions: provision = StatutoryProvision.query \ .filter(func.lower(StatutoryProvision.title) == func.lower(prov)).first() if provision is None: message = "Statutory provision '{0}' does not exist.".format(prov) current_app.logger.info(message) raise ApplicationError(message, 404, 404) mapping = CategoryStatProvisionMapping(sub_category_obj.id, provision.id) db.session.add(mapping) for instrument in instruments: exists = Instruments.query.filter(func.lower(Instruments.name) == func.lower(instrument)).first() if exists is None: message = "Instrument '{0}' does not exist.".format(instrument) current_app.logger.info(message) raise ApplicationError(message, 404, 404) mapping = CategoryInstrumentsMapping(sub_category_obj.id, exists.id) db.session.add(mapping) db.session.commit() return "", 204 except Exception: current_app.logger.info("Rolling back transaction") db.session.rollback() raise