"""Application Logic. This should return responses. """ from owsresponse import response from features import context from features.constants import error from features.constants import context as context_constants from features.server import features from features.models import split_feature def get_single_feature_with_user_context(feature_name, orchard_user_id): """Get variants for a single feature in the user context. Args: feature_name (str): the name of the feature. orchard_user_id (str): the platform and ID for the user. Returns: response.Response: contains a dict of the feature data """ context_helper = context.Context(orchard_user_id=orchard_user_id) validation_response = context_helper.validate() if not validation_response: return validation_response if feature_name in features.keys(): active_variant = features[feature_name].get_active_variant( context_helper.to_dict() ) return response.Response(active_variant) else: return response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ) def get_single_feature_with_profile_context( feature_name, profile_type=None, profile_id=None, identity_id=None ): """Get variants for a single feature in the profile context. Args: feature_name (str): the name of the feature. profile_type (str): the profile type. e.g. 'ArtistProfile' or 'LabelManagerProfile'. profile_id (int): the profile_id within the profile_type identity_id (str): the Auth0 id of the user Returns: response.Response: contains a dict of the feature data """ context_helper = context.ProfileContext(profile_type, profile_id, identity_id) validation_response = context_helper.validate() if not validation_response: return validation_response if feature_name in features.keys(): active_variant = features[feature_name].get_active_variant( context_helper.to_dict() ) return response.Response(active_variant) else: return response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ) def get_all_features_with_user_context(orchard_user_id, subaccount_id, vendor_id): """Get variants for all features in the user context. Args: orchard_user_id (str): the platform and ID for the user. Returns: dict: all features and variants. """ context_helper = context.Context(orchard_user_id, subaccount_id, vendor_id) validation_response = context_helper.validate() if not validation_response: return validation_response contextualized_features = {} context_dict = context_helper.to_dict() for feature_name in features.keys(): active_variant = features[feature_name].get_active_variant(context_dict) contextualized_features[feature_name] = active_variant return response.Response(contextualized_features) def get_all_features_with_profile_context( profile_type=None, profile_id=None, identity_id=None ): """Get variants for all features in the profile context. Args: profile_type (str): the profile type. e.g. 'ArtistProfile' or 'LabelManagerProfile'. profile_id (int): the profile_id within the profile_type identity_id (str): the Auth0 id of the user Returns: dict: all features and variants for this profile. """ context_helper = context.ProfileContext(profile_type, profile_id, identity_id) validation_response = context_helper.validate() if not validation_response: return validation_response contextualized_features = {} context_dict = context_helper.to_dict() for feature_name in features.keys(): active_variant = features[feature_name].get_active_variant(context_dict) contextualized_features[feature_name] = active_variant return response.Response(contextualized_features) def get_all_features_without_user_context(subaccount_id, vendor_id): """Get variants for all features without a user context. Returns: dict: all features and variants """ context_helper = context.Context(None, subaccount_id, vendor_id) contextualized_features = {} context_dict = context_helper.to_dict() for feature_name in features.keys(): active_variant = features[feature_name].get_active_variant(context_dict) contextualized_features[feature_name] = active_variant return response.Response(contextualized_features) def _get_single_feature(feature_name, attributes): """Get variant for a single feature from split.io or features.yml 1) When feature not found split returns "control" 2) Incase where split feature is disabled, the response is "off" so we convert it to "control" 3) Incase where split feature is enabled, the response is "on" so we convert it to "enabled" Args: feature_name (str): the name of the feature. attributes(dict): parameter to filter features based on split rules eg: {'user_id': 'oa:562'}, {'user_id': 'alw:46997'}, {'vendor_id': 23904}, {'identity_id': '5c548bd462501638ea3ed444'} Returns: response.Response: contains a dict of the feature data """ traffic_type = _get_traffic_type_identifier(attributes) active_variant = split_feature.get_split_feature( traffic_type=traffic_type, split_name=feature_name, attributes=attributes ) if active_variant != context_constants.SPLIT_NOT_AVAILABLE: if active_variant == context_constants.SPLIT_DISABLED: active_variant = context_constants.FEATURE_DISABLED elif active_variant == context_constants.SPLIT_ENABLED: active_variant = context_constants.FEATURE_ENABLED return response.Response(active_variant) else: if feature_name in features.keys(): active_variant = features[feature_name].get_active_variant(attributes) return response.Response(active_variant) else: return response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ) def get_single_split_feature_with_user_context( feature_name, orchard_user_id, identity_id=None ): """Get variant for a single feature in the user context from split.io Args: feature_name (str): the name of the feature. orchard_user_id (str): the platform and ID for the user. identity_id (str): auth0id if the user. Returns: response.Response: contains a dict of the feature data """ context_helper = context.Context( orchard_user_id=orchard_user_id, identity_id=identity_id ) # this will also load an ALW user's vendor and subaccount info validation_response = context_helper.validate() if not validation_response: return validation_response return _get_single_feature(feature_name, context_helper.to_dict()) def get_single_split_feature_with_profile_context( feature_name, profile_type=None, profile_id=None, identity_id=None, identity_uuid=None, ): """Get variant for single feature in the profile context from split.io. Args: feature_name (str): the name of the feature. profile_type (str): the profile type. e.g. 'ArtistProfile' or 'LabelManagerProfile'. profile_id (int): the profile_id within the profile_type identity_id (str): the Auth0 id of the user identity_uuid (str): UUID of the user Returns: response.Response: contains a dict of the feature data """ context_helper = context.ProfileContext( profile_type, profile_id, identity_id, identity_uuid ) return _get_single_feature(feature_name, context_helper.to_dict()) def _get_traffic_type_identifier(attributes): """Get traffic type identifier.""" if 'identity_uuid' in attributes: return attributes.get('identity_uuid') # fallback to identity_id if that exist. if 'identity_id' in attributes: return attributes.get('identity_id') # for OA users if 'profile_type_and_id' in attributes: return attributes.get('profile_type_and_id') return 'Anonymous' def _get_all_features(attributes): """Get variants for all features by merging features from split.io and features.yml If feature exist in both split.io as well as feature.yml variant will be fetched from split.io Incases where variants are fetched from split.io: 1) If split feature is disabled, the response is "off" so we convert it to "control" 2) If split feature is enabled, the response is "on" so we convert it to "enabled" Args: attributes(dict): parameter to filter features based on split rules eg: {'user_id': 'oa:562'}, {'user_id': 'alw:46997'}, {'vendor_id': 23904}, {'identity_id': '5c548bd462501638ea3ed444'} Returns: response.Response: contains a dict of the feature data """ contextualized_features = {} for feature_name in features.keys(): active_variant = features[feature_name].get_active_variant(attributes) contextualized_features[feature_name] = active_variant traffic_type = _get_traffic_type_identifier(attributes) split_features = split_feature.get_all_split_features( traffic_type=traffic_type, attributes=attributes ) if split_features: for key, value in split_features.items(): if value == context_constants.SPLIT_ENABLED: split_features[key] = context_constants.FEATURE_ENABLED elif value == context_constants.SPLIT_DISABLED: split_features[key] = context_constants.FEATURE_DISABLED contextualized_features.update(split_features) return response.Response(contextualized_features) def get_all_split_features_with_user_context( orchard_user_id, subaccount_id, vendor_id, identity_id=None, identity_uuid=None ): """Get variants for all features with/without user context from split.io and features.yml Args: orchard_user_id (str): the platform and ID for the user. subaccount_id (str): subaccount id. vendor_id(str): vendor id. identity_id (str): auth0id of the user. identity_uuid (str): UUID of the user Returns: response.Response: contains a dict of the feature data """ context_helper = context.Context( orchard_user_id, subaccount_id, vendor_id, identity_id, identity_uuid ) if orchard_user_id is not None: validation_response = context_helper.validate() if not validation_response: return validation_response return _get_all_features(context_helper.to_dict()) def get_all_split_features_with_profile_context( profile_type=None, profile_id=None, identity_id=None, identity_uuid=None ): """Get variants for all feature in the profile context from split.io and features.yml Args: profile_type (str): the profile type. e.g. 'ArtistProfile' or 'LabelManagerProfile'. profile_id (int): the profile_id within the profile_type identity_id (str): the Auth0 id of the user identity_uuid (str): UUID of the user Returns: response.Response: contains a dict of the feature data """ context_helper = context.ProfileContext( profile_type, profile_id, identity_id, identity_uuid ) return _get_all_features(context_helper.to_dict())