Example #1
0
    def update_session(self) -> None:
        """Switch to a dev mode session and checkout the desired branch."""

        if not self.project:
            raise FonzException(
                "No Looker project name provided. "
                "Please include the desired project name with --project")
        if not self.branch:
            raise FonzException(
                "No git branch provided. "
                "Please include the desired git branch name with --branch")

        logger.debug("Updating session to use development workspace.")
        url = utils.compose_url(self.base_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:
            raise ConnectionError(
                f"Unable to update session to development workspace.\n"
                f'Error raised: "{error}"')

        logger.debug(f"Setting git branch to: {self.branch}")
        url = utils.compose_url(self.base_url,
                                path=["projects", self.project, "git_branch"])
        body = {"name": self.branch}
        response = self.session.put(url=url, json=body)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise ConnectionError(
                f'Unable to set git branch to "{self.branch}".\n'
                f'Error raised: "{error}"')
Example #2
0
    def get_explores(self) -> List[JsonDict]:
        """Get all explores from the LookmlModel endpoint."""

        logger.debug("Getting all explores in Looker instance.")
        url = utils.compose_url(self.base_url, path=["lookml_models"])
        response = self.session.get(url=url)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise FonzException(
                f'Unable to retrieve explores.\nError raised: "{error}"')

        explores = []

        logger.debug(f"Filtering explores for project: {self.project}")

        for model in response.json():
            if model["project_name"] == self.project:
                for explore in model["explores"]:
                    explores.append({
                        "model": model["name"],
                        "explore": explore["name"]
                    })

        return explores
Example #3
0
    def run_query(self, query_id: int) -> List[JsonDict]:
        """Run a Looker query by ID and return the JSON result."""

        logger.debug(f"Running query {query_id}")
        url = utils.compose_url(self.base_url,
                                path=["queries", query_id, "run", "json"])
        response = self.session.get(url=url)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise FonzException(
                f'Failed to run query "{query_id}".\nError raised: "{error}"')
        query_result = response.json()

        return query_result
Example #4
0
    def get_query_sql(self, query_id: int) -> str:
        """Collect the SQL string for a Looker query."""

        logger.debug(f"Getting SQL for query {query_id}")
        url = utils.compose_url(self.base_url,
                                path=["queries", query_id, "run", "sql"])
        response = self.session.get(url=url)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise FonzException(
                f'Failed to obtain SQL for query "{query_id}".\nError raised: "{error}"'
            )
        sql = response.text

        return sql
Example #5
0
    def connect(self) -> None:
        """Authenticate, start a dev session, check out specified branch."""

        logger.info("Authenticating Looker credentials. \n")

        url = utils.compose_url(self.base_url, path=["login"])
        body = {
            "client_id": self.client_id,
            "client_secret": self.client_secret
        }
        response = self.session.post(url=url, data=body)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise ConnectionError(
                f"Failed to authenticate to {url}\n"
                f'Attempted authentication with client ID "{self.client_id}"\n'
                f'Error raised: "{error}"')

        access_token = response.json()["access_token"]
        self.session.headers = {"Authorization": f"token {access_token}"}
Example #6
0
    def create_query(self, model: str, explore_name: str,
                     dimensions: List[str]) -> int:
        """Build a Looker query using all the specified dimensions."""

        logger.debug(f"Creating query for {explore_name}")
        url = utils.compose_url(self.base_url, path=["queries"])
        body = {
            "model": model,
            "view": explore_name,
            "fields": dimensions,
            "limit": 1
        }
        response = self.session.post(url=url, json=body)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise FonzException(
                f'Unable to create a query for "{model}/{explore_name}".\n'
                f'Error raised: "{error}"')
        query_id = response.json()["id"]

        return query_id
Example #7
0
    def get_dimensions(self, model: str, explore_name: str) -> List[str]:
        """Get dimensions for an explore from the LookmlModel endpoint."""

        logger.debug(f"Getting dimensions for {explore_name}")
        url = utils.compose_url(
            self.base_url,
            path=["lookml_models", model, "explores", explore_name])
        response = self.session.get(url=url)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise FonzException(
                f'Unable to get dimensions for explore "{explore_name}".\n'
                f'Error raised: "{error}"')

        dimensions = []

        for dimension in response.json()["fields"]["dimensions"]:
            if "fonz: ignore" not in dimension["sql"]:
                dimensions.append(dimension["name"])

        return dimensions
Example #8
0
import pytest
import ast
import requests
import requests_mock
from fonz.utils import compose_url
from tests.constants import TEST_BASE_URL
from urllib.parse import parse_qs

BASE_URL_30 = compose_url(TEST_BASE_URL + ":19999", path=["api", "3.0"])

lookml_models = [
    {
        "name": "model_one",
        "project_name": "test_project",
        "explores": [{
            "name": "explore_one"
        }, {
            "name": "explore_two"
        }],
    },
    {
        "name": "model_two",
        "project_name": "not_test_project",
        "explores": [{
            "name": "explore_three"
        }, {
            "name": "explore_four"
        }],
    },
]
Example #9
0
def test_compose_url_multiple_path_components():
    url = utils.compose_url(TEST_BASE_URL,
                            ["api", "3.0", "login", "42", "auth", "27"])
    assert url == "https://test.looker.com/api/3.0/login/42/auth/27"
Example #10
0
def test_compose_url_one_path_component():
    url = utils.compose_url(TEST_BASE_URL, ["api"])
    assert url == "https://test.looker.com/api"
Example #11
0
def test_compose_url_with_extra_slashes():
    url = utils.compose_url(TEST_BASE_URL + "/", ["/api//", "3.0/login/"])
    assert url == "https://test.looker.com/api/3.0/login"