Exemplo n.º 1
0
    def check_connection(self, logger: AirbyteLogger,
                         config: Mapping[str, Any]) -> Tuple[bool, Any]:
        try:
            params = {"access_key": config["access_key"]}
            base = config.get("base")
            if base is not None:
                params["base"] = base

            resp = requests.get(
                f"{ExchangeRates.url_base}{config['start_date']}",
                params=params)
            status = resp.status_code
            logger.info(f"Ping response code: {status}")
            if status == 200:
                return True, None
            # When API requests is sent but the requested data is not available or the API call fails
            # for some reason, a JSON error is returned.
            # https://exchangeratesapi.io/documentation/#errors
            error = resp.json().get("error")
            code = error.get("code")
            message = error.get("message") or error.get("info")
            # If code is base_currency_access_restricted, error is caused by switching base currency while using free
            # plan
            if code == "base_currency_access_restricted":
                message = f"{message} (this plan doesn't support selecting the base currency)"
            return False, message
        except Exception as e:
            return False, e
Exemplo n.º 2
0
 def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, any]:
     try:
         logger.info("Checking the config")
         GoogleAds(credentials=config["credentials"], customer_id=config["customer_id"])
         return True, None
     except Exception as error:
         return False, f"Unable to connect to Google Ads API with the provided credentials - {repr(error)}"
Exemplo n.º 3
0
 def set_sub_primary_key(self):
     if isinstance(self.primary_key, list):
         for index, value in enumerate(self.primary_key):
             setattr(self, f"sub_primary_key_{index + 1}", value)
     else:
         logger = AirbyteLogger()
         logger.error(
             "Failed during setting sub primary keys. Primary key should be list."
         )
Exemplo n.º 4
0
    def read(
        self,
        logger: AirbyteLogger,
        config: Mapping[str, Any],
        catalog: ConfiguredAirbyteCatalog,
        state: Optional[MutableMapping[str, Any]] = None,
    ) -> Iterator[AirbyteMessage]:
        """
        Overwritten to dynamically receive only those streams that are necessary for reading for significant speed gains
        (Salesforce has a strict API limit on requests).
        """
        connector_state = copy.deepcopy(state or {})
        config, internal_config = split_config(config)
        # get the streams once in case the connector needs to make any queries to generate them
        logger.info("Starting generating streams")
        stream_instances = {
            s.name: s
            for s in self.streams(config, catalog=catalog, state=state)
        }
        logger.info(f"Starting syncing {self.name}")
        self._stream_to_instance_map = stream_instances
        for configured_stream in catalog.streams:
            stream_instance = stream_instances.get(
                configured_stream.stream.name)
            if not stream_instance:
                raise KeyError(
                    f"The requested stream {configured_stream.stream.name} was not found in the source. Available streams: {stream_instances.keys()}"
                )

            try:
                yield from self._read_stream(
                    logger=logger,
                    stream_instance=stream_instance,
                    configured_stream=configured_stream,
                    connector_state=connector_state,
                    internal_config=internal_config,
                )
            except exceptions.HTTPError as error:
                error_data = error.response.json()[0]
                error_code = error_data.get("errorCode")
                if error.response.status_code == codes.FORBIDDEN and error_code == "REQUEST_LIMIT_EXCEEDED":
                    logger.warn(
                        f"API Call limit is exceeded. Error message: '{error_data.get('message')}'"
                    )
                    break  # if got 403 rate limit response, finish the sync with success.
                raise error

            except Exception as e:
                logger.exception(
                    f"Encountered an exception while reading stream {self.name}"
                )
                raise e

        logger.info(f"Finished syncing {self.name}")
