diff --git a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml index 0e61cd6b65f..07aa535b0e1 100644 --- a/airbyte-config/init/src/main/resources/seed/source_definitions.yaml +++ b/airbyte-config/init/src/main/resources/seed/source_definitions.yaml @@ -745,7 +745,7 @@ - name: TikTok Marketing sourceDefinitionId: 4bfac00d-ce15-44ff-95b9-9e3c3e8fbd35 dockerRepository: airbyte/source-tiktok-marketing - dockerImageTag: 0.1.4 + dockerImageTag: 0.1.5 documentationUrl: https://docs.airbyte.io/integrations/sources/tiktok-marketing icon: tiktok.svg sourceType: api diff --git a/airbyte-integrations/connectors/source-tiktok-marketing/Dockerfile b/airbyte-integrations/connectors/source-tiktok-marketing/Dockerfile index 6b72d874007..13d5967d168 100644 --- a/airbyte-integrations/connectors/source-tiktok-marketing/Dockerfile +++ b/airbyte-integrations/connectors/source-tiktok-marketing/Dockerfile @@ -32,5 +32,5 @@ COPY source_tiktok_marketing ./source_tiktok_marketing ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py" ENTRYPOINT ["python", "/airbyte/integration_code/main.py"] -LABEL io.airbyte.version=0.1.4 +LABEL io.airbyte.version=0.1.5 LABEL io.airbyte.name=airbyte/source-tiktok-marketing diff --git a/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/schemas/audience_reports.json b/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/schemas/audience_reports.json new file mode 100644 index 00000000000..9cc09274170 --- /dev/null +++ b/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/schemas/audience_reports.json @@ -0,0 +1,157 @@ +{ + "type": ["null", "object"], + "properties": { + "metrics": { + "type": ["null", "object"], + "properties": { + "campaign_name": { + "type": ["null", "string"] + }, + "campaign_id": { + "type": ["null", "integer"] + }, + "adgroup_name": { + "type": ["null", "string"] + }, + "placement": { + "type": ["null", "string"] + }, + "adgroup_id": { + "type": ["null", "integer"] + }, + "ad_name": { + "type": ["null", "string"] + }, + "ad_text": { + "type": ["null", "string"] + }, + "tt_app_id": { + "type": ["null", "integer"] + }, + "tt_app_name": { + "type": ["null", "string"] + }, + "mobile_app_id": { + "type": ["null", "integer"] + }, + "promotion_type": { + "type": ["null", "string"] + }, + "dpa_target_audience_type": { + "type": ["null", "string"] + }, + "spend": { + "type": ["null", "number"] + }, + "cash_spend": { + "type": ["null", "number"] + }, + "voucher_spend": { + "type": ["null", "number"] + }, + "cpc": { + "type": ["null", "number"] + }, + "cpm": { + "type": ["null", "number"] + }, + "impressions": { + "type": ["null", "integer"] + }, + "clicks": { + "type": ["null", "integer"] + }, + "ctr": { + "type": ["null", "number"] + }, + "reach": { + "type": ["null", "integer"] + }, + "cost_per_1000_reached": { + "type": ["null", "number"] + }, + "conversion": { + "type": ["null", "integer"] + }, + "cost_per_conversion": { + "type": ["null", "number"] + }, + "conversion_rate": { + "type": ["null", "number"] + }, + "real_time_conversion": { + "type": ["null", "integer"] + }, + "real_time_cost_per_conversion": { + "type": ["null", "number"] + }, + "real_time_conversion_rate": { + "type": ["null", "number"] + }, + "result": { + "type": ["null", "number"] + }, + "cost_per_result": { + "type": ["null", "number"] + }, + "result_rate": { + "type": ["null", "number"] + }, + "real_time_result": { + "type": ["null", "number"] + }, + "real_time_cost_per_result": { + "type": ["null", "number"] + }, + "real_time_result_rate": { + "type": ["null", "number"] + } + } + }, + "dimensions": { + "type": ["null", "object"], + "properties": { + "stat_time_day": { + "type": ["null", "string"], + "format": "date-time" + }, + "country_code": { + "type": ["null", "string"] + }, + "campaign_id": { + "type": ["null", "integer"] + }, + "adgroup_id": { + "type": ["null", "integer"] + }, + "ad_id": { + "type": ["null", "integer"] + }, + "advertiser_id": { + "type": ["null", "integer"] + }, + "gender": { + "type": ["null", "string"] + }, + "age": { + "type": ["null", "string"] + }, + "ac": { + "type": ["null", "string"] + }, + "language": { + "type": ["null", "string"] + }, + "platform": { + "type": ["null", "string"] + }, + "interest_category": { + "type": ["null", "string"] + }, + "placement": { + "type": ["null", "string"] + } + } + } + } +} diff --git a/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/source.py b/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/source.py index 2aa8e1b90f0..08533fc3a46 100644 --- a/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/source.py +++ b/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/source.py @@ -19,13 +19,17 @@ from .spec import ( ) from .streams import ( DEFAULT_START_DATE, + AdGroupAudienceReports, AdGroups, AdGroupsReports, Ads, + AdsAudienceReports, AdsReports, Advertisers, + AdvertisersAudienceReports, AdvertisersReports, Campaigns, + CampaignsAudienceReportsByCountry, CampaignsReports, ReportGranularity, ) @@ -116,5 +120,9 @@ class SourceTiktokMarketing(AbstractSource): AdGroupsReports(**report_args), Campaigns(**args), CampaignsReports(**report_args), + CampaignsAudienceReportsByCountry(**report_args), + AdGroupAudienceReports(**report_args), + AdsAudienceReports(**report_args), + AdvertisersAudienceReports(**report_args), ] return [stream for stream in streams if stream] diff --git a/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/streams.py b/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/streams.py index eded7aff2ec..cdd682e9616 100644 --- a/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/streams.py +++ b/airbyte-integrations/connectors/source-tiktok-marketing/source_tiktok_marketing/streams.py @@ -23,6 +23,14 @@ from airbyte_cdk.sources.utils.transform import TransformConfig, TypeTransformer # TikTok Initial release date is September 2016 DEFAULT_START_DATE = "2016-09-01" +NOT_AUDIENCE_METRICS = [ + "reach", + "cost_per_1000_reached", + "frequency", + "secondary_goal_result", + "cost_per_secondary_goal_result", + "secondary_goal_result_rate", +] T = TypeVar("T") @@ -457,6 +465,7 @@ class BasicReports(IncrementalTiktokStream, ABC): if self.report_granularity and self.report_granularity in spec_time_dimensions: result.append(spec_time_dimensions[self.report_granularity]) + return result def _get_metrics(self): @@ -574,3 +583,51 @@ class AdGroupsReports(BasicReports): """Custom reports for adgroups""" report_level = ReportLevel.ADGROUP + + +class AudienceReport(BasicReports): + """Docs: https://ads.tiktok.com/marketing_api/docs?id=1707957217727489""" + + audience_dimensions: List = ["gender", "age"] + + def get_json_schema(self) -> Mapping[str, Any]: + """All reports have same schema""" + return ResourceSchemaLoader(package_name_from_class(self.__class__)).get_schema("audience_reports") + + def _get_metrics(self): + result = super()._get_metrics() + result = [e for e in result if e not in NOT_AUDIENCE_METRICS] + return result + + def request_params( + self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, **kwargs + ) -> MutableMapping[str, Any]: + params = super().request_params(stream_state=stream_state, stream_slice=stream_slice, **kwargs) + + dimensions = self._get_reporting_dimensions() + dimensions += self.audience_dimensions + params["dimensions"] = json.dumps(dimensions) + params["report_type"] = "AUDIENCE" + + return params + + +class AdGroupAudienceReports(AudienceReport): + report_level = ReportLevel.ADGROUP + + +class AdsAudienceReports(AudienceReport): + + report_level = ReportLevel.AD + + +class AdvertisersAudienceReports(AudienceReport): + + report_level = ReportLevel.ADVERTISER + + +class CampaignsAudienceReportsByCountry(AudienceReport): + """Custom reports for campaigns by country""" + + report_level = ReportLevel.CAMPAIGN + audience_dimensions = ["country_code"] diff --git a/docs/integrations/sources/tiktok-marketing.md b/docs/integrations/sources/tiktok-marketing.md index 001d7bd5df4..c30f8200374 100644 --- a/docs/integrations/sources/tiktok-marketing.md +++ b/docs/integrations/sources/tiktok-marketing.md @@ -51,8 +51,9 @@ Please read [How to get your AppID, Secret and Access Token](https://ads.tiktok. | Version | Date | Pull Request | Subject | |:--------|:-----------| :----- | :------ | -| 0.1.4 | 2021-12-30 | [7636](https://github.com/airbytehq/airbyte/pull/7636) | Add OAuth support | -| 0.1.3 | 2021-12-10 | [8425](https://github.com/airbytehq/airbyte/pull/8425) | Update title, description fields in spec | -| 0.1.2 | 2021-12-02 | [8292](https://github.com/airbytehq/airbyte/pull/8292) | Support reports | -| 0.1.1 | 2021-11-08 | [7499](https://github.com/airbytehq/airbyte/pull/7499) | Remove base-python dependencies | -| 0.1.0 | 2021-09-18 | [5887](https://github.com/airbytehq/airbyte/pull/5887) | Release TikTok Marketing CDK Connector | +| 0.1.5 | 2022-02-17 | [10398](https://github.com/airbytehq/airbyte/pull/10398) | Add Audience reports | +| 0.1.4 | 2021-12-30 | [7636](https://github.com/airbytehq/airbyte/pull/7636) | Add OAuth support | +| 0.1.3 | 2021-12-10 | [8425](https://github.com/airbytehq/airbyte/pull/8425) | Update title, description fields in spec | +| 0.1.2 | 2021-12-02 | [8292](https://github.com/airbytehq/airbyte/pull/8292) | Support reports | +| 0.1.1 | 2021-11-08 | [7499](https://github.com/airbytehq/airbyte/pull/7499) | Remove base-python dependencies | +| 0.1.0 | 2021-09-18 | [5887](https://github.com/airbytehq/airbyte/pull/5887) | Release TikTok Marketing CDK Connector |