Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Source Bing Ads: added TaxCertificate field to accounts schema #35891

Merged
merged 9 commits into from
Mar 29, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ data:
connectorSubtype: api
connectorType: source
definitionId: 47f25999-dd5e-4636-8c39-e7cea2453331
dockerImageTag: 2.4.0
dockerImageTag: 2.5.0
dockerRepository: airbyte/source-bing-ads
documentationUrl: https://docs.airbyte.com/integrations/sources/bing-ads
githubIssueLabel: source-bing-ads
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
version = "2.4.0"
version = "2.5.0"
name = "source-bing-ads"
description = "Source implementation for Bing Ads."
authors = [ "Airbyte <contact@airbyte.io>",]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,21 @@ def request_params(
"ReturnAdditionalFields": self.additional_fields,
}

def _transform_tax_fields(self, record: Mapping[str, Any]) -> Mapping[str, Any]:
tax_certificates = record["TaxCertificate"].get("TaxCertificates", {}) if record.get("TaxCertificate") is not None else {}
askarpets marked this conversation as resolved.
Show resolved Hide resolved
if tax_certificates and not isinstance(tax_certificates, list):
askarpets marked this conversation as resolved.
Show resolved Hide resolved
tax_certificate_pairs = tax_certificates.get("KeyValuePairOfstringbase64Binary")
if tax_certificate_pairs:
record["TaxCertificate"]["TaxCertificates"] = tax_certificate_pairs
return record

def parse_response(self, response: sudsobject.Object, **kwargs) -> Iterable[Mapping]:
if response is not None and hasattr(response, self.data_field):
records = self.client.asdict(response)[self.data_field]
for record in records:
if record["Id"] not in self._unique_account_ids:
self._unique_account_ids.add(record["Id"])
yield record
yield self._transform_tax_fields(record)


class Campaigns(BingAdsCampaignManagementStream):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,32 @@
},
"TimeStamp": {
"type": ["null", "string"]
},
"TaxCertificate": {
"type": ["null", "object"],
"properties": {
"TaxCertificateBlobContainerName": {
"type": ["null", "string"]
},
"Status": {
"type": ["null", "string"],
"enum": ["Invalid", "Pending", "Valid"]
},
"TaxCertificates": {
"type": ["null", "array"],
"items": {
"type": ["null", "object"],
"properties": {
"key": {
"type": ["null", "string"]
},
"value": {
"type": ["null", "string"]
}
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@
</a:BusinessAddress>
<a:AutoTagType>Inactive</a:AutoTagType>
<a:SoldToPaymentInstrumentId i:nil="true"/>
<a:TaxCertificate i:nil="false">
<a:TaxCertificateBlobContainerName i:nil="false">Test Container Name</a:TaxCertificateBlobContainerName>
<a:TaxCertificates xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic" i:nil="false">
<a:KeyValuePairOfstringbase64Binary>
<a:key i:nil="false">test_key</a:key>
<a:value i:nil="false">test_value</a:value>
</a:KeyValuePairOfstringbase64Binary>
</a:TaxCertificates>
<a:Status i:nil="false">Active</a:Status>
</a:TaxCertificate>
<a:AccountMode>Expert</a:AccountMode>
</a:AdvertiserAccount>
</Accounts>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
from typing import Any, Dict, Optional, Tuple
from unittest.mock import MagicMock, patch

from airbyte_cdk.models import SyncMode
from airbyte_cdk.test.catalog_builder import CatalogBuilder
from airbyte_cdk.test.entrypoint_wrapper import EntrypointOutput, read
from airbyte_cdk.test.mock_http import HttpMocker
from base_test import BaseTest
from source_bing_ads.source import SourceBingAds
from suds.transport.https import HttpAuthenticated
from suds_response_mock import mock_http_authenticated_send


class TestAccountsStream(BaseTest):
stream_name = "accounts"

def read_stream(
self,
stream_name: str,
sync_mode: SyncMode,
config: Dict[str, Any],
state: Optional[Dict[str, Any]] = None,
expecting_exception: bool = False,
) -> Tuple[EntrypointOutput, MagicMock]:
with patch.object(HttpAuthenticated, "send", mock_http_authenticated_send):
catalog = CatalogBuilder().with_stream(stream_name, sync_mode).build()
return read(SourceBingAds(), config, catalog, state, expecting_exception)

@HttpMocker()
def test_read_accounts_tax_certificate_data(self, http_mocker):
# Our account doesn't have configured Tax certificate.
self.auth_client(http_mocker)
output = self.read_stream(self.stream_name, SyncMode.full_refresh, self._config)
assert output.records[0].record.data["TaxCertificate"] == {
"Status": "Active",
"TaxCertificateBlobContainerName": "Test Container Name",
"TaxCertificates": [{"key": "test_key", "value": "test_value"}],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#

from unittest.mock import patch

import pytest
import source_bing_ads
from source_bing_ads.base_streams import Accounts


@patch.object(source_bing_ads.source, "Client")
@pytest.mark.parametrize(
"record, expected",
[
(
{
"AccountId": 16253412,
"TaxCertificate": {
"TaxCertificates": {"KeyValuePairOfstringbase64Binary": [{"key": "test key", "value": "test value"}]},
"Status": "Active",
"TaxCertificateBlobContainerName": "Test Container Name",
},
},
{
"AccountId": 16253412,
"TaxCertificate": {
"TaxCertificates": [{"key": "test key", "value": "test value"}],
"Status": "Active",
"TaxCertificateBlobContainerName": "Test Container Name",
},
},
),
(
{
"AccountId": 16253412,
"TaxCertificate": {
"TaxCertificates": [{"key": "test key", "value": "test value"}],
"Status": "Active",
"TaxCertificateBlobContainerName": "Test Container Name",
},
},
{
"AccountId": 16253412,
"TaxCertificate": {
"TaxCertificates": [{"key": "test key", "value": "test value"}],
"Status": "Active",
"TaxCertificateBlobContainerName": "Test Container Name",
},
},
),
(
{
"AccountId": 16253412,
},
{
"AccountId": 16253412,
},
),
(
{"AccountId": 16253412, "TaxCertificate": None},
{"AccountId": 16253412, "TaxCertificate": None},
),
],
ids=[
"record_with_KeyValuePairOfstringbase64Binary_field",
"record_without_KeyValuePairOfstringbase64Binary_field",
"record_without_TaxCertificate_field",
"record_with_TaxCertificate_is_None",
],
)
def test_accounts_transform_tax_fields(mocked_client, config, record, expected):
stream = Accounts(mocked_client, config)
actual = stream._transform_tax_fields(record)
assert actual == expected
1 change: 1 addition & 0 deletions docs/integrations/sources/bing-ads.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ The Bing Ads API limits the number of requests for all Microsoft Advertising cli

| Version | Date | Pull Request | Subject |
|:--------|:-----------|:---------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------|
| 2.5.0 | 2024-03-21 | [35891](https://github.com/airbytehq/airbyte/pull/35891) | Accounts stream: add TaxCertificate field to schema. |
| 2.4.0 | 2024-03-19 | [36267](https://github.com/airbytehq/airbyte/pull/36267) | Pin airbyte-cdk version to `^0` |
| 2.3.0 | 2024-03-05 | [35812](https://github.com/airbytehq/airbyte/pull/35812) | New streams: Audience Performance Report, Goals And Funnels Report, Product Dimension Performance Report. |
| 2.2.0 | 2024-02-13 | [35201](https://github.com/airbytehq/airbyte/pull/35201) | New streams: Budget and Product Dimension Performance. |
Expand Down
Loading