Exemplo n.º 5
0
    def read(
        self, logger: AirbyteLogger, config: Mapping[str, Any], catalog: ConfiguredAirbyteCatalog, state: MutableMapping[str, Any] = None
    ) -> Iterator[AirbyteMessage]:
        """
        Overwritten to dynamically receive only those streams that are necessary for reading for significant speed gains
        (Salesforce has a strict API limit on requests).
        """
        connector_state = copy.deepcopy(state or {})
        config, internal_config = split_config(config)
        # get the streams once in case the connector needs to make any queries to generate them
        logger.info("Starting generating streams")
        stream_instances = {s.name: s for s in self.streams(config, catalog=catalog)}
        logger.info(f"Starting syncing {self.name}")
        self._stream_to_instance_map = stream_instances
        for configured_stream in catalog.streams:
            stream_instance = stream_instances.get(configured_stream.stream.name)
            if not stream_instance:
                raise KeyError(
                    f"The requested stream {configured_stream.stream.name} was not found in the source. Available streams: {stream_instances.keys()}"
                )

            try:
                yield from self._read_stream(
                    logger=logger,
                    stream_instance=stream_instance,
                    configured_stream=configured_stream,
                    connector_state=connector_state,
                    internal_config=internal_config,
                )
            except Exception as e:
                logger.exception(f"Encountered an exception while reading stream {self.name}")
                raise e

        logger.info(f"Finished syncing {self.name}")
Exemplo n.º 6
0
def create_firebolt_wirter(connection: Connection, config: json,
                           logger: AirbyteLogger) -> FireboltWriter:
    if config["loading_method"]["method"] == "S3":
        logger.info("Using the S3 writing strategy")
        writer = FireboltS3Writer(
            connection,
            config["loading_method"]["s3_bucket"],
            config["loading_method"]["aws_key_id"],
            config["loading_method"]["aws_key_secret"],
            config["loading_method"]["s3_region"],
        )
    else:
        logger.info("Using the SQL writing strategy")
        writer = FireboltSQLWriter(connection)
    return writer
Exemplo n.º 7
0
    def check_connection(
            self, logger: AirbyteLogger,
            config: Mapping[str, Any]) -> Tuple[bool, Optional[str]]:
        try:
            _ = self._get_sf_object(config)
        except exceptions.HTTPError as error:
            error_data = error.response.json()[0]
            error_code = error_data.get("errorCode")
            if error.response.status_code == codes.FORBIDDEN and error_code == "REQUEST_LIMIT_EXCEEDED":
                logger.warn(
                    f"API Call limit is exceeded. Error message: '{error_data.get('message')}'"
                )
                return False, "API Call limit is exceeded"

        return True, None
Exemplo n.º 8
0
def run_load_dataframes(config, expected_columns=10, expected_rows=42):
    df_list = SourceFile.load_dataframes(config=config, logger=AirbyteLogger(), skip_data=False)
    assert len(df_list) == 1  # Properly load 1 DataFrame
    df = df_list[0]
    assert len(df.columns) == expected_columns  # DataFrame should have 10 columns
    assert len(df.index) == expected_rows  # DataFrame should have 42 rows of data
    return df
Exemplo n.º 9
0
            def wrapper_balance_rate_limit(*args, **kwargs):
                sleep_time = 0
                free_load = float("inf")
                # find the Response inside args list
                for arg in args:
                    response = arg if type(
                        arg) is requests.models.Response else None

                # Get the rate_limits from response
                rate_limits = ([
                    (response.headers.get(rate_remaining_limit_header),
                     response.headers.get(rate_max_limit_header))
                    for rate_remaining_limit_header, rate_max_limit_header in
                    rate_limits_headers
                ] if response else None)

                # define current load from rate_limits
                if rate_limits:
                    for current_rate, max_rate_limit in rate_limits:
                        free_load = min(
                            free_load,
                            int(current_rate) / int(max_rate_limit))

                # define sleep time based on load conditions
                if free_load <= threshold:
                    sleep_time = sleep_on_high_load

                # sleep based on load conditions
                sleep(sleep_time)
                AirbyteLogger().info(
                    f"Sleep {sleep_time} seconds based on load conditions.")

                return func(*args, **kwargs)
Exemplo n.º 10
0
class GoogleSheetsClient:

    logger = AirbyteLogger()

    def __init__(self, config: Dict):
        self.config = config
        self.retries = 100  # max number of backoff retries

    def authorize(self) -> pygsheets_client:
        input_creds = self.config.get("credentials")
        auth_creds = client_account.Credentials.from_authorized_user_info(info=input_creds)
        client = pygsheets.authorize(custom_credentials=auth_creds)

        # Increase max number of retries if Rate Limit is reached. Error: <HttpError 429>
        client.drive.retries = self.retries  # for google drive api
        client.sheet.retries = self.retries  # for google sheets api

        # check if token is expired and refresh it
        if client.oauth.expired:
            self.logger.info("Auth session is expired. Refreshing...")
            client.oauth.refresh(Request())
            if not client.oauth.expired:
                self.logger.info("Successfully refreshed auth session")
            else:
                self.logger.fatal("The token is expired and could not be refreshed, please check the credentials are still valid!")

        return client
