def update_session(self, project: str, branch: str) -> None: """Switches to a development mode session and checks out the desired branch. Args: project: Name of the Looker project to use. branch: Name of the Git branch to check out. """ if branch == "master": logger.debug("Updating session to use production workspace") url = utils.compose_url(self.api_url, path=["session"]) body = {"workspace_id": "production"} response = self.session.patch(url=url, json=body) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f"Unable to update session to production workspace.\n" f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"' ) else: logger.debug("Updating session to use development workspace") url = utils.compose_url(self.api_url, path=["session"]) body = {"workspace_id": "dev"} response = self.session.patch(url=url, json=body) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f"Unable to update session to development workspace.\n" f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"' ) logger.debug(f"Setting Git branch to {branch}") url = utils.compose_url( self.api_url, path=["projects", project, "git_branch"] ) body = {"name": branch} response = self.session.put(url=url, json=body) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f"Unable to checkout Git branch {branch}. " "If you have uncommitted changes on the current branch, " "please commit or revert them, then try again.\n\n" f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"' ) logger.info(f"Checked out branch {branch}")
def get_query_task_multi_results(self, query_task_ids: List[str]) -> JsonDict: """Returns query task results. If a ClientError or TimeoutError is received, attempts to retry. Args: query_task_ids: IDs for the query tasks running asynchronously. Returns: List[JsonDict]: JSON response from the query task. """ # Using old-style string formatting so that strings are formatted lazily logger.debug( "Attempting to get results for %d query tasks", len(query_task_ids) ) url = utils.compose_url(self.api_url, path=["query_tasks", "multi_results"]) response = self.session.get( url=url, params={"query_task_ids": ",".join(query_task_ids)} ) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"' ) return response.json()
def get_lookml_dimensions(self, model: str, explore: str) -> List[str]: """Gets all dimensions for an explore from the LookmlModel endpoint. Args: model: Name of LookML model to query. explore: Name of LookML explore to query. Returns: List[str]: Names of all the dimensions in the specified explore. Dimension names are returned in the format 'explore_name.dimension_name'. """ logger.debug(f"Getting all dimensions from explore {explore}") url = utils.compose_url( self.api_url, path=["lookml_models", model, "explores", explore] ) response = self.session.get(url=url) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f'Unable to get dimensions for explore "{explore}".\n' f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"' ) return response.json()["fields"]["dimensions"]
def run_lookml_test(self, project: str, model: str = None) -> List[JsonDict]: """Runs all LookML/data tests for a given project and model (optional) This command only runs tests in production, as the Looker API doesn't currently allow us to run data tests on a specific branch. Args: project: Name of the Looker project to use model: Optional name of the LookML model to restrict testing to Returns: List[JsonDict]: JSON response containing any LookML/data test errors """ logger.debug(f"Running LookML tests for project {project}") url = utils.compose_url( self.api_url, path=["projects", project, "lookml_tests", "run"] ) if model is not None: response = self.session.get(url=url, params={"model": model}) else: response = self.session.get(url=url) try: response.raise_for_status() except requests.exceptions.HTTPError as error: raise ApiConnectionError( f"Failed to run data tests for project {project}\n" f'Error raised: "{error}"' ) return response.json()
def all_lookml_tests(self, project: str) -> List[JsonDict]: """Gets all LookML/data tests for a given project. Args: project: Name of the Looker project to use Returns: List[JsonDict]: JSON response containing all LookML/data tests """ logger.debug(f"Getting LookML tests for project {project}") url = utils.compose_url( self.api_url, path=["projects", project, "lookml_tests"] ) response = self.session.get(url=url) try: response.raise_for_status() except requests.exceptions.HTTPError as error: raise ApiConnectionError( f"Failed to retrieve data tests for project {project}\n" f'Error raised: "{error}"' ) return response.json()
def authenticate(self, client_id: str, client_secret: str, api_version: float) -> None: """Logs in to Looker's API using a client ID/secret pair and an API version. Args: client_id: Looker API client ID. client_secret: Looker API client secret. api_version: Desired API version to use for requests. """ logger.debug("Authenticating Looker API credentials") url = utils.compose_url(self.api_url, path=["login"]) body = {"client_id": client_id, "client_secret": client_secret} response = self.session.post(url=url, data=body) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f"Failed to authenticate to {url}\n" f"Attempted authentication with client ID {client_id}\n" f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"') access_token = response.json()["access_token"] self.session.headers = {"Authorization": f"token {access_token}"} logger.info(f"Connected using Looker API {api_version}")
def get_lookml_models(self) -> List[JsonDict]: """Gets all models and explores from the LookmlModel endpoint. Returns: List[JsonDict]: JSON response containing LookML models and explores. """ logger.debug(f"Getting all models and explores from {self.base_url}") url = utils.compose_url(self.api_url, path=["lookml_models"]) response = self.session.get(url=url) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( f"Unable to retrieve explores.\n" f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"') return response.json()
def get_looker_release_version(self) -> str: """Gets the version number of connected Looker instance. Returns: str: Looker instance release version number (e.g. 6.22.12) """ logger.debug("Checking Looker instance release version") url = utils.compose_url(self.api_url, path=["versions"]) response = self.session.get(url=url) try: response.raise_for_status() except requests.exceptions.HTTPError as error: details = utils.details_from_http_error(response) raise ApiConnectionError( "Failed to get Looker instance release version\n" f"Looker API error encountered: {error}\n" + "Message received from Looker's API: " f'"{details}"') return response.json()["looker_release_version"]