"""Model flow. Provides functions to sync over model entities from main looker instance to client looker instance. Doesn't cover actual .model.lkml files in a repo. Refer to guthub_tools.py module for that. """ import requests import config import utils def get_main_instance_model_json(model_name): """Get json representation of a given model. Args: model_name (str): name of the model and (at the same time) its unique identifier. Should come in snake_case, without file extension. Example -> 'core_metadata' Returns: dict: json representation of a model. bool: False if something went wrong and http response was other than 200. """ response = requests.get( f'{config.LOOKER_MAIN_INSTANCE_URL}/lookml_models/{model_name}', headers=config.LOOKER_MAIN_VALIDATION ) return utils.process_response(response) def check_model_name_on_client_instance(model_name): """Check if model exists on a client looker instance. Args: model_name (str): name of the model. Comes in snake_case without file extension. Example -> core_metadata. Returns: bool: True if model exists, False otherwise. """ response = requests.get( f'{config.LOOKER_CLIENT_INSTANCE_URL}/lookml_models/{model_name}', headers=config.LOOKER_CLIENT_VALIDATION ) if response.status_code == 200: return True return False def update_model_on_client_instance(model_name, model_json): """Update model on client looker instance. Args: model_name (str): name of the model. Comes in snake_case without file extension. Example -> core_metadata. model_json (dict): json representation of a model. Comes as a return value of change_allowed_db_connection function. Returns: bool: True if model was successfully updated, False otherwise. """ response = requests.patch( f'{config.LOOKER_CLIENT_INSTANCE_URL}/lookml_models/{model_name}', headers=config.LOOKER_CLIENT_VALIDATION, json=model_json ) return utils.process_response(response) def post_model_to_client_instance(model_json): """Create a model on client looker instance. Args: model_json (dict): json representation of a model. Comes as a return value of change_allowed_db_connection function. Returns: dict: json representation of a model. """ response = requests.post( f'{config.LOOKER_CLIENT_INSTANCE_URL}/lookml_models', headers=config.LOOKER_CLIENT_VALIDATION, json=model_json ) return utils.process_response(response) def sync_lookml_model(model_name, model_json): """Sync over lookml model. "One button" function for syncing lookml model. Checks if model with given name exists on client instance and updates it if that's the case. Otherwise, creates a new model on client instance. Notion: this only syncs _lookml_ model, not .view.lookml or .model.lkml files. Refer to github_tools.py for those. Args: model_name (str): name of the model and (at the same time) its unique identifier. Should come in snake_case, without file extension. Example -> 'core_metadata' model_json (dic): json representation of a model. Returns: calls respective function. """ model_json['allowed_db_connection_names'] = ['looker_high_performance'] if check_model_name_on_client_instance(model_name): return update_model_on_client_instance(model_name, model_json) else: return post_model_to_client_instance(model_json) def get_set_of_explore_names(model_json): """Get set of explore names of a model. Args: model_json (dict): json representation of a model. Comes as a return value of get_main_instance_model_json function. Returns: set: set of strings, strings are explore names. """ model_explores = model_json['explores'] set_of_explore_names = set() for explore in model_explores: set_of_explore_names.add(explore['name']) return set_of_explore_names def get_model_explore_json(model_name, model_explore_name): """Get a json representation of a model explore. Args: model_name (str): name of the model and (at the same time) its unique identifier. Should come in snake_case, without file extension. Example -> 'core_metadata' model_explore_name (str): name of a model explore. Comes as an item from return value of get_names_of_all_model_explores function. Returns: dict: json representation of a specific model explore. bool: False if something went wrong and http response was other than 200. """ response = requests.get( f'{config.LOOKER_MAIN_INSTANCE_URL}/lookml_models/' f'{model_name}/explores/{model_explore_name}', headers=config.LOOKER_MAIN_VALIDATION ) return utils.process_response(response) def get_all_view_files(model_explore_json): """Get names of all view files included as joins in a given model explore. Some joins have their names aliased, in which case there won't be a corresponding file where alias is used instead of a filename. For this case function first checks if there is a 'from' field for a join. If there is, then the value of 'from' field is stored in a resulting set. If there is no 'from' field in the join it means that 'name' field is an actual filename and not an alias, so it's added to resulting set. Args: model_explore_json (dict): json representation of a model explore. Comes as a return value of get_model_explore_json function. Returns: set: non-duplicate list of all .view.lkml file names included in a given explore as joins. Used further for syncing all these files through github_tools.py module. Notion! Contents of the set are file names WITHOUT file extensions. """ set_of_names = set() for join_field in model_explore_json['joins']: optional = join_field.get('from', '') if optional: set_of_names.add(optional) else: set_of_names.add(join_field['name']) return set_of_names def get_set_of_views_for_explore(model_name, model_explore_name): """Get list of all joined view files for a given explore. Args: model_name (str): name of the model in snake_case without file extension. Example -> core_metadata model_explore_name (str): name of a model explore. Returns: set: non-duplicate list of all .view.lkml file names included in a given explore as joins. Used further for syncing all these files through github_tools.py module. Notion! Contents of the set are file names WITHOUT file extensions. """ explore_json = get_model_explore_json(model_name, model_explore_name) set_of_file_names = get_all_view_files(explore_json) set_of_file_names.add(model_explore_name) return set_of_file_names