Exemplo n.º 11
0
 def check(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> AirbyteConnectionStatus:
     try:
         connection = create_connection(config=config)
     except Exception as e:
         logger.error(f"Failed to create connection. Error: {e}")
         return AirbyteConnectionStatus(status=Status.FAILED, message=f"Could not create connection: {repr(e)}")
     try:
         channel = connection.channel()
         if channel.is_open:
             return AirbyteConnectionStatus(status=Status.SUCCEEDED)
         return AirbyteConnectionStatus(status=Status.FAILED, message="Could not open channel")
     except Exception as e:
         logger.error(f"Failed to open RabbitMQ channel. Error: {e}")
         return AirbyteConnectionStatus(status=Status.FAILED, message=f"An exception occurred: {repr(e)}")
     finally:
         connection.close()
Exemplo n.º 12
0
    def check(self, logger: AirbyteLogger,
              config: Mapping[str, Any]) -> AirbyteConnectionStatus:
        """
        Tests if the input configuration can be used to successfully connect to the destination with the needed permissions
            e.g: if a provided API token or password can be used to connect and write to the destination.

        :param logger: Logging object to display debug/info/error to the logs
            (logs will not be accessible via airbyte UI if they are not passed to this logger)
        :param config: Json object containing the configuration of this destination, content of this json is as specified in
        the properties of the spec.json file

        :return: AirbyteConnectionStatus indicating a Success or Failure
        """

        connector_config = ConnectorConfig(**config)

        try:
            aws_handler = AwsHandler(connector_config, self)
        except (ClientError, AttributeError) as e:
            logger.error(
                f"""Could not create session on {connector_config.aws_account_id} Exception: {repr(e)}"""
            )
            message = f"""Could not authenticate using {connector_config.credentials_type} on Account {connector_config.aws_account_id} Exception: {repr(e)}"""
            return AirbyteConnectionStatus(status=Status.FAILED,
                                           message=message)

        try:
            aws_handler.head_bucket()
        except ClientError as e:
            message = f"""Could not find bucket {connector_config.bucket_name} in aws://{connector_config.aws_account_id}:{connector_config.region} Exception: {repr(e)}"""
            return AirbyteConnectionStatus(status=Status.FAILED,
                                           message=message)

        with LakeformationTransaction(aws_handler) as tx:
            table_location = "s3://" + connector_config.bucket_name + "/" + connector_config.bucket_prefix + "/" + "airbyte_test/"
            table = aws_handler.get_table(
                txid=tx.txid,
                database_name=connector_config.lakeformation_database_name,
                table_name="airbyte_test",
                location=table_location,
            )
        if table is None:
            message = f"Could not create a table in database {connector_config.lakeformation_database_name}"
            return AirbyteConnectionStatus(status=Status.FAILED,
                                           message=message)

        return AirbyteConnectionStatus(status=Status.SUCCEEDED)
Exemplo n.º 13
0
class NotImplementedAuth(Exception):
    """Not implemented Auth option error"""

    logger = AirbyteLogger()

    def __init__(self, auth_method: str = None):
        self.message = f"Not implemented Auth method = {auth_method}"
        super().__init__(self.logger.error(self.message))
Exemplo n.º 14
0
def test_local_storage_spec():
    """Checks spec properties"""
    source = SourceFileSecure()
    spec = source.spec(logger=AirbyteLogger())
    for provider in spec.connectionSpecification["properties"]["provider"][
            "oneOf"]:
        assert provider["properties"]["storage"][
            "const"] != LOCAL_STORAGE_NAME, "This connector shouldn't work with local files."
Exemplo n.º 15
0
    def discover(self, logger: AirbyteLogger,
                 config: Mapping) -> AirbyteCatalog:
        """
        Returns an AirbyteCatalog representing the available streams and fields in this integration. For example, given valid credentials to a
        Remote CSV File, returns an Airbyte catalog where each csv file is a stream, and each column is a field.
        """
        client = self._get_client(config)
        name = client.stream_name

        logger.info(
            f"Discovering schema of {name} at {client.reader.full_url}...")
        try:
            streams = list(client.streams)
        except Exception as err:
            reason = f"Failed to discover schemas of {name} at {client.reader.full_url}: {repr(err)}\n{traceback.format_exc()}"
            logger.error(reason)
            raise err
        return AirbyteCatalog(streams=streams)
Exemplo n.º 16
0
class AbstractTestParser(ABC):
    """ Prefix this class with Abstract so the tests don't run here but only in the children """

    logger = AirbyteLogger()

    @property
    @abstractmethod
    def test_files(self) -> List[Mapping[str, Any]]:
        """return a list of test_file dicts in structure:
        [
            {"AbstractFileParser": CsvParser(format, master_schema), "filepath": "...", "num_records": 5, "inferred_schema": {...}, line_checks:{}, fails: []},
            {"AbstractFileParser": CsvParser(format, master_schema), "filepath": "...", "num_records": 16, "inferred_schema": {...}, line_checks:{}, fails: []}
        ]
        note: line_checks index is 1-based to align with row numbers
        """

    def _get_readmode(self, test_name, test_file):
        self.logger.info(
            f"testing {test_name}() with {test_file.get('test_alias', test_file['filepath'].split('/')[-1])} ..."
        )
        return "rb" if test_file["AbstractFileParser"].is_binary else "r"

    def test_get_inferred_schema(self):
        for test_file in self.test_files:
            with smart_open(
                    test_file["filepath"],
                    self._get_readmode("get_inferred_schema", test_file)) as f:
                if "test_get_inferred_schema" in test_file["fails"]:
                    with pytest.raises(Exception) as e_info:
                        test_file["AbstractFileParser"].get_inferred_schema(f)
                        self.logger.debug(str(e_info))
                else:
                    assert test_file["AbstractFileParser"].get_inferred_schema(
                        f) == test_file["inferred_schema"]

    def test_stream_records(self):
        for test_file in self.test_files:
            with smart_open(test_file["filepath"],
                            self._get_readmode("stream_records",
                                               test_file)) as f:
                if "test_stream_records" in test_file["fails"]:
                    with pytest.raises(Exception) as e_info:
                        [
                            print(r) for r in
                            test_file["AbstractFileParser"].stream_records(f)
                        ]
                        self.logger.debug(str(e_info))
                else:
                    records = [
                        r for r in
                        test_file["AbstractFileParser"].stream_records(f)
                    ]

                    assert len(records) == test_file["num_records"]
                    for index, expected_record in test_file[
                            "line_checks"].items():
                        assert records[index - 1] == expected_record
Exemplo n.º 17
0
    def _read_records(conf, catalog, state=None) -> Tuple[List[AirbyteMessage], List[AirbyteMessage]]:
        records = []
        states = []
        for message in SourceFacebookMarketing().read(AirbyteLogger(), conf, catalog, state=state):
            if message.type == Type.RECORD:
                records.append(message)
            elif message.type == Type.STATE:
                states.append(message)

        return records, states
Exemplo n.º 18
0
    def check(self, logger: AirbyteLogger,
              config: json) -> AirbyteConnectionStatus:
        try:
            access_token = config["access_token"]
            spreadsheet_id = config["spreadsheet_id"]

            smartsheet_client = smartsheet.Smartsheet(access_token)
            smartsheet_client.errors_as_exceptions(True)
            smartsheet_client.Sheets.get_sheet(spreadsheet_id)

            return AirbyteConnectionStatus(status=Status.SUCCEEDED)
        except Exception as e:
            if isinstance(e, smartsheet.exceptions.ApiError):
                err = e.error.result
                code = 404 if err.code == 1006 else err.code
                reason = f"{err.name}: {code} - {err.message} | Check your spreadsheet ID."
            else:
                reason = str(e)
            logger.error(reason)
        return AirbyteConnectionStatus(status=Status.FAILED)
Exemplo n.º 19
0
 def check_connection(self, logger: AirbyteLogger,
                      config: Mapping[str, Any]) -> Tuple[bool, any]:
     try:
         logger.info("Checking the config")
         google_api = GoogleAds(credentials=self.get_credentials(config),
                                customer_id=config["customer_id"])
         account_stream = Accounts(api=google_api)
         list(account_stream.read_records(sync_mode=SyncMode.full_refresh))
         # Check custom query request validity by sending metric request with non-existant time window
         for q in config.get("custom_queries", []):
             q = q.get("query")
             if CustomQuery.cursor_field in q:
                 raise Exception(
                     f"Custom query should not contain {CustomQuery.cursor_field}"
                 )
             req_q = CustomQuery.insert_segments_date_expr(
                 q, "1980-01-01", "1980-01-01")
             google_api.send_request(req_q)
         return True, None
     except GoogleAdsException as error:
         return False, f"Unable to connect to Google Ads API with the provided credentials - {repr(error.failure)}"
Exemplo n.º 20
0
    def check_connection(self, logger: AirbyteLogger, config: Mapping[str, any]) -> Tuple[bool, any]:
        """
        :param config:  the user-input config object conforming to the connector's spec.json
        :param logger:  logger object
        :return Tuple[bool, any]: (True, None) if the input config can be used to connect to the API successfully, (False, error) otherwise.
        """
        url_template = "https://{company}.hellobaton.com/api/"
        try:
            params = {
                "api_key": config["api_key"],
            }
            base_url = url_template.format(company=config["company"])
            # This is just going to return a mapping of available endpoints
            response = requests.get(base_url, params=params)
            status_code = response.status_code
            logger.info(f"Status code: {status_code}")
            if status_code == 200:
                return True, None

        except Exception as e:
            return False, e
Exemplo n.º 21
0
    def check_connection(self, logger: AirbyteLogger,
                         config: Mapping[str, Any]) -> Tuple[bool, any]:
        try:
            logger.info("Checking the config")
            google_api = GoogleAds(credentials=self.get_credentials(config))

            accounts = self.get_account_info(google_api, config)
            customers = Customer.from_accounts(accounts)
            # Check custom query request validity by sending metric request with non-existant time window
            for customer in customers:
                for query in config.get("custom_queries", []):
                    query = query.get("query")
                    if customer.is_manager_account and self.is_metrics_in_custom_query(
                            query):
                        logger.warning(
                            f"Metrics are not available for manager account {customer.id}. "
                            f"Please remove metrics fields in your custom query: {query}."
                        )
                    if CustomQuery.cursor_field in query:
                        return False, f"Custom query should not contain {CustomQuery.cursor_field}"
                    req_q = CustomQuery.insert_segments_date_expr(
                        query, "1980-01-01", "1980-01-01")
                    response = google_api.send_request(req_q,
                                                       customer_id=customer.id)
                    # iterate over the response otherwise exceptions will not be raised!
                    for _ in response:
                        pass
            return True, None
        except GoogleAdsException as exception:
            error_messages = ", ".join(
                [error.message for error in exception.failure.errors])
            logger.error(traceback.format_exc())
            return False, f"Unable to connect to Google Ads API with the provided configuration - {error_messages}"
Exemplo n.º 22
0
    def streams(self, config: Mapping[str, Any]) -> List[Stream]:
        authenticator = TokenAuthenticator(config["api_token"])
        default_start_date = pendulum.parse(config["start_date"])
        threads_lookback_window = pendulum.Duration(
            days=config["lookback_window"])

        streams = [
            Channels(authenticator=authenticator),
            ChannelMembers(authenticator=authenticator),
            ChannelMessages(authenticator=authenticator,
                            default_start_date=default_start_date),
            Threads(authenticator=authenticator,
                    default_start_date=default_start_date,
                    lookback_window=threads_lookback_window),
            Users(authenticator=authenticator),
        ]

        # To sync data from channels, the bot backed by this token needs to join all those channels. This operation is idempotent.
        if config["join_channels"]:
            logger = AirbyteLogger()
            logger.info("joining Slack channels")
            join_channels_stream = JoinChannelsStream(
                authenticator=authenticator)
            for stream_slice in join_channels_stream.stream_slices():
                for message in join_channels_stream.read_records(
                        sync_mode=SyncMode.full_refresh,
                        stream_slice=stream_slice):
                    logger.info(message["message"])

        return streams
Exemplo n.º 23
0
    def read(
        self, logger: AirbyteLogger, config: Mapping,
        catalog: ConfiguredAirbyteCatalog,
        state_path: Mapping[str,
                            any]) -> Generator[AirbyteMessage, None, None]:
        """Returns a generator of the AirbyteMessages generated by reading the source with the given configuration, catalog, and state."""
        client = self._get_client(config)
        fields = self.selected_fields(catalog)
        name = client.stream_name

        logger.info(f"Reading {name} ({client.reader.full_url})...")
        try:
            for row in client.read(fields=fields):
                record = AirbyteRecordMessage(
                    stream=name,
                    data=row,
                    emitted_at=int(datetime.now().timestamp()) * 1000)
                yield AirbyteMessage(type=Type.RECORD, record=record)
        except Exception as err:
            reason = f"Failed to read data of {name} at {client.reader.full_url}: {repr(err)}\n{traceback.format_exc()}"
            logger.error(reason)
            raise err
Exemplo n.º 24
0
    def discover(self, logger: AirbyteLogger, config: json) -> AirbyteCatalog:
        access_token = config["access_token"]
        spreadsheet_id = config["spreadsheet_id"]
        streams = []

        smartsheet_client = smartsheet.Smartsheet(access_token)
        try:
            sheet = smartsheet_client.Sheets.get_sheet(spreadsheet_id)
            sheet = json.loads(str(sheet))  # make it subscriptable
            sheet_json_schema = get_json_schema(sheet)

            logger.info(
                f"Running discovery on sheet: {sheet['name']} with {spreadsheet_id}"
            )

            stream = AirbyteStream(name=sheet["name"],
                                   json_schema=sheet_json_schema)
            streams.append(stream)

        except Exception as e:
            raise Exception(f"Could not run discovery: {str(e)}")

        return AirbyteCatalog(streams=streams)
Exemplo n.º 25
0
def test_check_connection_should_fail_when_api_call_fails(mocker):
    # We patch the object inside source.py because that's the calling context
    # https://docs.python.org/3/library/unittest.mock.html#where-to-patch
    mocker.patch("source_google_ads.source.GoogleAds",
                 MockErroringGoogleAdsClient)
    source = SourceGoogleAds()
    check_successful, message = source.check_connection(
        AirbyteLogger(),
        {
            "credentials": {
                "developer_token": "fake_developer_token",
                "client_id": "fake_client_id",
                "client_secret": "fake_client_secret",
                "refresh_token": "fake_refresh_token",
            },
            "customer_id":
            "fake_customer_id",
            "start_date":
            "2022-01-01",
            "conversion_window_days":
            14,
            "custom_queries": [
                {
                    "query":
                    "SELECT campaign.accessible_bidding_strategy, segments.ad_destination_type, campaign.start_date, campaign.end_date FROM campaign",
                    "primary_key": None,
                    "cursor_field": "campaign.start_date",
                    "table_name": "happytable",
                },
                {
                    "query":
                    "SELECT segments.ad_destination_type, segments.ad_network_type, segments.day_of_week, customer.auto_tagging_enabled, customer.id, metrics.conversions, campaign.start_date FROM campaign",
                    "primary_key": "customer.id",
                    "cursor_field": None,
                    "table_name": "unhappytable",
                },
                {
                    "query":
                    "SELECT ad_group.targeting_setting.target_restrictions FROM ad_group",
                    "primary_key": "customer.id",
                    "cursor_field": None,
                    "table_name": "ad_group_custom",
                },
            ],
        },
    )
    assert not check_successful
    assert message.startswith(
        "Unable to connect to Google Ads API with the provided configuration")
Exemplo n.º 26
0
def test_invalid_custom_query_handled(mocked_gads_api, config):
    # limit to one custom query, otherwise need to mock more side effects
    config["custom_queries"] = [next(iter(config["custom_queries"]))]
    mocked_gads_api(
        response=[{
            "customer.id": "8765"
        }],
        failure_msg=
        "Unrecognized field in the query: 'ad_group_ad.ad.video_ad.media_file'",
        error_type="request_error",
    )
    source = SourceGoogleAds()
    status_ok, error = source.check_connection(AirbyteLogger(), config)
    assert not status_ok
    assert error == (
        "Unable to connect to Google Ads API with the provided configuration - Unrecognized field in the query: "
        "'ad_group_ad.ad.video_ad.media_file'")
Exemplo n.º 27
0
def test_check_connection_should_pass_when_config_valid(mocker):
    mocker.patch("source_google_ads.source.GoogleAds", MockGoogleAdsClient)
    source = SourceGoogleAds()
    check_successful, message = source.check_connection(
        AirbyteLogger(),
        {
            "credentials": {
                "developer_token": "fake_developer_token",
                "client_id": "fake_client_id",
                "client_secret": "fake_client_secret",
                "refresh_token": "fake_refresh_token",
            },
            "customer_id":
            "fake_customer_id",
            "start_date":
            "2022-01-01",
            "conversion_window_days":
            14,
            "custom_queries": [
                {
                    "query":
                    "SELECT campaign.accessible_bidding_strategy, segments.ad_destination_type, campaign.start_date, campaign.end_date FROM campaign",
                    "primary_key": None,
                    "cursor_field": "campaign.start_date",
                    "table_name": "happytable",
                },
                {
                    "query":
                    "SELECT segments.ad_destination_type, segments.ad_network_type, segments.day_of_week, customer.auto_tagging_enabled, customer.id, metrics.conversions, campaign.start_date FROM campaign",
                    "primary_key": "customer.id",
                    "cursor_field": None,
                    "table_name": "unhappytable",
                },
                {
                    "query":
                    "SELECT ad_group.targeting_setting.target_restrictions FROM ad_group",
                    "primary_key": "customer.id",
                    "cursor_field": None,
                    "table_name": "ad_group_custom",
                },
            ],
        },
    )
    assert check_successful
    assert message is None
Exemplo n.º 28
0
class AbstractTestParser(ABC):
    """Prefix this class with Abstract so the tests don't run here but only in the children"""

    logger = AirbyteLogger()

    def _get_readmode(self, test_name, test_file):
        self.logger.info(
            f"testing {test_name}() with {test_file.get('test_alias', test_file['filepath'].split('/')[-1])} ..."
        )
        return "rb" if test_file["AbstractFileParser"].is_binary else "r"

    def test_get_inferred_schema(self, test_file):
        with smart_open(test_file["filepath"],
                        self._get_readmode("get_inferred_schema",
                                           test_file)) as f:
            if "test_get_inferred_schema" in test_file["fails"]:
                with pytest.raises(Exception) as e_info:
                    test_file["AbstractFileParser"].get_inferred_schema(f)
                    self.logger.debug(str(e_info))
            else:
                inferred_schema = test_file[
                    "AbstractFileParser"].get_inferred_schema(f)
                expected_schema = test_file["inferred_schema"]
                assert inferred_schema == expected_schema

    def test_stream_records(self, test_file):
        with smart_open(test_file["filepath"],
                        self._get_readmode("stream_records", test_file)) as f:
            if "test_stream_records" in test_file["fails"]:
                with pytest.raises(Exception) as e_info:
                    [
                        print(r) for r in
                        test_file["AbstractFileParser"].stream_records(f)
                    ]
                    self.logger.debug(str(e_info))
            else:
                records = [
                    r
                    for r in test_file["AbstractFileParser"].stream_records(f)
                ]

                assert len(records) == test_file["num_records"]
                for index, expected_record in test_file["line_checks"].items():
                    assert records[index - 1] == expected_record
Exemplo n.º 29
0
 def read(
     self,
     logger: AirbyteLogger,
     config: Mapping[str, Any],
     catalog: ConfiguredAirbyteCatalog,
     state: MutableMapping[str, Any] = None,
 ) -> Iterable[AirbyteMessage]:
     logger.info(I_AM_A_SECRET_VALUE)
     logger.info(I_AM_A_SECRET_VALUE + " plus Some non secret Value in the same log record" + NOT_A_SECRET_VALUE)
     logger.info(NOT_A_SECRET_VALUE)
     yield AirbyteMessage(
         record=AirbyteRecordMessage(stream="stream", data={"data": "stuff"}, emitted_at=1),
         type=Type.RECORD,
     )
Exemplo n.º 30
0
    def request_params(self,
                       stream_state: Mapping[str, Any] = None,
                       next_page_token: Mapping[str, Any] = None,
                       **kwargs):
        params = super().request_params(stream_state=stream_state,
                                        next_page_token=next_page_token,
                                        **kwargs)
        AirbyteLogger().log("INFO", f"using params: {params}")
        # If there is a next page token then we should only send pagination-related parameters.
        if not next_page_token:
            params["orderby"] = self.order_field
            params["order"] = "asc"
            if stream_state:
                start_date = stream_state.get(self.cursor_field)
                start_date = pendulum.parse(start_date).replace(tzinfo=None)
                start_date = start_date.subtract(
                    days=self.conversion_window_days)

                params["after"] = start_date
        return params