"""Application Handlers. Requests are redirected to handlers, which are responsible for getting information from the URL and passing it down to the logic layer. The way each layer talks to each other is through Response objects which defines the type status of the data and the data itself. Please note: the Orchard uses the term handlers over views as convention for clarity See: oto.response for more details. """ from flask import g from flask import jsonify from flask import request from oto import response from oto.adaptors.flask import flaskify from owsrequest import flask_request from salessheets import config from salessheets import features from salessheets.api import app from salessheets.constants import features as feature_constants from salessheets.constants import field_const from salessheets.constants import models from salessheets.constants import salessheets from salessheets.logic import generation_result from salessheets.logic import salessheets_generation from salessheets.logic import template_details from salessheets.models import ows_account from salessheets.validation import json_schema from salessheets.validation.schema import header from salessheets.validation.schema import pdf_generation_body @app.route(config.HEALTH_CHECK, methods=['GET']) def health(): """Check the health of the application.""" return jsonify({'status': 'ok'}) @app.errorhandler(500) def exception_handler(error): """Default handler when uncaught exception is raised. Note: Exception will also be sent to Sentry if config.SENTRY is set. Returns: flask.Response: A 500 response with JSON 'code' & 'message' payload. """ message = ( 'The server encountered an internal error ' 'and was unable to complete your request.') g.log.exception(error) return flaskify(response.create_fatal_response(message)) def post_validation_schema_tweaks(schema): """Modify POST validation schema based on feature flags.""" if features.is_feature_enabled( feature_constants.SALES_SHEETS_SELECT_TEMPLATE): schema['properties'].update({ salessheets.TEMPLATE_TYPE: { 'type': 'string', 'enum': salessheets.AVAILABLE_TEMPLATES}}) schema['required'].append(salessheets.TEMPLATE_TYPE) if features.is_feature_enabled( feature_constants.LOCALIZED_SALES_SHEETS): schema['properties'].update({ salessheets.LOCALIZED_TEMPLATE_TYPE_ID: { 'type': 'integer', 'minimum': 1}}) @app.route('/bulk/duplicate-upc', methods=['POST']) @json_schema.validate_headers(header.schema) @json_schema.validate_body( pdf_generation_body.schema_duplicate_upc, schema_tweaks=post_validation_schema_tweaks) def trigger_pdf_generating_duplicate_upc(): """Handler that accepts new generation request details for duplicate upc. Returns: flask.Response: Response with JSON 'code' and 'message' as result of generation triggering. """ body = request.get_json(silent=True) correlation_id = request.headers.get(field_const.CORRELATION_ID) user_id = request.headers.get(field_const.ORCHARD_USER_ID) grass_account_type = request.headers.get( field_const.GRASS_ACCOUNT_TYPE, '') grass_account_id = request.headers.get(field_const.GRASS_ACCOUNT_ID, '') return flaskify( salessheets_generation .trigger_salessheets_generation_job_duplicate_upc( context=body.get(salessheets.CONTEXT), context_type=salessheets.DISPLAY_UPC, generation_method=body.get( salessheets.GENERATION_METHOD), user_id=user_id, correlation_id=correlation_id, grass_account_type=grass_account_type, grass_account_id=grass_account_id, template_type=body.get(salessheets.TEMPLATE_TYPE), localized_template_type_id=body.get( salessheets.LOCALIZED_TEMPLATE_TYPE_ID) )) @app.route('/bulk/', methods=['GET']) @json_schema.validate_headers(header.schema) def get_generation_result(job_id): """Handler that takes job id and returns result of this job. Returns: flask.Response: Response with JSON 'code' and 'message' as result of generation triggering. """ return flaskify(generation_result.get_download_url_by_job_id( job_id=job_id)) @app.route('/bulk/history', methods=['GET']) @json_schema.validate_headers(header.schema) def get_user_history(): """Handler that provides history of jobs by current user. Returns: flask.Response: Response with JSON 'code' and 'message' as result of generation triggering. """ user_id = request.headers.get(field_const.ORCHARD_USER_ID) sort_by = request.args.get( field_const.SORT_BY, field_const.DEFAULT_SORT_BY) sort_order = request.args.get( field_const.SORT_ORDER, field_const.DEFAULT_SORT_ORDER) page_limit = int(request.args.get( field_const.PAGE_LIMIT, field_const.DEFAULT_PAGE_LIMIT)) page_offset = int(request.args.get( field_const.PAGE_OFFSET, field_const.DEFAULT_PAGE_OFFSET)) return flaskify(generation_result.get_history_for_user( user_id, sort_by, sort_order, page_limit, page_offset)) @app.route('/single/', methods=['GET']) @json_schema.validate_headers(header.schema) def get_job_data_by_release_id(release_id): """Handler that takes single release_id and returns last job data for it. Returns: flask.Response: Response with JSON 'code' and 'message' as result of generation triggering. """ grass_account_type = request.headers.get( field_const.GRASS_ACCOUNT_TYPE, '') grass_account_id = request.headers.get(field_const.GRASS_ACCOUNT_ID, '') return flaskify(generation_result.get_last_successful_job_for_release_id( release_id=release_id, grass_account_id=grass_account_id, grass_account_type=grass_account_type)) @app.route('/single/', methods=['POST']) @json_schema.validate_headers(header.schema) def trigger_pdf_generating_single_release_id(release_id): """Handler that accepts new generation request details for single release_id. The endpoint can accept parameter template_type: template type to use for salessheet generation, possible values are: 'orchard', 'redessential'. Args: release_id(int): id of release for which the sales sheet will be generated. Returns: flask.Response: Response with JSON 'code' and 'message' as result of generation triggering. """ correlation_id = request.headers.get(field_const.CORRELATION_ID) user_id = request.headers.get(field_const.ORCHARD_USER_ID) grass_account_type = request.headers.get( field_const.GRASS_ACCOUNT_TYPE, '') grass_account_id = request.headers.get(field_const.GRASS_ACCOUNT_ID, '') template_type = request.args.get('template_type') localized_template_type_id = request.args.get('localized_template_type_id') return flaskify( salessheets_generation .trigger_salessheets_generation_job_duplicate_upc( context=[str(release_id)], context_type=models.RELEASE_ID, generation_method=salessheets.SINGLE, user_id=user_id, correlation_id=correlation_id, grass_account_type=grass_account_type, grass_account_id=grass_account_id, template_type=template_type, localized_template_type_id=localized_template_type_id )) @app.route('/vendor//default_template', methods=['GET']) @json_schema.validate_headers(header.schema) def get_vendor_template(vendor_id): """Return default template for given vendor. Args: vendor_id (int): vendor id, coincides with the art_relations.vendor.vendor_id Returns: flask.Response: Response with vendor template JSON. """ account_type, account_id = flask_request.get_grass_headers(request) result = ows_account.check_vendor_rights( vendor_id, account_type, account_id) if not result: return flaskify(result) return flaskify(template_details.get_vendor_template(vendor_id)) @app.route( '/vendor//default_template/', methods=['PUT']) @json_schema.validate_headers(header.schema) def post_vendor_template(vendor_id, template_id): """Set default template for given vendor. Args: vendor_id (int): vendor id, coincides with the art_relations.vendor.vendor_id template_id (int): template id, to set for a vendor Returns: flask.Response: Response with vendor template JSON. """ account_type, account_id = flask_request.get_grass_headers(request) result = ows_account.check_vendor_rights( vendor_id, account_type, account_id) if not result: return flaskify(result) return flaskify( template_details.set_vendor_template(vendor_id, template_id)) @app.route('/default_templates', methods=['GET']) def get_default_templates(): """Return the list of possible variants of default templates. Returns: flask.Response: Response with JSON list of templates. """ return flaskify(template_details.get_default_templates())