"""Application Handlers. Requests are redirected to handlers, which are responsible for getting information from the URL and passing it down to the logic layer. The way each layer talks to each other is through Response objects which defines the type status of the data and the data itself. Please note: the Orchard uses the term handlers over views as convention for clarity See: oto.response for more details. """ import json from ddtrace import tracer from flask import g from flask import request from oto import response from oto.adaptors.flask import flaskify from owsrequest import access, flask_request from product import auth, config from product.api import app from product.constants import error from product.constants import features from product.constants import header from product.logic import hello from product.logic import hfa from product.logic import localization as localization_logic from product.logic import placeholder_upc as placeholder_upc_logic from product.logic import product as product_logic from product.logic import product_copy as product_copy_logic from product.logic import profile as profile_logic from product.logic import upc as upc_logic from product.models import release_artist from product.models import release_asset_version from product.models import release_phonetic_translations from product.models import release_subgenre from product.utils import feature_control_util, handler_util from product.validation import json_schema @app.route(config.HEALTH_CHECK, methods=["GET"]) def health(): """Check the health of the application.""" return flaskify(hello.health_check()) @app.route("/upc/", methods=["HEAD"]) def upc_exists(upc): """Check if a product exists with the given UPC.""" return flaskify(upc_logic.upc_exists(upc)) @app.route("/upc/available/", methods=["HEAD"]) def upc_avaialble_for_use(upc): """Check if a upc is used with any products or is reserved.""" return flaskify(upc_logic.upc_available(upc)) @app.route("/upc//is_orchard_upc", methods=["GET"]) def is_orchard_upc(upc): """Check if a UPC is an Orchard UPC.""" return flaskify(upc_logic.is_orchard_upc(upc)) @app.errorhandler(500) def exception_handler(error): """Default handler when uncaught exception is raised. Note: Exception will also be sent to Sentry if config.SENTRY is set. Returns: flask.Response: A 500 response with JSON 'code' & 'message' payload. """ message = ( "The server encountered an internal error " "and was unable to complete your request." ) g.log.exception(error) return flaskify(response.create_fatal_response(message)) @app.route( "///product_code/", methods=["HEAD"], ) def product_code_exists(account_type, account_id, product_code): """Check if product code exists for account type/id. Args: account_type (str): Account type to verify product ownership for. account_id (int): Account id to verify product ownership for. product_code (str): Product code to check existance of. Returns: Response: Flask response. """ if (account_type not in header.GRASS_ACCOUNT_TYPES) or not account_id: return flaskify(response.Response(status=400)) exclude_product_id = int(request.args.get("exclude_product_id", 0)) return flaskify( product_logic.product_code_found_for_account( product_code, account_type, account_id, exclude_product_id ) ) @app.route( "///product_code/available", methods=["GET"], ) def product_codes_available_for_use(account_type, account_uuid): """Check if product code exists for account type/id. Args: account_type (str): Account type to verify product ownership for. account_uuid (UUID): Account id to verify product ownership for. Returns: Response: Flask response. """ if (account_type not in header.GRASS_ACCOUNT_TYPES) or not account_uuid: return flaskify(response.Response(status=400)) data = request.get_json() product_codes = data.get("product_codes", []) return flaskify( product_logic.product_codes_available_for_account( product_codes, account_type, account_uuid ) ) @app.route( "/product///product_code/", methods=["GET"], ) @json_schema.wrap_request_validation( '/product/{accountType}/{accountUuid}/product_code/{productCode}', 'get', json_schema.validate_request_headers ) def get_product_id_by_product_code_and_account(account_type, account_uuid, product_code): """Returns {"product_id": product_id} for account type/uuid and product_code. Args: account_type (str): Account type to get product for. account_uuid (UUID): Account id to get product for. product_code (str): Product code to get product by (if exists) Returns: Response: Flask response. """ if (account_type not in header.GRASS_ACCOUNT_TYPES) or not account_uuid: return flaskify(response.Response(status=400)) return flaskify( product_logic.get_product_id_by_product_code( product_code, account_type, account_uuid ) ) @app.route( "///product/", methods=["HEAD"] ) def check_product_ownership(account_type, account_id, product_id): """Check if a vendor or subaccount owns a product. Args: account_type (str): Account type to verify product ownership for. account_id (int): Account id to verify product ownership for. product_id (int): Product id of product to verify ownership of. Returns: Response: Flask response. """ if (account_type not in header.GRASS_ACCOUNT_TYPES) or not account_id: return flaskify(response.Response(status=400)) ownership_response = product_logic.check_product_ownership( product_id, **{account_type + "_id": account_id} ) return flaskify(response.Response(status=ownership_response.status)) @app.route("/upc/available", methods=["POST"]) def upcs_available_for_use(): """Check if a upc is used with any products or is reserved.""" data = request.get_json() upcs = data.get("upcs", []) return flaskify(upc_logic.upcs_available(upcs)) @app.route("/product/upc/", methods=["GET"]) def get_product_by_upc(upc): """Get a product from the product upc. Gets details about a product using UPC as the identifier. Args: upc (int) the upc for the product Returns: flask.response: Product information related to the given upc. """ product = product_logic.get_product_by_upc(upc) if not product: return flaskify(product) validation = flask_request.verify_grass_access( request, vendor=product.message.get("vendor_id"), subaccount=product.message.get("subaccount_id"), ) if not validation: return flaskify(validation) return flaskify(product) @app.route("/product/", methods=["GET"]) def get_product_by_product_id(product_id): """Get a product by its product_id. Get details about a product using product_id (release_id in AR) as the identifier. Args: product_id (int): the product_id (release_id) of the product Returns: flask.response: Product information related to the given product_id. """ with_tenant_uuids = request.args.get( "with_tenant_uuids", False, type=json.loads) product = product_logic.get_product_by_product_id( product_id, None, None, with_tenant_uuids) if not product: return flaskify(product) validation = flask_request.verify_grass_access( request, vendor=product.message.get("vendor_id"), subaccount=product.message.get("subaccount_id"), ) if not validation: return flaskify(validation) return flaskify(product) @app.route("/product//copy", methods=["POST"]) def copy_product(product_id): """Copy a product. Args: product_id (int): The product_id (release_id) of the product to copy. Returns: flask.response: The product_id of the new product. """ ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) account_type, account_id = flask_request.get_grass_headers(request) orchard_user_id = request.headers.get(header.ORCHARD_USER_ID) data = request.get_json() return flaskify( product_copy_logic.copy_product( product_id, data, orchard_user_id, account_type, account_id ) ) @app.route("/product//copy/", methods=["POST"]) def copy_product_flexible(product_id, context_type): """Copy a product. Args: product_id (int): The product_id (release_id) of the product to copy. context (str): physical|digital, the type of product your copying to. Returns: flask.response: The product_id of the new product. """ ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) account_type, account_id = flask_request.get_grass_headers(request) orchard_user_id = request.headers.get(header.ORCHARD_USER_ID) data = request.get_json() return flaskify( product_copy_logic.copy_product_flexible( product_id, context_type, data, orchard_user_id, account_type, account_id ) ) @app.route("/product/", methods=["DELETE"]) def delete_product_by_product_id(product_id): """Delete a product by its product_id. Args: product_id (int): the product_id (release_id) of the product Returns: flask.response: Product information related to the given product_id. """ account_type = request.headers.get(header.GRASS_ACCOUNT_TYPE) account_id = request.headers.get(header.GRASS_ACCOUNT_ID) profile_access_check_enabled = feature_control_util.is_feature_enabled( features.CCM_CHECK_DELETE_PRODUCT_ACCESS, g.request_context ) if not profile_access_check_enabled: return flaskify(product_logic.delete_product(product_id, account_type, account_id)) # the underlying endpoints fail without grass headers grass_headers_validation = access.verify_grass_headers( account_type, account_id, required=True) if not grass_headers_validation: return flaskify(grass_headers_validation) profile_type = g.request_context.profile_type profile_id = g.request_context.profile_id if profile_type or profile_id: profile_headers_validation = auth.verify_profile_headers( profile_type, profile_id, # validate types here, because the service doesn't apply access rules allowed_types=header.DELETE_PRODUCT_ALLOWED_PROFILE_TYPES ) if not profile_headers_validation: return flaskify(profile_headers_validation) profile_access_validation = profile_logic.check_profile_access_to_product( int(profile_id), profile_type, product_id ) if not profile_access_validation: return flaskify(profile_access_validation) return flaskify(product_logic.delete_product(product_id, account_type, account_id)) @app.route("/localization/languages", methods=["GET"]) def get_all_itunes_languages(): """Get all languages that can be used for localizations. Returns: flask.response: localization information. """ return flaskify(localization_logic.get_all_itunes_languages()) @app.route("/public/localization/languages", methods=["GET"]) def get_public_all_itunes_languages(): """Get all languages that can be used for localizations. Returns: flask.response: localization information. """ headers = {"Cache-Control": "max-age=86400"} return flaskify(localization_logic.get_all_itunes_languages(), headers) @app.route("/localization/product/", methods=["GET"]) def get_product_localization(product_id): """Get a product localization from the product_id. Gets details about a product localization depending on its type. Args: product_id (int): the product_id for the product Returns: flask.response: localization information. """ legacy_flag = bool(int(request.args.get("legacy", 0))) return flaskify( localization_logic.get_localization_by_product_id(product_id, legacy_flag) ) @app.route( "/localization/product//language/", methods=["POST"], ) @json_schema.wrap_request_validation( "/localization/product/{productId}/language/{languageId}", "post", json_schema.validate_request_body, ) def create_product_localizations(product_id, language_id): """Create/Update localization data for a product in a language. @todo POST schema validation pending.. see MOV-2249 Args: product_id (int): id for the product language_id (int): id for the itunes language Returns: flask.response: localization information. """ legacy_flag = bool(int(request.args.get("legacy", 0))) localize_data = localization_logic.create( product_id, language_id, request.get_json(), legacy_flag ) return flaskify(localize_data) @app.route('/language/', methods=['GET']) def get_itunes_language_by_id(language_id): """Get iTunes language by ID. Args: language_id (int): id for the itunes language Returns: flask.response: itunes language """ language_data = localization_logic.get_itunes_language_by_id(language_id) return flaskify(language_data) @app.route( "/localization/product//language/", methods=["DELETE"], ) def delete_product_localizations(product_id, language_id): """Delete localization data for a product in a language. Args: product_id (int): id for the product language_id (int): id for the itunes language Returns: flask.response: success/failure response. """ legacy_flag = bool(int(request.args.get("legacy", 0))) return flaskify(localization_logic.delete(product_id, language_id, legacy_flag)) @app.route("/localization/track/", methods=["DELETE"]) def delete_track_localization_with_tuids(tuids): """Delete track localization for given tuids. Args: tuids (list): List of track ids. Returns: flask.response: localization information. """ return flaskify(localization_logic.delete_track_localization(tuids)) @app.route( "/localization/track//language/", methods=["DELETE"] ) def delete_track_localization_with_language(tuid, language_id): """Delete track localization for given tuid and language_id. Args: tuid (int): track id. language_id (int): language id. Returns: flask.response: localization information. """ return flaskify(localization_logic.delete_track_localization([tuid], language_id)) @app.route("/localization/track/", methods=["GET"]) def get_track_localization(tuids): """Get a track localization from the tuids. Gets details about multiple track's localization. Args: tuids (list): List of track ids. Returns: flask.response: localization information. """ return flaskify(localization_logic.get_track_localization(tuids)) @app.route("/localization/track", methods=["POST"]) def bulk_get_track_localization(): """Get a track localization from the tuids. Gets details about multiple track's localization. Returns: flask.response: localization information. """ request_json = request.get_json() tuids = request_json.get("tuids") if not tuids: return flaskify( response.create_error_response( status=400, code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_NO_TUIDS, ) ) return flaskify(localization_logic.get_track_localization(tuids)) @app.route("/localization/track//language/", methods=["PUT"]) @json_schema.wrap_request_validation( "/localization/track/{tuid}/language/{language_id}", "put", json_schema.validate_request_body, ) def update_track_localization(tuid, language_id): """Create/Update localization data for a track in a language. @todo PUT schema validation pending.. Args: tuid (int): id for the track language_id (int): id for the itunes language Returns: flask.response: localization information. """ return flaskify( localization_logic.update_track_localization( tuid, language_id, request.get_json() ) ) @app.route("/localization/tracks", methods=["PUT"]) @json_schema.wrap_request_validation( "/localization/tracks", "put", json_schema.validate_request_body ) def update_multiple_tracks_localization(): """Update multiple tracks localization data. Returns: flask.response: localization information. """ return flaskify( localization_logic.update_multiple_localizations(request.get_json()) ) @app.route("/upc/placeholder", methods=["POST"]) def generate_placeholder_upc(): """Generate and return a placeholder upc. Returns: flask.response: generated placeholder upc value """ return flaskify(placeholder_upc_logic.generate_placeholder_upc()) @app.route("/vendor//display_upc/", methods=["GET"]) def get_upc_from_vendor_display_upc(vendor_id, display_upc): """Look up a upc for a vendor given its display_upc and context_type. Args: vendor_id (int): Account id to restrict products to. display_upc (str): The display UPC for the account. Returns: Response: Flask response. """ validation = flask_request.verify_grass_access(request, vendor=vendor_id) if not validation: return flaskify(validation) context_type = request.args.get("context_type") show_deletions = request.args.get("show_deletions", default=False) in ['true', 'True'] if context_type not in ("physical", "digital"): return flaskify( response.create_error_response( status=400, code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_CONTEXT_TYPE_REQUIRED, ) ) return flaskify( product_logic.get_product_by_display_upc( vendor_id, display_upc, context_type, show_deletions ) ) @app.route( "/vendor//display_upc//available", methods=["HEAD"], ) def check_display_upc_availability_for_vendor(vendor_id, display_upc): """Check for availability of a display_upc for a specific vendor. Returns: flask.response: success/failure response """ context_type = request.args.get("context_type") account_type = "vendor" if context_type not in ("physical", "digital"): return flaskify( response.create_error_response( status=400, code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_CONTEXT_TYPE_REQUIRED, ) ) validation = flask_request.verify_grass_access( request, vendor_id=vendor_id, subaccount=None ) if not validation: return flaskify(validation) return flaskify( product_logic.check_display_upc_availability( account_type, vendor_id, display_upc, context_type ) ) @app.route( ("/subaccount//" "display_upc//available"), methods=["HEAD"], ) def check_display_upc_availability_for_subaccount(subaccount_id, display_upc): """Check for availability of a display_upc for a specific subaccount. Returns: flask.response: success/failure response """ account_type = "subaccount" context_type = request.args.get("context_type") if context_type not in ("physical", "digital"): return flaskify( response.create_error_response( status=400, code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_CONTEXT_TYPE_REQUIRED, ) ) validation = flask_request.verify_grass_access( request, subaccount_id=subaccount_id, subaccount=None ) if not validation: return flaskify(validation) return flaskify( product_logic.check_display_upc_availability( account_type, subaccount_id, display_upc, context_type ) ) @app.route("///products", methods=["GET"]) def get_products(account_type, account_id): """Get list of products for a given vendor or subaccount. Args: account_type (str): Account type to restrict products to. account_id (int): Account id to restrict products to. Returns: Response: Flask response. """ jwt_identity_id = g.request_context.jwt_identity_id if jwt_identity_id and not handler_util.is_jwt_identity_authorized(jwt_identity_id): return flaskify(response.create_error_response( error.ERROR_CODE_AUTHORIZATION, error.ERROR_MESSAGE_FORBIDDEN_USER, status=403)) if (account_type not in header.GRASS_ACCOUNT_TYPES) or not account_id: return flaskify( response.create_error_response( code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_INVALID_ACCOUNT, ) ) headers_response = flask_request.verify_grass_headers(request) if not headers_response: return flaskify(headers_response) ownership_validation = flask_request.verify_grass_ownership( request, handler_util.check_ownership, account_type, account_id ) if not ownership_validation: return flaskify(ownership_validation) page_offset = request.args.get("page_offset") page_limit = request.args.get("page_limit") status = request.args.get("status") start_date = request.args.get("start_date") end_date = request.args.get("end_date") sort_order = request.args.get("sort_dir") deletions = request.args.get("deletions") sort_by = request.args.get("sort_by") return flaskify( product_logic.get_products_for_account( status=status, page_limit=page_limit, page_offset=page_offset, start_date=start_date, end_date=end_date, sort_order=sort_order, deletions=deletions, sort_by=sort_by, **{account_type + "_id": account_id}, ) ) @app.route("/products", methods=["GET"]) def get_upcs_by_product_ids(): """Get upcs by respective product_ids. Returns: flask.response: List of upcs dict. """ product_ids = request.args.get("product_ids") products_detail_data = product_logic.get_upcs_by_product_ids(product_ids) return flaskify(products_detail_data) @app.route("/bulk-upc", methods=["POST"]) def get_products_by_upcs(): """Get products by respective upcs. Returns: flask.response: List of products. """ data = request.get_json(silent=True, force=True) or {} return flaskify(product_logic.get_products_by_upcs(data)) @app.route("/product-id-by-upc-dataloader", methods=["POST"]) def get_product_id_by_upc_dataloader(): """Get products by respective upcs. Returns: flask.response: List of products. """ data = request.get_json(silent=True, force=True) or {} return flaskify(product_logic.get_product_id_by_upc_dataloaded(data)) @app.route("/product//document", methods=["GET"]) def get_product_document(product_id): """Get single project with some artist info. Should have no grass headers since this is intended for internal use Args: product_id (int): Product ID. Returns: Response: Flask response """ account_type = request.headers.get(header.GRASS_ACCOUNT_TYPE) account_id = request.headers.get(header.GRASS_ACCOUNT_ID) if account_id or account_type: response_object = response.create_fatal_response( message=error.ERROR_CODE_BAD_GRASS_REQUEST ) return flaskify(response_object) with_company_brand = request.args.get("with_company_brand", False, type=json.loads) # with_tenant_uuids flag is used to populate documents for OS_Search. with_tenant_uuids = request.args.get("with_tenant_uuids", False, type=json.loads) result = product_logic.get_product_document( product_id, with_company_brand, with_tenant_uuids) return flaskify(result) @app.route("/products/documents", methods=["POST"]) def get_products_documents(): """Get products with some artist info. Should have no grass headers since this is intended for internal use Returns: Response: Flask response """ if request.data: data = request.get_json(force=True) else: data = {} product_ids = data.get("productIds", "") account_type = request.headers.get(header.GRASS_ACCOUNT_TYPE) account_id = request.headers.get(header.GRASS_ACCOUNT_ID) if account_id or account_type: response_object = response.create_fatal_response( message=error.ERROR_CODE_BAD_GRASS_REQUEST ) return flaskify(response_object) result = product_logic.get_products_documents(product_ids) return flaskify(result) @app.route("/product//subgenre", methods=["POST"]) def create_release_subgenre(product_id): """Create release subgenre.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) data = request.get_json() data["release_id"] = product_id return flaskify(release_subgenre.create(data)) @app.route("/product//subgenre", methods=["PUT"]) def update_release_subgenre(product_id): """Update release subgenre.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) data = request.get_json() return flaskify(release_subgenre.update(data, product_id)) @app.route("/product//subgenre", methods=["GET"]) def get_release_subgenre(product_id): """Get release subgenre.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) return flaskify(release_subgenre.get(product_id)) @app.route("/product//subgenre", methods=["DELETE"]) def delete_release_subgenre(product_id): """Delete release subgenre.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) return flaskify(release_subgenre.delete(product_id)) @app.route("/product//artist", methods=["POST"]) def create_release_artist(product_id): """Create release artist.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) data = request.get_json() data["release_id"] = product_id return flaskify(release_artist.create(data)) @app.route("/product//artist", methods=["PUT"]) def update_release_artist(product_id): """Update release artist.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) data = request.get_json() return flaskify(release_artist.update(data, product_id)) @app.route("/product//artist", methods=["GET"]) def get_release_artist(product_id): """Get release artist.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) return flaskify(release_artist.get(product_id)) @app.route("/product//artist", methods=["DELETE"]) def delete_release_artist(product_id): """Delete release artist.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) return flaskify(release_artist.delete(product_id)) def _product_ownership_check(product_id): account_type, account_id = flask_request.get_grass_headers(request) if account_type or account_id: return product_logic.check_product_ownership( product_id, **{account_type + "_id": account_id} ) return True @app.route("///upcs", methods=["POST"]) def check_products_ownership_by_upcs(account_type, account_id): """Check products ownership by account id and upc(s). Args: account_type (str): Account type to verify product ownership for. account_id (int): Account id to verify product ownership for. Returns: Response: Flask response. """ validation = flask_request.verify_grass_access( request, required=False, **{account_type: account_id} ) if not validation: return flaskify(response=validation) if (account_type not in header.GRASS_ACCOUNT_TYPES) or not account_id: return flaskify( response.create_error_response( code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_INVALID_ACCOUNT, ) ) data = request.get_json(silent=True, force=True) or {} if "upcs" not in data: return flaskify( response.create_error_response( code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_MISSING_UPC, ) ) upcs = list(set([str(upc) for upc in data["upcs"] if str(upc).isdigit()])) if not len(upcs): return flaskify( response.create_error_response( code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_CODE_INVALID_DATA ) ) if len(upcs) > int(config.LIMIT): return flaskify( response.create_error_response( code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_MAX_PRODUCT, ) ) ownership_response = product_logic.check_products_ownership_by_upcs( upcs, **{account_type + "_id": account_id} ) return flaskify(ownership_response) @app.route("/sound-recording//products", methods=["GET"]) def get_products_by_isrc(isrc): """Get products for a given ISRC.""" account_type, account_id = flask_request.get_grass_headers(request) # Only allow this call for OA users if account_type or account_id: response_object = response.create_error_response( code=error.ERROR_CODE_AUTHORIZATION, message=error.ERROR_MESSAGE_FORBIDDEN_USER, status=403, ) return flaskify(response_object) result = product_logic.get_products_by_isrc(isrc) return flaskify(result) @app.route( "/product//phonetic-translations/", methods=["GET"] ) @app.route( "/product//phonetic-translations", defaults={"language_id": None}, methods=["GET"], ) def get_phonetic_translations(product_id, language_id): """Get release phonetic translations.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) return flaskify(release_phonetic_translations.get(product_id, language_id)) @app.route("/product//phonetic-translations", methods=["POST"]) def create_phonetic_translations(product_id): """Create a new release phonetic translation.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) orchard_user_id = request.headers.get(header.ORCHARD_USER_ID) data = request.get_json() hydrated_data = [ hydrate_phonetic_translations_item(item, product_id, orchard_user_id) for item in data ] return flaskify(release_phonetic_translations.create(hydrated_data)) @app.route("/product//phonetic-translations", methods=["PUT"]) def update_phonetic_translations(product_id): """Update release phonetic translations.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) orchard_user_id = request.headers.get(header.ORCHARD_USER_ID) data = request.get_json() hydrated_data = [ hydrate_phonetic_translations_item(item, product_id, orchard_user_id, True) for item in data ] return flaskify(release_phonetic_translations.update(hydrated_data)) @app.route("/product//phonetic-translations", methods=["DELETE"]) def delete_phonetic_translations(product_id): """Delete release phonetic translations.""" ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) release_phonetic_translations_ids = request.get_json(False, True, True) if release_phonetic_translations_ids is None: return flaskify(release_phonetic_translations.delete_by_product_id(product_id)) return flaskify( release_phonetic_translations.delete(release_phonetic_translations_ids) ) def hydrate_phonetic_translations_item( data, product_id, orchard_user_id, is_update=False ): """Add orchard_user_id and release_id to item.""" data["release_id"] = product_id if is_update: data["updated_by"] = orchard_user_id else: data["created_by"] = orchard_user_id return data @app.route("/release-artist//product", methods=["GET"]) def get_products_by_release_artist_id(release_artist_id): """Get products for a given release_artist_id.""" account_type, account_id = flask_request.get_grass_headers(request) account_type = request.headers.get(header.GRASS_ACCOUNT_TYPE) account_id = request.headers.get(header.GRASS_ACCOUNT_ID) if account_id or account_type: response_object = response.create_fatal_response( message=error.ERROR_CODE_BAD_GRASS_REQUEST ) return flaskify(response_object) result = product_logic.get_product_by_release_artist_id(release_artist_id) return flaskify(result) @app.route("/upc/provision", methods=["POST"]) def provision_upc(): """ Retrieve a UPC using the UPC Provisioner. Update UPC status to used when {'mark_used': True} in post body. """ if request.data: data = request.get_json(force=True) else: data = {} mark_used = data.get("mark_used", False) response_object = upc_logic.retrieve_upc(mark_used) return flaskify(response_object) @app.route("/product//territory_dates", methods=["PUT"]) def update_territory_dates(product_id): """Update release territory dates. Args: product_id (int): Product ID Returns: flask.Response """ ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) if product_logic.replace_product_territory_dates(product_id, request.get_json()): return flaskify(response.Response(status=200)) return flaskify( response.create_fatal_response( message="Error replacing territory release dates" ) ) @app.route("/product//asset-version", methods=["GET"]) def get_asset_version(product_id): """Get asset version. Args: product_id (int): Product ID Returns: flask.Response """ return flaskify(release_asset_version.get(product_id)) @app.route("/product//asset-version", methods=["PUT"]) def update_asset_version(product_id): """Update product asset version. Args: product_id (int): Product ID Returns: flask.Response """ ownership_response = _product_ownership_check(product_id) if not ownership_response: return flaskify(ownership_response) request_json = request.get_json() api_version = request_json.get("api_version") if not api_version: return flaskify( response.create_error_response( status=400, code=error.ERROR_CODE_BAD_REQUEST, message=error.ERROR_MESSAGE_NO_API_VERSION, ) ) return flaskify(release_asset_version.update_api_version(product_id, api_version)) @app.route("/lookup/product/ownership/", methods=["POST"]) def lookup_product_ownership(): """Returns product ownership attributes in a dataloader-style format.""" identity_id = g.request_context.jwt_identity_id if not identity_id: return flaskify( response.create_error_response( error.ERROR_CODE_AUTHORIZATION, error.ERROR_MESSAGE_INVALID_JWT_NO_IDENTITY_ID, status=401, ) ) data = request.get_json(silent=True, force=True) or {} product_ids = data.get("product_ids", []) if not product_ids: return flaskify( response.create_error_response( error.ERROR_CODE_BAD_REQUEST, error.ERROR_MESSAGE_NO_PRODUCT_IDS ) ) for product_id in product_ids: if not isinstance(product_id, int): return flaskify( response.create_error_response( error.ERROR_CODE_BAD_REQUEST, error.ERROR_MESSAGE_PRODUCT_IDS_INTS, ) ) return flaskify(product_logic.lookup_product_ownership(product_ids)) @app.route("/log", methods=["GET"]) def say_hi(): """Test logger.""" trace_id, span_id = tracer.get_log_correlation_context() message = "dd trace debug trace_id {} span_id {}".format(trace_id, span_id) g.log.info( dict( message=message, large_span_int=13385470528116265835, large_span_str="13385470528116265835", ) ) return flaskify(response.Response(status=200)) @app.route("/hfa/eligible-tracks", methods=["GET"]) def get_hfa_eligible_tracks(): """Get hfa pending tracks for HFA job.""" result = hfa.get_hfa_tracks_for_processing() return flaskify(result)