From 49f8088f89e3cabe15268f7917b08f6815cfe386 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Tue, 5 Sep 2023 13:06:25 -0700 Subject: [PATCH 1/6] 15687 - pay-api remove restx --- pay-api/requirements.txt | 2 +- pay-api/requirements/prod.txt | 2 +- pay-api/src/pay_api/__init__.py | 16 +- pay-api/src/pay_api/models/invoice.py | 4 +- pay-api/src/pay_api/resources/__init__.py | 76 +-- pay-api/src/pay_api/resources/account.py | 449 ++++++++---------- .../pay_api/resources/account_statements.py | 81 ++-- .../account_statements_notifications.py | 85 ++-- .../resources/account_statements_settings.py | 87 ++-- pay-api/src/pay_api/resources/apihelper.py | 29 -- .../src/pay_api/resources/bank_accounts.py | 63 ++- pay-api/src/pay_api/resources/code.py | 41 +- .../src/pay_api/resources/distributions.py | 244 +++++----- pay-api/src/pay_api/resources/endpoints.py | 40 ++ pay-api/src/pay_api/resources/fas/__init__.py | 4 +- pay-api/src/pay_api/resources/fas/refund.py | 58 ++- .../src/pay_api/resources/fas/routing_slip.py | 416 ++++++++-------- pay-api/src/pay_api/resources/fee.py | 76 ++- pay-api/src/pay_api/resources/fee_schedule.py | 45 +- pay-api/src/pay_api/resources/invoice.py | 273 +++++------ .../src/pay_api/resources/invoice_receipt.py | 87 ++-- pay-api/src/pay_api/resources/invoices.py | 42 +- pay-api/src/pay_api/resources/meta.py | 20 +- pay-api/src/pay_api/resources/ops.py | 43 +- pay-api/src/pay_api/resources/payment.py | 102 ++-- pay-api/src/pay_api/resources/refund.py | 45 +- pay-api/src/pay_api/resources/transaction.py | 179 ++++--- pay-api/src/pay_api/resources/v1/__init__.py | 73 +++ pay-api/src/pay_api/utils/endpoints_enums.py | 24 + pay-api/tests/unit/api/test_cors_preflight.py | 223 +++++++++ 30 files changed, 1516 insertions(+), 1413 deletions(-) mode change 100755 => 100644 pay-api/src/pay_api/resources/__init__.py delete mode 100644 pay-api/src/pay_api/resources/apihelper.py create mode 100644 pay-api/src/pay_api/resources/endpoints.py create mode 100644 pay-api/src/pay_api/resources/v1/__init__.py create mode 100644 pay-api/src/pay_api/utils/endpoints_enums.py create mode 100644 pay-api/tests/unit/api/test_cors_preflight.py diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt index 8052fe144..17a64eaf9 100644 --- a/pay-api/requirements.txt +++ b/pay-api/requirements.txt @@ -1,4 +1,5 @@ Flask-Caching==2.0.2 +Flask-Cors==3.0.10 Flask-Migrate==2.7.0 Flask-Moment==1.0.5 Flask-SQLAlchemy==2.5.1 @@ -34,7 +35,6 @@ exceptiongroup==1.1.1 expiringdict==1.2.2 flask-jwt-oidc==0.3.0 flask-marshmallow==0.11.0 -flask-restx==1.1.0 google-api-core==2.11.0 google-auth==2.18.1 google-cloud-pubsub==2.17.0 diff --git a/pay-api/requirements/prod.txt b/pay-api/requirements/prod.txt index cf33fa840..b2fcaef1d 100644 --- a/pay-api/requirements/prod.txt +++ b/pay-api/requirements/prod.txt @@ -1,11 +1,11 @@ gunicorn Flask Flask-Caching +Flask-Cors Flask-Migrate<3 Flask-Script Flask-Moment Flask-SQLAlchemy -flask-restx flask-marshmallow==0.11.0 flask-jwt-oidc python-dotenv diff --git a/pay-api/src/pay_api/__init__.py b/pay-api/src/pay_api/__init__.py index ec680f4b6..1a77c8a64 100755 --- a/pay-api/src/pay_api/__init__.py +++ b/pay-api/src/pay_api/__init__.py @@ -27,6 +27,7 @@ # import pay_api.config as config from pay_api import config from pay_api.config import _Config +from pay_api.resources import endpoints from pay_api.services.flags import flags from pay_api.models import db, ma from pay_api.utils.auth import jwt @@ -47,6 +48,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): flags.init_app(app) db.init_app(app) ma.init_app(app) + endpoints.init_app(app) if run_mode != 'migration': @@ -57,11 +59,7 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): dsn=app.config.get('SENTRY_DSN'), integrations=[FlaskIntegration()] ) - # pylint: disable=import-outside-toplevel - from pay_api.resources import API_BLUEPRINT, OPS_BLUEPRINT - app.register_blueprint(API_BLUEPRINT) - app.register_blueprint(OPS_BLUEPRINT) app.after_request(convert_to_camel) setup_jwt_manager(app, jwt) @@ -69,6 +67,16 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')): ExceptionHandler(app) @app.after_request + def handle_after_request(response): # pylint: disable=unused-variable + add_version(response) + set_access_control_header(response) + return response + + def set_access_control_header(response): + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, registries-trace-id, ' \ + 'Account-Id' + def add_version(response): # pylint: disable=unused-variable version = get_run_version() response.headers['API'] = f'pay_api/{version}' diff --git a/pay-api/src/pay_api/models/invoice.py b/pay-api/src/pay_api/models/invoice.py index 164049032..ea05d8c9a 100644 --- a/pay-api/src/pay_api/models/invoice.py +++ b/pay-api/src/pay_api/models/invoice.py @@ -190,8 +190,8 @@ class Meta(BaseSchema.Meta): # pylint: disable=too-few-public-methods payment_account = ma.Nested(PaymentAccountSchema(only=('auth_account_id', 'name', 'billable')), many=False) _links = ma.Hyperlinks({ - 'self': ma.URLFor('API.invoice_invoice', invoice_id=''), - 'collection': ma.URLFor('API.invoice_invoices', invoice_id='') + 'self': ma.URLFor('INVOICE.get_invoice', invoice_id=''), + 'collection': ma.URLFor('INVOICE.get_invoices', invoice_id='') }) total = fields.Float(data_key='total') diff --git a/pay-api/src/pay_api/resources/__init__.py b/pay-api/src/pay_api/resources/__init__.py old mode 100755 new mode 100644 index b9b0b0540..b9e0dcbb5 --- a/pay-api/src/pay_api/resources/__init__.py +++ b/pay-api/src/pay_api/resources/__init__.py @@ -20,78 +20,4 @@ - meta That are used to expose operational health information about the service, and meta information. """ -from flask import Blueprint -from sbc_common_components.exception_handling.exception_handler import ExceptionHandler - -from .account import API as ACCOUNTS_API -from .account_statements import API as ACCOUNT_STATEMENTS_API -from .account_statements_notifications import API as ACCOUNT_STATEMENT_NOTIFICATIONS_API -from .account_statements_settings import API as ACCOUNT_STATEMENTS_SETTINGS_API -from .apihelper import Api -from .bank_accounts import API as BANK_ACCOUNTS_API -from .code import API as CODES_API -from .distributions import API as DISTRIBUTION_API -from .fas import ROUTING_SLIP_API, ROUTING_SLIP_REFUND_API -from .fee import API as FEE_API -from .fee_schedule import API as FEE_SCHEDULE_API -from .invoice import API as INVOICE_API -from .invoice_receipt import API as INVOICE_RECEIPT_API -from .invoices import API as INVOICES_API -from .meta import API as META_API -from .ops import API as OPS_API -from .payment import API as PAYMENT_API -from .refund import API as REFUND_API -from .transaction import API as TRANSACTION_API - - -__all__ = ('API_BLUEPRINT', 'OPS_BLUEPRINT') - -# This will add the Authorize button to the swagger docs -# TODO oauth2 & openid may not yet be supported by restplus <- check on this -AUTHORIZATIONS = {'apikey': {'type': 'apiKey', 'in': 'header', 'name': 'Authorization'}} - -OPS_BLUEPRINT = Blueprint('API_OPS', __name__, url_prefix='/ops') - -API_OPS = Api( - OPS_BLUEPRINT, - title='Service OPS API', - version='1.0', - description='The Core API for the Payment System', - security=['apikey'], - authorizations=AUTHORIZATIONS, -) - -API_OPS.add_namespace(OPS_API, path='/') - -API_BLUEPRINT = Blueprint('API', __name__, url_prefix='/api/v1') - -API = Api( - API_BLUEPRINT, - title='Payment API', - version='1.0', - description='The Core API for the Payment System', - security=['apikey'], - authorizations=AUTHORIZATIONS, -) - -HANDLER = ExceptionHandler(API) - -API.add_namespace(META_API, path='/meta') -API.add_namespace(INVOICE_API, path='/payment-requests') -API.add_namespace(FEE_API, path='/fees') -API.add_namespace(FEE_SCHEDULE_API, '/fees/schedules') -API.add_namespace(DISTRIBUTION_API, '/fees/distributions') -API.add_namespace(TRANSACTION_API, path='') -API.add_namespace(INVOICE_RECEIPT_API, path='/payment-requests/') -API.add_namespace(INVOICES_API, path='/payment-requests//invoices') -API.add_namespace(ACCOUNTS_API, path='/accounts') -API.add_namespace(ACCOUNT_STATEMENTS_API, path='/accounts//statements') -API.add_namespace(ACCOUNT_STATEMENTS_SETTINGS_API, path='/accounts//statements/settings') -API.add_namespace(ACCOUNT_STATEMENT_NOTIFICATIONS_API, path='/accounts//statements/notifications') -API.add_namespace(BANK_ACCOUNTS_API, path='/bank-accounts/verifications') -API.add_namespace(REFUND_API, path='/payment-requests/') -API.add_namespace(PAYMENT_API, path='/accounts//payments') - -API.add_namespace(CODES_API, path='/codes') -API.add_namespace(ROUTING_SLIP_API, path='/fas/routing-slips') -API.add_namespace(ROUTING_SLIP_REFUND_API, path='/fas/routing-slips//refunds') +from .endpoints import endpoints diff --git a/pay-api/src/pay_api/resources/account.py b/pay-api/src/pay_api/resources/account.py index 2c54944ee..a1d0f49ea 100644 --- a/pay-api/src/pay_api/resources/account.py +++ b/pay-api/src/pay_api/resources/account.py @@ -15,8 +15,8 @@ from datetime import datetime from http import HTTPStatus -from flask import Response, abort, current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, Response, abort, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, ServiceUnavailableException, error_to_response from pay_api.schemas import utils as schema_utils @@ -25,251 +25,224 @@ from pay_api.services.payment_account import PaymentAccount as PaymentAccountService from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import EDIT_ROLE, VIEW_ROLE +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import CfsAccountStatus, ContentType, Role from pay_api.utils.errors import Error from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight - -API = Namespace('accounts', description='Payment System - Accounts') - - -@cors_preflight('POST') -@API.route('', methods=['POST', 'OPTIONS']) -class Accounts(Resource): - """Endpoint resource to create payment account.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_jwt.has_one_of_roles([Role.SYSTEM.value]) - def post(): - """Create the payment account records.""" - current_app.logger.info('Account.post') - return jsonify(response.asdict()), status - - -@cors_preflight('PUT,GET,DELETE') -@API.route('/', methods=['PUT', 'GET', 'DELETE', 'OPTIONS']) -class Account(Resource): - """Endpoint resource to update and get payment account.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.SYSTEM.value]) - @_tracing.trace() - def put(account_number: str): - """Create the payment account records.""" - current_app.logger.info('Account.post') - return jsonify(response.asdict()), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(account_number: str): - """Get payment account details.""" - current_app.logger.info('Account.get') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.SYSTEM.value]) - @_tracing.trace() - def delete(account_number: str): - """Get payment account details.""" - current_app.logger.info('Account.delete') - return jsonify({}), HTTPStatus.NO_CONTENT - - -@cors_preflight('POST,GET') -@API.route('//fees', methods=['POST', 'GET', 'OPTIONS']) -class AccountFees(Resource): - """Endpoint resource to create payment account fee settings.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_jwt.has_one_of_roles([Role.MANAGE_ACCOUNTS.value]) - def post(account_number: str): - """Create or update the account fee settings.""" - current_app.logger.info('AccountFees.post') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_jwt.has_one_of_roles([Role.MANAGE_ACCOUNTS.value]) - def get(account_number: str): - """Get Fee details for the account.""" - current_app.logger.info('AccountFees.get') - return jsonify(response), status - - -@cors_preflight('PUT') -@API.route('//fees/', methods=['PUT', 'OPTIONS']) -class AccountFee(Resource): - """Endpoint resource to update payment account fee settings.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_jwt.has_one_of_roles([Role.MANAGE_ACCOUNTS.value]) - def put(account_number: str, product: str): - """Create or update the account fee settings.""" - current_app.logger.info('AccountFee.post') - return jsonify(response), status + else HTTPStatus.CREATED + except BusinessException as exception: + return exception.response() + current_app.logger.debug('>post_account') + return jsonify(response.asdict()), status + + +@bp.route('/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'PUT', 'DELETE']) +@_tracing.trace() +@_jwt.requires_auth +def get_account(account_number: str): + """Get payment account details.""" + current_app.logger.info('get_account') + return jsonify(response), status + + +@bp.route('/', methods=['PUT']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.has_one_of_roles([Role.SYSTEM.value]) +def put_account(account_number: str): + """Update the payment account records.""" + current_app.logger.info('put_account') + return jsonify(response.asdict()), status + + +@bp.route('/', methods=['DELETE']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.has_one_of_roles([Role.SYSTEM.value]) +def delete_account(account_number: str): + """Delete payment account details.""" + current_app.logger.info('delete_account') + return jsonify({}), HTTPStatus.NO_CONTENT + + +@bp.route('//fees', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_jwt.requires_auth +@_jwt.has_one_of_roles([Role.MANAGE_ACCOUNTS.value]) +def get_account_fees(account_number: str): + """Get Fee details for the account.""" + current_app.logger.info('get_account_fees') + return jsonify(response), status + + +@bp.route('//fees', methods=['POST']) +@cross_origin(origins='*') +@_jwt.requires_auth +@_jwt.has_one_of_roles([Role.MANAGE_ACCOUNTS.value]) +def post_account_fees(account_number: str): + """Create or update the account fee settings.""" + current_app.logger.info('post_account_fees') + return jsonify(response), status + + +@bp.route('//fees/', methods=['PUT', 'OPTIONS']) +@cross_origin(origins='*', methods=['PUT']) +@_jwt.requires_auth +@_jwt.has_one_of_roles([Role.MANAGE_ACCOUNTS.value]) +def put_account_fee_product(account_number: str, product: str): + """Create or update the account fee settings.""" + current_app.logger.info('put_account_fee_product') + return jsonify(response), status ######################################################################################################### # Note this route is used by CSO for reconciliation, so be careful if making any changes to the response. ######################################################################################################### -@cors_preflight('POST') -@API.route('//payments/queries', methods=['POST', 'OPTIONS']) -class AccountPurchaseHistory(Resource): - """Endpoint resource to query payment.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(account_number: str): - """Create the payment records.""" - current_app.logger.info(' searches transactions across all accounts -- needs special role - view_all = request.args.get('viewAll', None) == 'true' - - # Check if user is authorized to perform this action - required_roles = [Role.EDITOR.value, Role.VIEW_ALL_TRANSACTIONS.value] if view_all else [Role.EDITOR.value] - check_auth(business_identifier=None, account_id=account_number, all_of_roles=required_roles) - - account_to_search = None if view_all else account_number - page: int = int(request.args.get('page', '1')) - limit: int = int(request.args.get('limit', '10')) - response, status = Payment.search_purchase_history(account_to_search, request_json, page, - limit), HTTPStatus.OK - current_app.logger.debug('>AccountPurchaseHistory.post') - return jsonify(response), status - - -@cors_preflight('POST') -@API.route('//payments/reports', methods=['POST', 'OPTIONS']) -class AccountPurchaseReport(Resource): - """Endpoint resource to create payment.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(account_number: str): - """Create the payment records.""" - current_app.logger.info('/payments/queries', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_tracing.trace() +@_jwt.requires_auth +def post_search_purchase_history(account_number: str): + """Search purchase history.""" + current_app.logger.info(' searches transactions across all accounts -- needs special role + view_all = request.args.get('viewAll', None) == 'true' + + # Check if user is authorized to perform this action + required_roles = [Role.EDITOR.value, Role.VIEW_ALL_TRANSACTIONS.value] if view_all else [Role.EDITOR.value] + check_auth(business_identifier=None, account_id=account_number, all_of_roles=required_roles) + + account_to_search = None if view_all else account_number + page: int = int(request.args.get('page', '1')) + limit: int = int(request.args.get('limit', '10')) + response, status = Payment.search_purchase_history(account_to_search, request_json, page, + limit), HTTPStatus.OK + current_app.logger.debug('>post_search_purchase_history') + return jsonify(response), status + + +@bp.route('//payments/reports', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_tracing.trace() +@_jwt.requires_auth +def post_account_purchase_report(account_number: str): + """Create the account purchase report.""" + current_app.logger.info('/statements') -@cors_preflight('GET') -@API.route('', methods=['GET', 'OPTIONS']) -class AccountStatements(Resource): - """Endpoint resource for statements.""" +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +@_jwt.requires_auth +def get_account_statements(account_id): + """Get all statements records for an account.""" + current_app.logger.info('get_account_statements') + return jsonify(response), status - response, status = StatementService.find_by_account_id(account_id, page, limit), HTTPStatus.OK - current_app.logger.debug('>AccountStatements.get') - return jsonify(response), status +@bp.route('/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +@_jwt.requires_auth +def get_account_statement(account_id: str, statement_id: str): + """Create the statement report.""" + current_app.logger.info('', methods=['GET', 'OPTIONS']) -class AccountStatementReport(Resource): - """Endpoint resource to generate account statement report.""" + # Check if user is authorized to perform this action + auth = check_auth(business_identifier=None, account_id=account_id, contains_role=EDIT_ROLE, is_premium=True) - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(account_id: str, statement_id: str): - """Create the statement report.""" - current_app.logger.info('/statements/notifications') -@cors_preflight('GET,POST') -@API.route('', methods=['GET', 'POST', 'OPTIONS']) -class AccountStatementsNotifications(Resource): - """Endpoint resource for account statements notifications.""" +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.requires_auth +def get_account_notifications(account_id): + """Get all statements records for an account.""" + current_app.logger.info('get_account_notifications') + return jsonify(response), status - # Check if user is authorized to perform this action - check_auth(business_identifier=None, account_id=account_id, contains_role=EDIT_ROLE, is_premium=True) - statement_notification_details = StatementRecipients.find_statement_notification_details(account_id) - response, status = statement_notification_details, HTTPStatus.OK - current_app.logger.debug('>AccountStatementsNotifications.get') - return jsonify(response), status - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(account_id): - """Update the statement settings .""" - current_app.logger.info('AccountStatementsNotifications.post') - return jsonify(None), HTTPStatus.CREATED + except BusinessException as exception: + return exception.response() + current_app.logger.debug('>post_account_notification') + return jsonify(None), HTTPStatus.CREATED diff --git a/pay-api/src/pay_api/resources/account_statements_settings.py b/pay-api/src/pay_api/resources/account_statements_settings.py index 46d4aef6b..0a9432a5a 100644 --- a/pay-api/src/pay_api/resources/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/account_statements_settings.py @@ -14,64 +14,61 @@ """Resource for Statement Settings.""" from http import HTTPStatus -from flask import current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException from pay_api.services import StatementSettings as StatementSettingsService from pay_api.services.auth import check_auth from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import CHANGE_STATEMENT_SETTINGS, EDIT_ROLE +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight -API = Namespace('accounts', description='Payment System - Statements Settings') +bp = Blueprint('ACCOUNT_SETTINGS', __name__, + url_prefix=f'{EndpointEnum.API_V1.value}/accounts//statements/settings') -@cors_preflight('GET,POST') -@API.route('', methods=['GET', 'POST', 'OPTIONS']) -class AccountStatementsSettings(Resource): - """Endpoint resource for statements.""" +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.requires_auth +def get_account_statement_settings(account_id): + """Get all statements records for an account.""" + current_app.logger.info('get_account_statement_settings') + return jsonify(response), status - response, status = StatementSettingsService.find_by_account_id(account_id), HTTPStatus.OK - current_app.logger.debug('>AccountStatementsSettings.get') - return jsonify(response), status - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(account_id): - """Update the statement settings .""" - current_app.logger.info('Payment.put') - return jsonify(response), status + try: + response, status = ( + StatementSettingsService.update_statement_settings( + account_id, frequency + ), + HTTPStatus.OK, + ) + except BusinessException as exception: + return exception.response() + current_app.logger.debug('>post_account_statement_settings') + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/apihelper.py b/pay-api/src/pay_api/resources/apihelper.py deleted file mode 100644 index 276e9b9c1..000000000 --- a/pay-api/src/pay_api/resources/apihelper.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright © 2019 Province of British Columbia -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Meta information about the service. - -to support swagger on http -""" -from flask import url_for -from flask_restx import Api as BaseApi - - -class Api(BaseApi): - """Monkey patch Swagger API to return HTTPS URLs.""" - - @property - def specs_url(self): - """Return URL for endpoint.""" - scheme = 'http' if '5000' in self.base_url else 'https' - return url_for(self.endpoint('specs'), _external=True, _scheme=scheme) diff --git a/pay-api/src/pay_api/resources/bank_accounts.py b/pay-api/src/pay_api/resources/bank_accounts.py index df5951c42..9398164e8 100644 --- a/pay-api/src/pay_api/resources/bank_accounts.py +++ b/pay-api/src/pay_api/resources/bank_accounts.py @@ -14,45 +14,40 @@ """Resource for Payment account.""" from http import HTTPStatus -from flask import current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, ServiceUnavailableException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services import CFSService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error -from pay_api.utils.util import cors_preflight -API = Namespace('bank_accounts', description='Payment System - Bank Accounts') - - -@cors_preflight('POST') -@API.route('', methods=['POST', 'OPTIONS']) -class BankAccounts(Resource): - """Endpoint resource to deal with bank details.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - def post(): - """Validate the bank account details against CFS.""" - current_app.logger.info('Account.post') - return jsonify(response), status +bp = Blueprint('BANK_ACCOUNTS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/bank-accounts/verifications') + + +@bp.route('', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_jwt.requires_auth +def post_bank_account_validate(): + """Validate the bank account details against CFS.""" + current_app.logger.info('Account.post') + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/code.py b/pay-api/src/pay_api/resources/code.py index 4a11b97c1..2794a7696 100644 --- a/pay-api/src/pay_api/resources/code.py +++ b/pay-api/src/pay_api/resources/code.py @@ -14,37 +14,28 @@ """Resource for code endpoints.""" from http import HTTPStatus -from flask_restx import Namespace, Resource, cors +from flask import Blueprint +from flask_cors import cross_origin from pay_api.services.code import Code as CodeService from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight +from pay_api.utils.endpoints_enums import EndpointEnum -API = Namespace('codes', description='Payment System - Codes') +bp = Blueprint('CODES', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/codes') -@cors_preflight('GET') -@API.route('/', methods=['GET', 'OPTIONS']) -class Codes(Resource): - """Endpoint resource to return codes.""" +@bp.route('/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +def get_codes_by_type(code_type): + """Return all codes based on code_type.""" + return CodeService.find_code_values_by_type(code_type), HTTPStatus.OK - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - def get(code_type): - """Return all codes based on code_type.""" - return CodeService.find_code_values_by_type(code_type), HTTPStatus.OK - -@cors_preflight('GET') -@API.route('//', methods=['GET', 'OPTIONS']) -class Code(Resource): - """Endpoint resource to return codes.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - def get(code_type, code): - """Return all codes based on code_type.""" - return CodeService.find_code_value_by_type_and_code(code_type, code), HTTPStatus.OK +@bp.route('//', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +def get_code(code_type, code): + """Return all codes based on code_type.""" + return CodeService.find_code_value_by_type_and_code(code_type, code), HTTPStatus.OK diff --git a/pay-api/src/pay_api/resources/distributions.py b/pay-api/src/pay_api/resources/distributions.py index 3ca870bca..5329c5c78 100644 --- a/pay-api/src/pay_api/resources/distributions.py +++ b/pay-api/src/pay_api/resources/distributions.py @@ -14,139 +14,127 @@ """Resource for Distribution endpoints.""" from http import HTTPStatus -from flask import jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services import DistributionCode as DistributionCodeService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight - - -API = Namespace('fees-distributions', description='Payment System - Distributions') - - -@cors_preflight('GET, POST') -@API.route('', methods=['GET', 'POST', 'OPTIONS']) -class Distributions(Resource): - """Endpoint resource to get and post distribution.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) - def get(): - """Return all distributions.""" - try: - response, status = ( - DistributionCodeService.find_all(), - HTTPStatus.OK, - ) - - except BusinessException as exception: - return exception.response() - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) - def post(): - """Create a new distribution from the payload.""" - request_json = request.get_json() - - valid_format, errors = schema_utils.validate(request_json, 'distribution_code') - if not valid_format: - return error_to_response(Error.INVALID_REQUEST, invalid_params=schema_utils.serialize(errors)) - - try: - response, status = ( - DistributionCodeService.save_or_update(request_json), - HTTPStatus.CREATED, - ) - except BusinessException as exception: - return exception.response() - return jsonify(response), status - - -@cors_preflight(['GET', 'PUT']) -@API.route('/', methods=['GET', 'PUT', 'OPTIONS']) -class Distribution(Resource): - """Endpoint resource to handle distribution.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) - def get(distribution_code_id: int): - """Return distribution by provided id.""" - try: - response, status = ( - DistributionCodeService.find_by_id(distribution_code_id), - HTTPStatus.OK, - ) - - except BusinessException as exception: - return exception.response() - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) - def put(distribution_code_id: int): - """Update distribution from the payload.""" - request_json = request.get_json() - - valid_format, errors = schema_utils.validate(request_json, 'distribution_code') - if not valid_format: - return error_to_response(Error.INVALID_REQUEST, invalid_params=schema_utils.serialize(errors)) - - try: - response, status = ( - DistributionCodeService.save_or_update(request_json, distribution_code_id), - HTTPStatus.OK, - ) - except BusinessException as exception: - return exception.response() - return jsonify(response), status - - -@cors_preflight(['GET', 'POST']) -@API.route('//schedules', methods=['GET', 'POST', 'OPTIONS']) -class DistributionSchedules(Resource): - """Endpoint resource to handle Distribution Schedules.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) - def get(distribution_code_id: int): - """Return all fee schedules linked to the distribution.""" - try: - response, status = ( - DistributionCodeService.find_fee_schedules_by_distribution_id(distribution_code_id), - HTTPStatus.OK, - ) - - except BusinessException as exception: - return exception.response() - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) - def post(distribution_code_id: int): - """Create link between distribution and fee schedule.""" - request_json = request.get_json() - - try: - DistributionCodeService.create_link(request_json, distribution_code_id) - except BusinessException as exception: - return exception.response() - return jsonify(None), HTTPStatus.CREATED + + +bp = Blueprint('DISTRIBUTIONS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees/distributions') + + +@bp.route('/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'PUT']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) +def get_fee_distribution(distribution_code_id: int): + """Return distribution by provided id.""" + try: + response, status = ( + DistributionCodeService.find_by_id(distribution_code_id), + HTTPStatus.OK, + ) + + except BusinessException as exception: + return exception.response() + return jsonify(response), status + + +@bp.route('/', methods=['PUT']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) +def put_fee_distribution(distribution_code_id: int): + """Update distribution from the payload.""" + request_json = request.get_json() + + valid_format, errors = schema_utils.validate(request_json, 'distribution_code') + if not valid_format: + return error_to_response(Error.INVALID_REQUEST, invalid_params=schema_utils.serialize(errors)) + + try: + response, status = ( + DistributionCodeService.save_or_update(request_json, distribution_code_id), + HTTPStatus.OK, + ) + except BusinessException as exception: + return exception.response() + return jsonify(response), status + + +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) +def get_fee_distributions(): + """Return all distributions.""" + try: + response, status = ( + DistributionCodeService.find_all(), + HTTPStatus.OK, + ) + + except BusinessException as exception: + return exception.response() + return jsonify(response), status + + +@bp.route('', methods=['POST']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) +def post_fee_distribution(): + """Create a new distribution from the payload.""" + request_json = request.get_json() + + valid_format, errors = schema_utils.validate(request_json, 'distribution_code') + if not valid_format: + return error_to_response(Error.INVALID_REQUEST, invalid_params=schema_utils.serialize(errors)) + + try: + response, status = ( + DistributionCodeService.save_or_update(request_json), + HTTPStatus.CREATED, + ) + except BusinessException as exception: + return exception.response() + return jsonify(response), status + + +@bp.route('//schedules', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) +def get_fee_distribution_schedules(distribution_code_id: int): + """Return all fee schedules linked to the distribution.""" + try: + response, status = ( + DistributionCodeService.find_fee_schedules_by_distribution_id(distribution_code_id), + HTTPStatus.OK, + ) + + except BusinessException as exception: + return exception.response() + return jsonify(response), status + + +@bp.route('//schedules', methods=['POST']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.has_one_of_roles([Role.MANAGE_GL_CODES.value]) +def post_fee_distribution_schedule(distribution_code_id: int): + """Create link between distribution and fee schedule.""" + request_json = request.get_json() + + try: + DistributionCodeService.create_link(request_json, distribution_code_id) + except BusinessException as exception: + return exception.response() + return jsonify(None), HTTPStatus.CREATED diff --git a/pay-api/src/pay_api/resources/endpoints.py b/pay-api/src/pay_api/resources/endpoints.py new file mode 100644 index 000000000..e5ef64532 --- /dev/null +++ b/pay-api/src/pay_api/resources/endpoints.py @@ -0,0 +1,40 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Mounting the end-points.""" +from typing import Optional + +from flask import Flask +from .v1 import v1_endpoint + + +class Endpoints: # pylint: disable=too-few-public-methods + """Manage the mounting, traversal and redirects for a set of versioned end-points.""" + + app: Optional[Flask] = None + + def init_app(self, app: Flask): + """Initialize the endpoints mapped for all services. + + Manages the versioned routes. + Sets up redirects based on Accept headers or Versioned routes. + """ + self.app = app + self._mount_endpoints() + + def _mount_endpoints(self): + """Mount the endpoints of the system.""" + v1_endpoint.init_app(self.app) + + +endpoints = Endpoints() diff --git a/pay-api/src/pay_api/resources/fas/__init__.py b/pay-api/src/pay_api/resources/fas/__init__.py index 58e2d6aea..7ec3a8f2d 100644 --- a/pay-api/src/pay_api/resources/fas/__init__.py +++ b/pay-api/src/pay_api/resources/fas/__init__.py @@ -20,5 +20,5 @@ - meta That are used to expose operational health information about the service, and meta information. """ -from .routing_slip import API as ROUTING_SLIP_API -from .refund import API as ROUTING_SLIP_REFUND_API +from .routing_slip import bp as fas_routing_slip_bp +from .refund import bp as fas_refund_bp diff --git a/pay-api/src/pay_api/resources/fas/refund.py b/pay-api/src/pay_api/resources/fas/refund.py index 596dddee6..78e098f32 100644 --- a/pay-api/src/pay_api/resources/fas/refund.py +++ b/pay-api/src/pay_api/resources/fas/refund.py @@ -14,42 +14,38 @@ """Resource for FAS Refunds endpoints.""" from http import HTTPStatus -from flask import current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services import RefundService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.util import cors_preflight -API = Namespace('fas', description='Fee Accounting System') - - -@cors_preflight('POST') -@API.route('', methods=['POST', 'OPTIONS']) -class Refund(Resource): - """Endpoint resource to create refunds against routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles( - [Role.FAS_REFUND.value, Role.FAS_REFUND_APPROVER.value]) - def post(routing_slip_number): - """Create the Refund for the Invoice.""" - current_app.logger.info('Refund.post') - return jsonify(response), HTTPStatus.ACCEPTED +bp = Blueprint('FAS_REFUNDS', __name__, + url_prefix=f'{EndpointEnum.API_V1.value}/fas/routing-slips//refunds') + + +@bp.route('', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_jwt.has_one_of_roles( + [Role.FAS_REFUND.value, Role.FAS_REFUND_APPROVER.value]) +def post_fas_refund(routing_slip_number): + """Create the Refund for the Invoice.""" + current_app.logger.info('post_fas_refund') + return jsonify(response), HTTPStatus.ACCEPTED diff --git a/pay-api/src/pay_api/resources/fas/routing_slip.py b/pay-api/src/pay_api/resources/fas/routing_slip.py index 2e52872d2..6ab11edbc 100644 --- a/pay-api/src/pay_api/resources/fas/routing_slip.py +++ b/pay-api/src/pay_api/resources/fas/routing_slip.py @@ -14,241 +14,209 @@ """Resource for Account payments endpoints.""" from http import HTTPStatus -from flask import Response, current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, Response, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, ServiceUnavailableException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services.fas import RoutingSlipService, CommentService from pay_api.utils.auth import jwt as _jwt # noqa: I005 +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight - -API = Namespace('fas', description='Fee Accounting System') - - -@cors_preflight('GET,POST') -@API.route('', methods=['GET', 'POST', 'OPTIONS']) -class RoutingSlips(Resource): - """Endpoint resource to create and return routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_CREATE.value]) - @_tracing.trace() - def post(): - """Create routing slip.""" - current_app.logger.info('RoutingSlips.post') - return jsonify(response), status - - -@cors_preflight('POST') -@API.route('/queries', methods=['POST', 'OPTIONS']) -class RoutingSlipSearch(Resource): - """Endpoint resource to search for routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_SEARCH.value]) - @_tracing.trace() - def post(): - """Get routing slips.""" - current_app.logger.info('RoutingSlips.query.post') - return jsonify(response), status - - -@cors_preflight('POST') -@API.route('//reports', methods=['POST', 'OPTIONS']) -class RoutingSlipReport(Resource): - """Endpoint resource to generate report for routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_REPORTS.value]) - @_tracing.trace() - def post(date: str): - """Create routing slip report.""" - current_app.logger.info('RoutingSlipReport.post') - return response - - -@cors_preflight('GET,PATCH') -@API.route('/', methods=['GET', 'PATCH', 'OPTIONS']) -class RoutingSlip(Resource): - """Endpoint resource update and return routing slip by number.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) - @_tracing.trace() - def get(routing_slip_number: str): - """Get routing slip.""" - current_app.logger.info('RoutingSlips.get') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_EDIT.value]) - @_tracing.trace() - def patch(routing_slip_number: str): - """Patch routing slip.""" - current_app.logger.info('RoutingSlips.patch') - return jsonify(response), status - - -@cors_preflight('GET') -@API.route('//links', methods=['GET', 'OPTIONS']) -class RoutingSlipLink(Resource): - """Endpoint resource to deal with links in routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_VIEW.value, Role.FAS_LINK.value]) - @_tracing.trace() - def get(routing_slip_number: str): - """Get routing slip links ;ie parent/child details.""" - current_app.logger.info('post_routing_slip') + return jsonify(response), status + + +@bp.route('/queries', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.FAS_SEARCH.value]) +def post_search_routing_slips(): + """Get routing slips.""" + current_app.logger.info('post_search_routing_slips') + return jsonify(response), status + + +@bp.route('//reports', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.FAS_REPORTS.value]) +def post_routing_slip_report(date: str): + """Create routing slip report.""" + current_app.logger.info('post_routing_slip_report') + return response + + +@bp.route('/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'PATCH']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.FAS_VIEW.value]) +def get_routing_slip(routing_slip_number: str): + """Get routing slip.""" + current_app.logger.info('RoutingSlipLink.get') - return jsonify(response), status - - -@cors_preflight('POST') -@API.route('/links', methods=['POST', 'OPTIONS']) -class RoutingSlipLinks(Resource): - """Endpoint resource to deal with links in routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_LINK.value]) - @_tracing.trace() - def post(): - """Get routing slip links ;ie parent/child details.""" - current_app.logger.info('RoutingSlipLink.post') - return jsonify(response), status - - -@cors_preflight('POST') -@API.route('//comments', methods=['POST', 'GET', 'OPTIONS']) -class RoutingSlipComment(Resource): - """Endpoint resource to create/get comments for routing slips.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) - @_tracing.trace() - def post(routing_slip_number: str): - """Create comment for a slip.""" - current_app.logger.info('get_routing_slip') + return jsonify(response), status + + +@bp.route('/', methods=['PATCH']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.has_one_of_roles([Role.FAS_EDIT.value]) +def patch_routing_slip(routing_slip_number: str): + """Patch routing slip.""" + current_app.logger.info('patch_routing_slip') + return jsonify(response), status + + +@bp.route('//links', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.FAS_VIEW.value, Role.FAS_LINK.value]) +def get_routing_slip_links(routing_slip_number: str): + """Get routing slip links ;ie parent/child details.""" + current_app.logger.info('get_routing_slip_links') + return jsonify(response), status + + +@bp.route('/links', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.FAS_LINK.value]) +def post_routing_slip_link(): + """Get routing slip links ;ie parent/child details.""" + current_app.logger.info('post_routing_slip_link') + return jsonify(response), status + + +@bp.route('//comments', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_jwt.has_one_of_roles([Role.FAS_VIEW.value]) +@_tracing.trace() +def get_routing_slip_comments(routing_slip_number: str): + """Get comments for a slip.""" + current_app.logger.info('get_routing_slip_comments') + return jsonify(response), status + + +@bp.route('//comments', methods=['POST']) +@cross_origin(origins='*') +@_jwt.has_one_of_roles([Role.FAS_VIEW.value]) +@_tracing.trace() +def post_routing_slip_comment(routing_slip_number: str): + """Create comment for a slip.""" + current_app.logger.info('Comment.post.request') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.has_one_of_roles([Role.FAS_VIEW.value]) - @_tracing.trace() - def get(routing_slip_number: str): - """Get comments for a slip.""" - current_app.logger.info('Comment.get.request') - return jsonify(response), status + return error_to_response(Error.INVALID_REQUEST, invalid_params=schema_utils.serialize(errors)) + if comment: + response, status = \ + CommentService.create(comment_value=comment, rs_number=routing_slip_number), HTTPStatus.CREATED + except (BusinessException, ServiceUnavailableException) as exception: + return exception.response() + + current_app.logger.debug('>post_routing_slip_comment') + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/fee.py b/pay-api/src/pay_api/resources/fee.py index 8b57b61f9..4cba89a26 100644 --- a/pay-api/src/pay_api/resources/fee.py +++ b/pay-api/src/pay_api/resources/fee.py @@ -15,55 +15,51 @@ from datetime import datetime from http import HTTPStatus -from flask import jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException from pay_api.services import FeeSchedule from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import DEFAULT_JURISDICTION, DT_SHORT_FORMAT +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import convert_to_bool, cors_preflight +from pay_api.utils.util import convert_to_bool -API = Namespace('fees', description='Payment System - Fees') +bp = Blueprint('FEES', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees') -@cors_preflight('GET') -@API.route('//', methods=['GET', 'OPTIONS']) -class Fee(Resource): - """Endpoint resource to calculate fee.""" +@bp.route('//', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +@_jwt.has_one_of_roles([Role.VIEWER.value, Role.EDITOR.value, Role.STAFF.value]) +def get_fee_by_corp_and_filing_type(corp_type, filing_type_code): + """Calculate the fee for the filing using the corp type/filing type and return fee.""" + date = request.args.get('date', datetime.today().strftime(DT_SHORT_FORMAT)) + is_priority = convert_to_bool(request.args.get('priority', 'False')) + is_future_effective = convert_to_bool(request.args.get('futureEffective', 'False')) + jurisdiction = request.args.get('jurisdiction', DEFAULT_JURISDICTION) + quantity = int(request.args.get('quantity', 1)) + waive_fees = False + if _jwt.validate_roles([Role.STAFF.value]): + waive_fees = convert_to_bool(request.args.get('waiveFees', 'False')) - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - @_jwt.has_one_of_roles([Role.VIEWER.value, Role.EDITOR.value, Role.STAFF.value]) - def get(corp_type, filing_type_code): - """Calculate the fee for the filing using the corp type/filing type and return fee.""" - date = request.args.get('date', datetime.today().strftime(DT_SHORT_FORMAT)) - is_priority = convert_to_bool(request.args.get('priority', 'False')) - is_future_effective = convert_to_bool(request.args.get('futureEffective', 'False')) - jurisdiction = request.args.get('jurisdiction', DEFAULT_JURISDICTION) - quantity = int(request.args.get('quantity', 1)) - waive_fees = False - if _jwt.validate_roles([Role.STAFF.value]): - waive_fees = convert_to_bool(request.args.get('waiveFees', 'False')) - - try: - response, status = ( - FeeSchedule.find_by_corp_type_and_filing_type( - corp_type=corp_type, - filing_type_code=filing_type_code, - valid_date=date, - jurisdiction=jurisdiction, - is_priority=is_priority, - is_future_effective=is_future_effective, - waive_fees=waive_fees, - quantity=quantity - ).asdict(), - HTTPStatus.OK, - ) - except BusinessException as exception: - return exception.response() - return jsonify(response), status + try: + response, status = ( + FeeSchedule.find_by_corp_type_and_filing_type( + corp_type=corp_type, + filing_type_code=filing_type_code, + valid_date=date, + jurisdiction=jurisdiction, + is_priority=is_priority, + is_future_effective=is_future_effective, + waive_fees=waive_fees, + quantity=quantity + ).asdict(), + HTTPStatus.OK, + ) + except BusinessException as exception: + return exception.response() + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/fee_schedule.py b/pay-api/src/pay_api/resources/fee_schedule.py index 88b017847..d26dccea0 100644 --- a/pay-api/src/pay_api/resources/fee_schedule.py +++ b/pay-api/src/pay_api/resources/fee_schedule.py @@ -14,35 +14,30 @@ """Resource for Fee Calculation endpoints.""" from http import HTTPStatus -from flask import jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException from pay_api.services import FeeSchedule +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight -API = Namespace('fee-schedules', description='Payment System - Fee Schedules') +bp = Blueprint('FEE_SCHEDULE', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/fees/schedules') -@cors_preflight('GET') -@API.route('', methods=['GET', 'OPTIONS']) -class FeeSchedules(Resource): - """Endpoint resource to calculate fee.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - def get(): - """Calculate the fee for the filing using the corp type/filing type and return fee.""" - try: - corp_type = request.args.get('corp_type', None) - filing_type = request.args.get('filing_type', None) - description = request.args.get('description', None) - response, status = ( - FeeSchedule.find_all(corp_type=corp_type, filing_type_code=filing_type, description=description), - HTTPStatus.OK, - ) - except BusinessException as exception: - return exception.response() - return jsonify(response), status +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +def get_fee_schedules(): + """Calculate the fee for the filing using the corp type/filing type and return fee.""" + try: + corp_type = request.args.get('corp_type', None) + filing_type = request.args.get('filing_type', None) + description = request.args.get('description', None) + response, status = ( + FeeSchedule.find_all(corp_type=corp_type, filing_type_code=filing_type, description=description), + HTTPStatus.OK, + ) + except BusinessException as exception: + return exception.response() + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/invoice.py b/pay-api/src/pay_api/resources/invoice.py index 8bbcd9b44..fb3b20d65 100644 --- a/pay-api/src/pay_api/resources/invoice.py +++ b/pay-api/src/pay_api/resources/invoice.py @@ -14,8 +14,8 @@ """Resource for Payment Request/Invoice endpoints.""" from http import HTTPStatus -from flask import Response, current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, Response, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, ServiceUnavailableException, error_to_response from pay_api.schemas import utils as schema_utils @@ -24,146 +24,135 @@ from pay_api.services.invoice import Invoice as InvoiceService from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import MAKE_PAYMENT +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight, get_str_by_path - -API = Namespace('invoice', description='Payment System - Invoices') - - -@cors_preflight('POST,GET') -@API.route('', methods=['POST', 'GET', 'OPTIONS']) -class Invoice(Resource): - """Endpoint resource to create and get invoice.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(): - """Create the payment request records.""" - request_json = request.get_json() - current_app.logger.debug(f'Invoice.post') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_roles([Role.SYSTEM.value]) - @_tracing.trace() - def get(): - """Get the invoice records.""" - current_app.logger.info('Invoice.get') - return jsonify(response), status - - -@cors_preflight(['GET', 'PUT', 'DELETE', 'PATCH']) -@API.route('/', methods=['GET', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']) -class Invoices(Resource): - """Endpoint resource to create payment request.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(invoice_id): - """Get the invoice records.""" - try: - response, status = InvoiceService.find_by_id(invoice_id).asdict(include_dynamic_fields=True), HTTPStatus.OK - except BusinessException as exception: - return exception.response() - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def delete(invoice_id): - """Soft delete the invoice records.""" - current_app.logger.info('Invoices.delete') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def patch(invoice_id: int = None): - """Update the payment method for an online banking .""" - current_app.logger.info('Invoices.post') - return jsonify(response), status - - -@cors_preflight(['POST']) -@API.route('//reports', methods=['POST', 'OPTIONS']) -class InvoiceReport(Resource): - """Endpoint resource to create invoice PDF.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(invoice_id: int = None): - """Update the payment method for an online banking .""" - current_app.logger.info('InvoiceReport.post') - return jsonify(response), 200 +from pay_api.utils.util import get_str_by_path + +bp = Blueprint('INVOICE', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/payment-requests') + + +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.requires_roles([Role.SYSTEM.value]) +def get_invoices(): + """Get the invoice records.""" + current_app.logger.info('get_invoices') + return jsonify(response), status + + +@bp.route('', methods=['POST']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.requires_auth +def post_invoice(): + """Create the payment request records.""" + request_json = request.get_json() + current_app.logger.debug(f'post_invoice') + return jsonify(response), status + + +@bp.route('/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'DELETE', 'PATCH']) +@_tracing.trace() +@_jwt.requires_auth +def get_invoice(invoice_id): + """Get the invoice records.""" + try: + response, status = InvoiceService.find_by_id(invoice_id).asdict(include_dynamic_fields=True), HTTPStatus.OK + except BusinessException as exception: + return exception.response() + return jsonify(response), status + + +@bp.route('/', methods=['DELETE']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.requires_auth +def delete_invoice(invoice_id): + """Soft delete the invoice records.""" + current_app.logger.info('delete_invoice') + return jsonify(response), status + + +@bp.route('/', methods=['PATCH']) +@cross_origin(origins='*') +@_tracing.trace() +@_jwt.requires_auth +def patch_invoice(invoice_id: int = None): + """Update the payment method for an online banking .""" + current_app.logger.info('patch_invoice') + return jsonify(response), status + + +@bp.route('//reports', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_jwt.requires_auth +@_tracing.trace() +def post_invoice_report(invoice_id: int = None): + """Update the payment method for an online banking .""" + current_app.logger.info('post_invoice_report') + return jsonify(response), 200 diff --git a/pay-api/src/pay_api/resources/invoice_receipt.py b/pay-api/src/pay_api/resources/invoice_receipt.py index 49eed630b..b741b0732 100644 --- a/pay-api/src/pay_api/resources/invoice_receipt.py +++ b/pay-api/src/pay_api/resources/invoice_receipt.py @@ -13,63 +13,60 @@ # limitations under the License. """Resource for Transaction endpoints.""" -from flask import Response, current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, Response, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services import ReceiptService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error -from pay_api.utils.util import cors_preflight -API = Namespace('invoice-receipts', description='Payment System - Receipts') +bp = Blueprint('INVOICE_RECEIPTS', __name__, + url_prefix=f'{EndpointEnum.API_V1.value}/payment-requests/') -@cors_preflight('POST,GET') -@API.route('/receipts', methods=['GET', 'POST', 'OPTIONS']) -class InvoiceReceipt(Resource): - """Endpoint resource to create receipt.Use this endpoint when no invoice number is available.""" +@bp.route('/receipts', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_jwt.requires_auth +def get_invoice_receipt(invoice_id): + """Return the receipt details.""" + current_app.logger.info('get_invoice_receipt') + return jsonify(receipt_details), 200 - pdf = ReceiptService.create_receipt(invoice_id, request_json) - current_app.logger.info('Transaction.post') - return jsonify(response), 200 +@bp.route('/receipts', methods=['POST']) +@cross_origin(origins='*') +@_jwt.requires_auth +def post_invoice_receipt(invoice_id): + """Create the Receipt for the Invoice.""" + request_json = request.get_json() + current_app.logger.info('Transaction.post') - return jsonify(receipt_details), 200 + except BusinessException as exception: + return exception.response() + current_app.logger.debug('>post_invoice_receipt') + return jsonify(response), 200 diff --git a/pay-api/src/pay_api/resources/invoices.py b/pay-api/src/pay_api/resources/invoices.py index b32dcd773..85d866b5f 100644 --- a/pay-api/src/pay_api/resources/invoices.py +++ b/pay-api/src/pay_api/resources/invoices.py @@ -14,36 +14,32 @@ """Resource for Invoice endpoints.""" from http import HTTPStatus -from flask import jsonify -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, jsonify +from flask_cors import cross_origin from pay_api.exceptions import BusinessException from pay_api.services import InvoiceService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight -API = Namespace('invoices', description='Payment System - Payment Requests') +bp = Blueprint('INVOICES', __name__, + url_prefix=f'{EndpointEnum.API_V1.value}/payment-requests//invoices') -@cors_preflight(['GET']) -@API.route('', methods=['GET', 'OPTIONS'], doc={'deprecated': True}) -class PaymentRequestInvoice(Resource): - """Temporary endpoint to unblock teams who are using this endpoint.""" +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET']) +@_tracing.trace() +@_jwt.requires_auth +def get_invoice_by_id(invoice_id): + """Subject to remove once the change has been notified to teams.""" + try: + response = { + 'items': [] + } - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(invoice_id): - """Subject to remove once the change has been notified to teams.""" - try: - response = { - 'items': [] - } - - response['items'].append(InvoiceService.find_by_id(invoice_id).asdict()) - except BusinessException as exception: - return exception.response() - return jsonify(response), HTTPStatus.OK + response['items'].append(InvoiceService.find_by_id(invoice_id).asdict()) + except BusinessException as exception: + return exception.response() + return jsonify(response), HTTPStatus.OK diff --git a/pay-api/src/pay_api/resources/meta.py b/pay-api/src/pay_api/resources/meta.py index 3e72d68b8..aedb19294 100755 --- a/pay-api/src/pay_api/resources/meta.py +++ b/pay-api/src/pay_api/resources/meta.py @@ -15,21 +15,17 @@ Currently this only provides API versioning information """ -from flask import jsonify -from flask_restx import Namespace, Resource +from flask import Blueprint, jsonify +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.run_version import get_run_version -API = Namespace('Meta', description='Metadata') +bp = Blueprint('META', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/meta') -@API.route('/info') -class Info(Resource): - """Meta information about the overall service.""" - - @staticmethod - def get(): - """Return a JSON object with meta information about the Service.""" - version = get_run_version() - return jsonify(API=f'pay_api/{version}') +@bp.route('/info') +def get(): + """Return a JSON object with meta information about the Service.""" + version = get_run_version() + return jsonify(API=f'pay_api/{version}') diff --git a/pay-api/src/pay_api/resources/ops.py b/pay-api/src/pay_api/resources/ops.py index 70eb133fd..5441be70a 100755 --- a/pay-api/src/pay_api/resources/ops.py +++ b/pay-api/src/pay_api/resources/ops.py @@ -12,42 +12,31 @@ # See the License for the specific language governing permissions and # limitations under the License. """Endpoints to check and manage the health of the service.""" -from flask_restx import Namespace, Resource +from flask import Blueprint from sqlalchemy import exc, text from pay_api.models import db -API = Namespace('OPS', description='Service - OPS checks') +bp = Blueprint('OPS', __name__, url_prefix='/ops') SQL = text('select 1') -@API.route('healthz') -class Healthz(Resource): - """Determines if the service and required dependencies are still working. +@bp.route('healthz') +def get_ops_healthz(): + """Return a JSON object stating the health of the Service and dependencies.""" + try: + db.engine.execute(SQL) + except exc.SQLAlchemyError: + return {'message': 'api is down'}, 500 - This could be thought of as a heartbeat for the service. - """ + # made it here, so all checks passed + return {'message': 'api is healthy'}, 200 - @staticmethod - def get(): - """Return a JSON object stating the health of the Service and dependencies.""" - try: - db.engine.execute(SQL) - except exc.SQLAlchemyError: - return {'message': 'api is down'}, 500 - # made it here, so all checks passed - return {'message': 'api is healthy'}, 200 - - -@API.route('readyz') -class Readyz(Resource): - """Determines if the service is ready to respond.""" - - @staticmethod - def get(): - """Return a JSON object that identifies if the service is setupAnd ready to work.""" - # TODO: add a poll to the DB when called - return {'message': 'api is ready'}, 200 +@bp.route('readyz') +def get_ops_readyz(): + """Return a JSON object that identifies if the service is setupAnd ready to work.""" + # TODO: add a poll to the DB when called + return {'message': 'api is ready'}, 200 diff --git a/pay-api/src/pay_api/resources/payment.py b/pay-api/src/pay_api/resources/payment.py index e8166dbb2..9e4f6afd9 100644 --- a/pay-api/src/pay_api/resources/payment.py +++ b/pay-api/src/pay_api/resources/payment.py @@ -14,8 +14,8 @@ """Resource for Account payments endpoints.""" from http import HTTPStatus -from flask import current_app, g, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, current_app, g, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import error_to_response from pay_api.schemas import utils as schema_utils @@ -23,67 +23,63 @@ from pay_api.services.auth import check_auth from pay_api.utils.auth import jwt as _jwt from pay_api.utils.constants import EDIT_ROLE, MAKE_PAYMENT, VIEW_ROLE +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import PaymentMethod, Role from pay_api.utils.errors import Error from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight -API = Namespace('payment', description='Payment System - Payments') +bp = Blueprint('PAYMENTS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/accounts//payments') -@cors_preflight('GET,POST') -@API.route('', methods=['GET', 'POST', 'OPTIONS']) -class Payments(Resource): - """Endpoint resource to create and return an account payments.""" +@bp.route('', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.requires_auth +def get_account_payments(account_id: str): + """Get account payments.""" + current_app.logger.info('get_account_payments') + return jsonify(response), status - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(account_id: str): - """Get account payments.""" - current_app.logger.info('Payments.get') - return jsonify(response), status - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def post(account_id: str): - """Create account payments.""" - current_app.logger.info('Payments.post') - return jsonify(response), status + current_app.logger.debug('>post_account_payment') + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/refund.py b/pay-api/src/pay_api/resources/refund.py index 17e27a560..b1cb75bc4 100644 --- a/pay-api/src/pay_api/resources/refund.py +++ b/pay-api/src/pay_api/resources/refund.py @@ -14,40 +14,35 @@ """Resource for Refunds endpoints.""" from http import HTTPStatus -from flask import current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services import RefundService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.enums import Role from pay_api.utils.errors import Error -from pay_api.utils.util import cors_preflight -API = Namespace('refunds', description='Payment System - Refunds') +bp = Blueprint('REFUNDS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}/payment-requests/') -@cors_preflight('POST') -@API.route('/refunds', methods=['POST', 'OPTIONS']) -class Refund(Resource): - """Endpoint resource to create refunds against invoices.""" +@bp.route('/refunds', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_jwt.has_one_of_roles([Role.SYSTEM.value, Role.CREATE_CREDITS.value, Role.FAS_REFUND.value]) +def post_refund(invoice_id): + """Create the Refund for the Invoice.""" + current_app.logger.info(f'Refund.post : {invoice_id}') - return jsonify(response), HTTPStatus.ACCEPTED + except BusinessException as exception: + return exception.response() + current_app.logger.debug(f'>post_refund : {invoice_id}') + return jsonify(response), HTTPStatus.ACCEPTED diff --git a/pay-api/src/pay_api/resources/transaction.py b/pay-api/src/pay_api/resources/transaction.py index 5baa347c2..c35aba958 100644 --- a/pay-api/src/pay_api/resources/transaction.py +++ b/pay-api/src/pay_api/resources/transaction.py @@ -14,103 +14,96 @@ """Resource for Transaction endpoints.""" from http import HTTPStatus -from flask import current_app, jsonify, request -from flask_restx import Namespace, Resource, cors +from flask import Blueprint, current_app, jsonify, request +from flask_cors import cross_origin from pay_api.exceptions import BusinessException, error_to_response from pay_api.schemas import utils as schema_utils from pay_api.services import TransactionService from pay_api.utils.auth import jwt as _jwt +from pay_api.utils.endpoints_enums import EndpointEnum from pay_api.utils.errors import Error from pay_api.utils.trace import tracing as _tracing -from pay_api.utils.util import cors_preflight - - -API = Namespace('', description='Payment System - Transactions') - - -@cors_preflight('POST,GET') -@API.route('/payment-requests//transactions', methods=['GET', 'POST', 'OPTIONS']) -@API.route('/payments//transactions', methods=['GET', 'POST', 'OPTIONS']) -class Transaction(Resource): - """Endpoint resource to create transaction.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - def post(invoice_id: int = None, payment_id: int = None): - """Create the Transaction records.""" - current_app.logger.info('Transaction.post') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(invoice_id): - """Get all transaction records for a invoice.""" - current_app.logger.info('Transaction.get') - return jsonify(response), status - - -@cors_preflight('PATCH,GET') -@API.route('/payment-requests//transactions/', methods=['GET', 'PATCH', 'OPTIONS']) -@API.route('/payments//transactions/', methods=['GET', 'PATCH', 'OPTIONS']) -class Transactions(Resource): - """Endpoint resource to get transaction.""" - - @staticmethod - @cors.crossdomain(origin='*') - @_jwt.requires_auth - @_tracing.trace() - def get(invoice_id: int = None, payment_id: int = None, transaction_id=None): - """Get the Transaction record.""" - current_app.logger.info('Transaction.get') - return jsonify(response), status - - @staticmethod - @cors.crossdomain(origin='*') - @_tracing.trace() - def patch(invoice_id: int = None, payment_id: int = None, transaction_id=None): - """Update the transaction record by querying payment system.""" - current_app.logger.info('Transaction.post') - return jsonify(response), status + + +bp = Blueprint('TRANSACTIONS', __name__, url_prefix=f'{EndpointEnum.API_V1.value}') + + +@bp.route('/payment-requests//transactions', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'POST']) +@_tracing.trace() +@_jwt.requires_auth +def get_transactions(invoice_id): + """Get all transaction records for a invoice.""" + current_app.logger.info('get_transactions') + return jsonify(response), status + + +@bp.route('/payment-requests//transactions', methods=['POST']) +@bp.route('/payments//transactions', methods=['POST', 'OPTIONS']) +@cross_origin(origins='*', methods=['POST']) +@_tracing.trace() +def post_transaction(invoice_id: int = None, payment_id: int = None): + """Create the Transaction records.""" + current_app.logger.info('post_transaction') + return jsonify(response), status + + +@bp.route('/payment-requests//transactions/', methods=['GET', 'OPTIONS']) +@bp.route('/payments//transactions/', methods=['GET', 'OPTIONS']) +@cross_origin(origins='*', methods=['GET', 'PATCH']) +@_tracing.trace() +@_jwt.requires_auth +def get_transaction(invoice_id: int = None, payment_id: int = None, transaction_id=None): + """Get the Transaction record.""" + current_app.logger.info('get_transaction') + return jsonify(response), status + + +@bp.route('/payment-requests//transactions/', methods=['PATCH']) +@bp.route('/payments//transactions/', methods=['PATCH']) +@cross_origin(origins='*') +@_tracing.trace() +def patch_transaction(invoice_id: int = None, payment_id: int = None, transaction_id=None): + """Update the transaction record by querying payment system.""" + current_app.logger.info('patch_transaction') + return jsonify(response), status diff --git a/pay-api/src/pay_api/resources/v1/__init__.py b/pay-api/src/pay_api/resources/v1/__init__.py new file mode 100644 index 000000000..89cc9577c --- /dev/null +++ b/pay-api/src/pay_api/resources/v1/__init__.py @@ -0,0 +1,73 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Exposes all of the resource endpoints mounted in Flask-Blueprints.""" +from typing import Optional + +from flask import Flask + +from ..account import bp as account_bp +from ..account_statements import bp as account_statements_bp +from ..account_statements_notifications import bp as account_notifications_bp +from ..account_statements_settings import bp as account_settings_bp +from ..bank_accounts import bp as bank_accounts_bp +from ..code import bp as code_bp +from ..distributions import bp as distributions_bp +from ..fas import fas_refund_bp, fas_routing_slip_bp +from ..fee import bp as fee_bp +from ..fee_schedule import bp as fee_schedule_bp +from ..invoice import bp as invoice_bp +from ..invoice_receipt import bp as invoice_receipt_bp +from ..invoices import bp as invoices_bp +from ..meta import bp as meta_bp +from ..ops import bp as ops_bp +from ..payment import bp as payment_bp +from ..refund import bp as refund_bp +from ..transaction import bp as transaction_bp + + +class V1Endpoint: # pylint: disable=too-few-public-methods, + """Setup all the V1 Endpoints.""" + + def __init__(self): + """Create the endpoint setup, without initializations.""" + self.app: Optional[Flask] = None + + def init_app(self, app): + """Register and initialize the Endpoint setup.""" + if not app: + raise Exception('Cannot initialize without a Flask App.') # pylint: disable=broad-exception-raised + + self.app = app + self.app.register_blueprint(account_bp) + self.app.register_blueprint(account_notifications_bp) + self.app.register_blueprint(account_settings_bp) + self.app.register_blueprint(account_statements_bp) + self.app.register_blueprint(bank_accounts_bp) + self.app.register_blueprint(code_bp) + self.app.register_blueprint(distributions_bp) + self.app.register_blueprint(fas_refund_bp) + self.app.register_blueprint(fas_routing_slip_bp) + self.app.register_blueprint(fee_bp) + self.app.register_blueprint(fee_schedule_bp) + self.app.register_blueprint(invoice_bp) + self.app.register_blueprint(invoices_bp) + self.app.register_blueprint(invoice_receipt_bp) + self.app.register_blueprint(meta_bp) + self.app.register_blueprint(ops_bp) + self.app.register_blueprint(payment_bp) + self.app.register_blueprint(refund_bp) + self.app.register_blueprint(transaction_bp) + + +v1_endpoint = V1Endpoint() diff --git a/pay-api/src/pay_api/utils/endpoints_enums.py b/pay-api/src/pay_api/utils/endpoints_enums.py new file mode 100644 index 000000000..04abba138 --- /dev/null +++ b/pay-api/src/pay_api/utils/endpoints_enums.py @@ -0,0 +1,24 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Enum definitions for endpoints.""" +from enum import Enum + + +class EndpointEnum(str, Enum): + """Endpoint route url paths.""" + + API_V1 = '/api/v1' + API = '/api' + TEST_API = '/test' + DEFAULT_API = API_V1 diff --git a/pay-api/tests/unit/api/test_cors_preflight.py b/pay-api/tests/unit/api/test_cors_preflight.py new file mode 100644 index 000000000..81afc7967 --- /dev/null +++ b/pay-api/tests/unit/api/test_cors_preflight.py @@ -0,0 +1,223 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests to verify the preflight requests on API end-points. + +Test-Suite to ensure that the cors flight responses are working as expected. +""" + + +def test_preflight_fas_refund(app, client, jwt, session): + """Assert preflight responses for fas refunds are correct.""" + rv = client.options('/api/v1/fas/routing-slips/1/refunds', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + +def test_preflight_fas_routing_slip(app, client, jwt, session): + """Assert preflight responses for fas routing slips are correct.""" + rv = client.options('/api/v1/fas/routing-slips', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + rv = client.options('/api/v1/fas/routing-slips/queries', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + rv = client.options('/api/v1/fas/routing-slips/2023-09-05/reports', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + rv = client.options('/api/v1/fas/routing-slips/1', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, PATCH') + + rv = client.options('/api/v1/fas/routing-slips/1/links', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + rv = client.options('/api/v1/fas/routing-slips/links', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + rv = client.options('/api/v1/fas/routing-slips/1/comments', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + +def test_preflight_account(app, client, jwt, session): + """Assert preflight responses for accounts are correct.""" + rv = client.options('/api/v1/accounts', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + rv = client.options('/api/v1/accounts/1', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'DELETE, GET, PUT') + + rv = client.options('/api/v1/accounts/1/fees', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + rv = client.options('/api/v1/accounts/1/fees/PRODUCT_CODE', + headers={'Access-Control-Request-Method': 'PUT'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'PUT') + + rv = client.options('/api/v1/accounts/1/payments/queries', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + rv = client.options('/api/v1/accounts/1/payments/reports', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + +def test_preflight_account_statements(app, client, jwt, session): + """Assert preflight responses for account statements are correct.""" + rv = client.options('/api/v1/accounts/1/statements', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + rv = client.options('/api/v1/accounts/1/statements/1', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + +def test_preflight_account_statement_notifications(app, client, jwt, session): + """Assert preflight responses for account statement notifications are correct.""" + rv = client.options('/api/v1/accounts/1/statements/notifications', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + +def test_preflight_account_statement_settings(app, client, jwt, session): + """Assert preflight responses for account statement settings are correct.""" + rv = client.options('/api/v1/accounts/1/statements/settings', + headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + +def test_preflight_bank_accounts(app, client, jwt, session): + """Assert preflight responses for bank accounts are correct.""" + rv = client.options('/api/v1/bank-accounts/verifications', + headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + +def test_preflight_code(app, client, jwt, session): + """Assert preflight responses for codes are correct.""" + rv = client.options('/api/v1/codes/CODETYPE', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + rv = client.options('/api/v1/codes/CODETYPE/CODE', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + +def test_preflight_distributions(app, client, jwt, session): + """Assert preflight responses for distributions are correct.""" + rv = client.options('/api/v1/fees/distributions', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + rv = client.options('/api/v1/fees/distributions/1', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, PUT') + + rv = client.options('/api/v1/fees/distributions/1/schedules', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + +def test_preflight_fee(app, client, jwt, session): + """Assert preflight responses for fees are correct.""" + rv = client.options('/api/v1/fees/COR_TYPE/FILING_TYPE_CODE', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + +def test_preflight_fee_schedule(app, client, jwt, session): + """Assert preflight responses for fee schedule are correct.""" + rv = client.options('/api/v1/fees/schedules', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + +def test_preflight_invoice(app, client, jwt, session): + """Assert preflight responses for invoice are correct.""" + rv = client.options('/api/v1/payment-requests', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + rv = client.options('/api/v1/payment-requests/1', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'DELETE, GET, PATCH') + + rv = client.options('/api/v1/payment-requests/1/reports', headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + +def test_preflight_invoice_receipt(app, client, jwt, session): + """Assert preflight responses for invoice receipt are correct.""" + rv = client.options('/api/v1/payment-requests/1/receipts', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + +def test_preflight_invoices(app, client, jwt, session): + """Assert preflight responses for invoices are correct.""" + rv = client.options('/api/v1/payment-requests/1/invoices', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET') + + +def test_preflight_payment(app, client, jwt, session): + """Assert preflight responses for payments are correct.""" + rv = client.options('/api/v1/accounts/1/payments', headers={'Access-Control-Request-Method': 'GET'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'GET, POST') + + +def test_preflight_refund(app, client, jwt, session): + """Assert preflight responses for refund are correct.""" + rv = client.options('/api/v1/payment-requests/1/refunds', headers={'Access-Control-Request-Method': 'POST'}) + assert rv.status_code == 200 + assert_access_control_headers(rv, '*', 'POST') + + +def assert_access_control_headers(rv, origins: str, methods: str): + """Assert access control headers are correct.""" + assert rv.headers['Access-Control-Allow-Origin'] == origins + assert rv.headers['Access-Control-Allow-Methods'] == methods From 0bb243109d4f9058f631376f096c509318b4c77f Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Tue, 5 Sep 2023 13:10:39 -0700 Subject: [PATCH 2/6] 15687 - move routes into v1 folder --- pay-api/src/pay_api/resources/v1/__init__.py | 34 +++++++++---------- .../src/pay_api/resources/{ => v1}/account.py | 0 .../resources/{ => v1}/account_statements.py | 0 .../account_statements_notifications.py | 0 .../{ => v1}/account_statements_settings.py | 0 .../resources/{ => v1}/bank_accounts.py | 0 .../src/pay_api/resources/{ => v1}/code.py | 0 .../resources/{ => v1}/distributions.py | 0 .../resources/{ => v1}/fas/__init__.py | 0 .../pay_api/resources/{ => v1}/fas/refund.py | 0 .../resources/{ => v1}/fas/routing_slip.py | 0 pay-api/src/pay_api/resources/{ => v1}/fee.py | 0 .../resources/{ => v1}/fee_schedule.py | 0 .../src/pay_api/resources/{ => v1}/invoice.py | 0 .../resources/{ => v1}/invoice_receipt.py | 0 .../pay_api/resources/{ => v1}/invoices.py | 0 .../src/pay_api/resources/{ => v1}/meta.py | 0 .../src/pay_api/resources/{ => v1}/payment.py | 0 .../src/pay_api/resources/{ => v1}/refund.py | 0 .../pay_api/resources/{ => v1}/transaction.py | 0 20 files changed, 17 insertions(+), 17 deletions(-) rename pay-api/src/pay_api/resources/{ => v1}/account.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/account_statements.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/account_statements_notifications.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/account_statements_settings.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/bank_accounts.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/code.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/distributions.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/fas/__init__.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/fas/refund.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/fas/routing_slip.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/fee.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/fee_schedule.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/invoice.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/invoice_receipt.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/invoices.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/meta.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/payment.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/refund.py (100%) rename pay-api/src/pay_api/resources/{ => v1}/transaction.py (100%) diff --git a/pay-api/src/pay_api/resources/v1/__init__.py b/pay-api/src/pay_api/resources/v1/__init__.py index 89cc9577c..340084d4a 100644 --- a/pay-api/src/pay_api/resources/v1/__init__.py +++ b/pay-api/src/pay_api/resources/v1/__init__.py @@ -16,24 +16,24 @@ from flask import Flask -from ..account import bp as account_bp -from ..account_statements import bp as account_statements_bp -from ..account_statements_notifications import bp as account_notifications_bp -from ..account_statements_settings import bp as account_settings_bp -from ..bank_accounts import bp as bank_accounts_bp -from ..code import bp as code_bp -from ..distributions import bp as distributions_bp -from ..fas import fas_refund_bp, fas_routing_slip_bp -from ..fee import bp as fee_bp -from ..fee_schedule import bp as fee_schedule_bp -from ..invoice import bp as invoice_bp -from ..invoice_receipt import bp as invoice_receipt_bp -from ..invoices import bp as invoices_bp -from ..meta import bp as meta_bp +from .account import bp as account_bp +from .account_statements import bp as account_statements_bp +from .account_statements_notifications import bp as account_notifications_bp +from .account_statements_settings import bp as account_settings_bp +from .bank_accounts import bp as bank_accounts_bp +from .code import bp as code_bp +from .distributions import bp as distributions_bp +from .fas import fas_refund_bp, fas_routing_slip_bp +from .fee import bp as fee_bp +from .fee_schedule import bp as fee_schedule_bp +from .invoice import bp as invoice_bp +from .invoice_receipt import bp as invoice_receipt_bp +from .invoices import bp as invoices_bp +from .meta import bp as meta_bp from ..ops import bp as ops_bp -from ..payment import bp as payment_bp -from ..refund import bp as refund_bp -from ..transaction import bp as transaction_bp +from .payment import bp as payment_bp +from .refund import bp as refund_bp +from .transaction import bp as transaction_bp class V1Endpoint: # pylint: disable=too-few-public-methods, diff --git a/pay-api/src/pay_api/resources/account.py b/pay-api/src/pay_api/resources/v1/account.py similarity index 100% rename from pay-api/src/pay_api/resources/account.py rename to pay-api/src/pay_api/resources/v1/account.py diff --git a/pay-api/src/pay_api/resources/account_statements.py b/pay-api/src/pay_api/resources/v1/account_statements.py similarity index 100% rename from pay-api/src/pay_api/resources/account_statements.py rename to pay-api/src/pay_api/resources/v1/account_statements.py diff --git a/pay-api/src/pay_api/resources/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py similarity index 100% rename from pay-api/src/pay_api/resources/account_statements_notifications.py rename to pay-api/src/pay_api/resources/v1/account_statements_notifications.py diff --git a/pay-api/src/pay_api/resources/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py similarity index 100% rename from pay-api/src/pay_api/resources/account_statements_settings.py rename to pay-api/src/pay_api/resources/v1/account_statements_settings.py diff --git a/pay-api/src/pay_api/resources/bank_accounts.py b/pay-api/src/pay_api/resources/v1/bank_accounts.py similarity index 100% rename from pay-api/src/pay_api/resources/bank_accounts.py rename to pay-api/src/pay_api/resources/v1/bank_accounts.py diff --git a/pay-api/src/pay_api/resources/code.py b/pay-api/src/pay_api/resources/v1/code.py similarity index 100% rename from pay-api/src/pay_api/resources/code.py rename to pay-api/src/pay_api/resources/v1/code.py diff --git a/pay-api/src/pay_api/resources/distributions.py b/pay-api/src/pay_api/resources/v1/distributions.py similarity index 100% rename from pay-api/src/pay_api/resources/distributions.py rename to pay-api/src/pay_api/resources/v1/distributions.py diff --git a/pay-api/src/pay_api/resources/fas/__init__.py b/pay-api/src/pay_api/resources/v1/fas/__init__.py similarity index 100% rename from pay-api/src/pay_api/resources/fas/__init__.py rename to pay-api/src/pay_api/resources/v1/fas/__init__.py diff --git a/pay-api/src/pay_api/resources/fas/refund.py b/pay-api/src/pay_api/resources/v1/fas/refund.py similarity index 100% rename from pay-api/src/pay_api/resources/fas/refund.py rename to pay-api/src/pay_api/resources/v1/fas/refund.py diff --git a/pay-api/src/pay_api/resources/fas/routing_slip.py b/pay-api/src/pay_api/resources/v1/fas/routing_slip.py similarity index 100% rename from pay-api/src/pay_api/resources/fas/routing_slip.py rename to pay-api/src/pay_api/resources/v1/fas/routing_slip.py diff --git a/pay-api/src/pay_api/resources/fee.py b/pay-api/src/pay_api/resources/v1/fee.py similarity index 100% rename from pay-api/src/pay_api/resources/fee.py rename to pay-api/src/pay_api/resources/v1/fee.py diff --git a/pay-api/src/pay_api/resources/fee_schedule.py b/pay-api/src/pay_api/resources/v1/fee_schedule.py similarity index 100% rename from pay-api/src/pay_api/resources/fee_schedule.py rename to pay-api/src/pay_api/resources/v1/fee_schedule.py diff --git a/pay-api/src/pay_api/resources/invoice.py b/pay-api/src/pay_api/resources/v1/invoice.py similarity index 100% rename from pay-api/src/pay_api/resources/invoice.py rename to pay-api/src/pay_api/resources/v1/invoice.py diff --git a/pay-api/src/pay_api/resources/invoice_receipt.py b/pay-api/src/pay_api/resources/v1/invoice_receipt.py similarity index 100% rename from pay-api/src/pay_api/resources/invoice_receipt.py rename to pay-api/src/pay_api/resources/v1/invoice_receipt.py diff --git a/pay-api/src/pay_api/resources/invoices.py b/pay-api/src/pay_api/resources/v1/invoices.py similarity index 100% rename from pay-api/src/pay_api/resources/invoices.py rename to pay-api/src/pay_api/resources/v1/invoices.py diff --git a/pay-api/src/pay_api/resources/meta.py b/pay-api/src/pay_api/resources/v1/meta.py similarity index 100% rename from pay-api/src/pay_api/resources/meta.py rename to pay-api/src/pay_api/resources/v1/meta.py diff --git a/pay-api/src/pay_api/resources/payment.py b/pay-api/src/pay_api/resources/v1/payment.py similarity index 100% rename from pay-api/src/pay_api/resources/payment.py rename to pay-api/src/pay_api/resources/v1/payment.py diff --git a/pay-api/src/pay_api/resources/refund.py b/pay-api/src/pay_api/resources/v1/refund.py similarity index 100% rename from pay-api/src/pay_api/resources/refund.py rename to pay-api/src/pay_api/resources/v1/refund.py diff --git a/pay-api/src/pay_api/resources/transaction.py b/pay-api/src/pay_api/resources/v1/transaction.py similarity index 100% rename from pay-api/src/pay_api/resources/transaction.py rename to pay-api/src/pay_api/resources/v1/transaction.py From 4607101fade7daf0b2a2a4ae1c668841a3d0dee0 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Tue, 5 Sep 2023 14:11:34 -0700 Subject: [PATCH 3/6] clean up code smells --- .../pay_api/resources/v1/account_statements_notifications.py | 2 +- pay-api/src/pay_api/resources/v1/account_statements_settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py index dc029ba24..9b492d6c7 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_notifications.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_notifications.py @@ -61,7 +61,7 @@ def post_account_notification(account_id): return error_to_response(Error.INVALID_REQUEST, invalid_params=schema_utils.serialize(errors)) current_app.logger.debug(request_json) - # TODO add valid formatting + # Check if user is authorized to perform this action check_auth(business_identifier=None, account_id=account_id, contains_role=CHANGE_STATEMENT_SETTINGS, is_premium=True) diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index 0a9432a5a..9224a129d 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -55,8 +55,8 @@ def post_account_statement_settings(account_id): current_app.logger.info(' Date: Tue, 5 Sep 2023 14:19:34 -0700 Subject: [PATCH 4/6] linting --- pay-api/src/pay_api/resources/v1/account_statements_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/src/pay_api/resources/v1/account_statements_settings.py b/pay-api/src/pay_api/resources/v1/account_statements_settings.py index 9224a129d..b40b70cd3 100644 --- a/pay-api/src/pay_api/resources/v1/account_statements_settings.py +++ b/pay-api/src/pay_api/resources/v1/account_statements_settings.py @@ -56,7 +56,7 @@ def post_account_statement_settings(account_id): request_json = request.get_json() current_app.logger.debug(request_json) frequency = request_json.get('frequency') - + # Check if user is authorized to perform this action check_auth(business_identifier=None, account_id=account_id, contains_role=CHANGE_STATEMENT_SETTINGS, is_premium=True) From 75fa6e672ada70433f9ea74f9ff52c0eaae49912 Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Wed, 6 Sep 2023 08:49:44 -0700 Subject: [PATCH 5/6] version bump --- pay-api/src/pay_api/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pay-api/src/pay_api/version.py b/pay-api/src/pay_api/version.py index 3ce741a78..f58f31733 100644 --- a/pay-api/src/pay_api/version.py +++ b/pay-api/src/pay_api/version.py @@ -22,4 +22,4 @@ Development release segment: .devN """ -__version__ = '1.18.7' # pylint: disable=invalid-name +__version__ = '1.19.0' # pylint: disable=invalid-name From d19fecb80dde4b96035aef063ac2f95a9c4e1a2f Mon Sep 17 00:00:00 2001 From: Odysseus Chiu Date: Thu, 7 Sep 2023 13:17:35 -0700 Subject: [PATCH 6/6] bump tornado from 6.3.2 to 6.3.3 --- bcol-api/requirements.txt | 2 +- jobs/ftp-poller/requirements.txt | 2 +- jobs/payment-jobs/requirements.txt | 2 +- pay-api/requirements.txt | 2 +- queue_services/events-listener/requirements.txt | 2 +- queue_services/payment-reconciliations/requirements.txt | 2 +- report-api/requirements.txt | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bcol-api/requirements.txt b/bcol-api/requirements.txt index dcaa70a4e..3fc4cbb12 100644 --- a/bcol-api/requirements.txt +++ b/bcol-api/requirements.txt @@ -44,7 +44,7 @@ sentry-sdk==1.14.0 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.2 +tornado==6.3.3 urllib3==1.26.13 zeep==4.2.1 zipp==3.11.0 diff --git a/jobs/ftp-poller/requirements.txt b/jobs/ftp-poller/requirements.txt index 47a504669..9ea102d19 100644 --- a/jobs/ftp-poller/requirements.txt +++ b/jobs/ftp-poller/requirements.txt @@ -37,7 +37,7 @@ semver==2.13.0 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.2 +tornado==6.3.3 urllib3==1.26.16 zipp==3.15.0 -e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python diff --git a/jobs/payment-jobs/requirements.txt b/jobs/payment-jobs/requirements.txt index 13c79af71..a95c28baa 100644 --- a/jobs/payment-jobs/requirements.txt +++ b/jobs/payment-jobs/requirements.txt @@ -90,7 +90,7 @@ six==1.16.0 strict-rfc3339==0.7 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.2 +tornado==6.3.3 typing_extensions==4.6.3 urllib3==1.26.16 zipp==3.15.0 diff --git a/pay-api/requirements.txt b/pay-api/requirements.txt index 17a64eaf9..33b1aa2fd 100644 --- a/pay-api/requirements.txt +++ b/pay-api/requirements.txt @@ -72,7 +72,7 @@ sentry-sdk==1.19.1 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.2 +tornado==6.3.3 typing_extensions==4.5.0 urllib3==1.26.15 zipp==3.15.0 diff --git a/queue_services/events-listener/requirements.txt b/queue_services/events-listener/requirements.txt index 660b1386d..a5380ae4c 100644 --- a/queue_services/events-listener/requirements.txt +++ b/queue_services/events-listener/requirements.txt @@ -27,7 +27,7 @@ sentry-sdk==1.25.1 six==1.16.0 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.2 +tornado==6.3.3 urllib3==1.26.16 zipp==3.15.0 -e git+https://github.com/bcgov/lear.git#egg=entity_queue_common&subdirectory=queue_services/common diff --git a/queue_services/payment-reconciliations/requirements.txt b/queue_services/payment-reconciliations/requirements.txt index 7c8c9494b..402dfdc18 100644 --- a/queue_services/payment-reconciliations/requirements.txt +++ b/queue_services/payment-reconciliations/requirements.txt @@ -87,7 +87,7 @@ six==1.16.0 strict-rfc3339==0.7 threadloop==1.0.2 thrift==0.16.0 -tornado==6.3.2 +tornado==6.3.3 typing_extensions==4.6.3 urllib3==1.26.16 yarl==1.9.2 diff --git a/report-api/requirements.txt b/report-api/requirements.txt index 368d42eb5..b1d7bb987 100644 --- a/report-api/requirements.txt +++ b/report-api/requirements.txt @@ -45,7 +45,7 @@ six==1.16.0 threadloop==1.0.2 thrift==0.16.0 tinycss2==1.2.1 -tornado==6.3.2 +tornado==6.3.3 typing_extensions==4.5.0 urllib3==1.26.15 weasyprint==58.1