airbyte-source-google-search-console 1.7.0__py3-none-any.whl → 1.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {airbyte_source_google_search_console-1.7.0.dist-info → airbyte_source_google_search_console-1.9.0.dist-info}/METADATA +1 -1
- airbyte_source_google_search_console-1.9.0.dist-info/RECORD +13 -0
- source_google_search_console/components.py +105 -3
- source_google_search_console/manifest.yaml +898 -30
- source_google_search_console/source.py +12 -37
- source_google_search_console/spec.json +9 -0
- airbyte_source_google_search_console-1.7.0.dist-info/RECORD +0 -22
- source_google_search_console/schemas/search_analytics_all_fields.json +0 -53
- source_google_search_console/schemas/search_analytics_by_date.json +0 -37
- source_google_search_console/schemas/search_analytics_by_device.json +0 -41
- source_google_search_console/schemas/search_analytics_by_page.json +0 -41
- source_google_search_console/schemas/search_analytics_by_query.json +0 -41
- source_google_search_console/schemas/search_analytics_page_report.json +0 -50
- source_google_search_console/schemas/search_analytics_site_report_by_page.json +0 -46
- source_google_search_console/schemas/search_analytics_site_report_by_site.json +0 -46
- source_google_search_console/streams.py +0 -394
- {airbyte_source_google_search_console-1.7.0.dist-info → airbyte_source_google_search_console-1.9.0.dist-info}/WHEEL +0 -0
- {airbyte_source_google_search_console-1.7.0.dist-info → airbyte_source_google_search_console-1.9.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
source_google_search_console/__init__.py,sha256=HQCPu-CK7XmVDtP9rmTdB2XyraVCc6pv9pw38-O8y48,1191
|
2
|
+
source_google_search_console/components.py,sha256=_6gl-xgwmvRgChEGNZhMsuYAcTw7gIf3yfe7IKvWkPk,6767
|
3
|
+
source_google_search_console/config_migrations.py,sha256=Cl4SUdJpAf6wMM_vVhqjjU89NfUq9LIGJ9zNrWiBk-A,4235
|
4
|
+
source_google_search_console/exceptions.py,sha256=iD3jYC4WxVCEKGsqQ7Vaj1tbjhJZ4S5mnSDnwFJdsIQ,1097
|
5
|
+
source_google_search_console/manifest.yaml,sha256=s7WVxmLhgyobNNovFcHAu6JYKX_4UlqnSID1pC1HKJE,54144
|
6
|
+
source_google_search_console/run.py,sha256=TBkPlseTERarkj6wL8AMEKgm5Xsb2drnltPVH6257-M,2195
|
7
|
+
source_google_search_console/service_account_authenticator.py,sha256=pAWKAXfwfTY3xkXvQJH0EyFphFULdCIcC47YXYTO9X8,1307
|
8
|
+
source_google_search_console/source.py,sha256=TFuafbBg8Nlb-LsYwNSXH1Xcz9U0ELZRX0e3hjCKPzs,7669
|
9
|
+
source_google_search_console/spec.json,sha256=HebTMHop7twcJH6jjgAccYEgg93bTLGsp6jVdMj1x0c,9409
|
10
|
+
airbyte_source_google_search_console-1.9.0.dist-info/METADATA,sha256=dqntQEZ33A7CkZ8YeSjbWtpglyVDRcw_4328p8Tkxjg,5621
|
11
|
+
airbyte_source_google_search_console-1.9.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
12
|
+
airbyte_source_google_search_console-1.9.0.dist-info/entry_points.txt,sha256=DMcgc9bCX-Vt6hm_68pa77qS3eGdeMhg-UdlFc-XKUM,85
|
13
|
+
airbyte_source_google_search_console-1.9.0.dist-info/RECORD,,
|
@@ -2,10 +2,13 @@
|
|
2
2
|
# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
|
3
3
|
#
|
4
4
|
|
5
|
-
from dataclasses import dataclass
|
6
|
-
from typing import Any, Mapping, Optional
|
5
|
+
from dataclasses import dataclass, field
|
6
|
+
from typing import Any, Dict, List, Mapping, Optional
|
7
7
|
|
8
8
|
from airbyte_cdk.sources.declarative.migrations.state_migration import StateMigration
|
9
|
+
from airbyte_cdk.sources.declarative.schema import SchemaLoader
|
10
|
+
from airbyte_cdk.sources.declarative.transformations import RecordTransformation
|
11
|
+
from airbyte_cdk.sources.types import Config, StreamSlice, StreamState
|
9
12
|
|
10
13
|
|
11
14
|
@dataclass
|
@@ -59,7 +62,6 @@ class NestedSubstreamStateMigration(StateMigration):
|
|
59
62
|
return len(stream_state) > 0 and "states" not in stream_state
|
60
63
|
|
61
64
|
def migrate(self, stream_state: Mapping[str, Any]) -> Mapping[str, Any]:
|
62
|
-
global_state: Optional[Mapping[str, Any]] = None
|
63
65
|
per_partition_state = []
|
64
66
|
for site_url_key, search_type_state in stream_state.items():
|
65
67
|
if site_url_key == "date":
|
@@ -79,3 +81,103 @@ class NestedSubstreamStateMigration(StateMigration):
|
|
79
81
|
"use_global_cursor": False,
|
80
82
|
"states": per_partition_state,
|
81
83
|
}
|
84
|
+
|
85
|
+
|
86
|
+
@dataclass
|
87
|
+
class CustomReportExtractDimensionsFromKeys(RecordTransformation):
|
88
|
+
"""
|
89
|
+
A record transformation that remaps each value in the keys array back to its associated
|
90
|
+
dimension. The reason this is a custom component is because we're unable to use list
|
91
|
+
comprehension and and enumerate() is not a valid function in our Jinja contact so can't
|
92
|
+
iterate over the dimensions defined in the config to create each field transformation on the
|
93
|
+
stream_template for each custom report.
|
94
|
+
|
95
|
+
If we were able to, the actual ComponentMappingDefinition would look like this:
|
96
|
+
|
97
|
+
type: ComponentMappingDefinition
|
98
|
+
field_path:
|
99
|
+
- transformations
|
100
|
+
- "1"
|
101
|
+
- fields
|
102
|
+
value: "{{ [{'path': [dimension], 'value': '{{ record['keys'][index]} for index, dimension in enumerate(components_values['dimensions'])] }}"
|
103
|
+
|
104
|
+
or
|
105
|
+
|
106
|
+
type: ComponentMappingDefinition
|
107
|
+
field_path:
|
108
|
+
- transformations
|
109
|
+
- "1"
|
110
|
+
- fields
|
111
|
+
value: >
|
112
|
+
{% for index, dimension in enumerate(components_values["dimensions"]) %}
|
113
|
+
- type: AddFields
|
114
|
+
fields:
|
115
|
+
- path: [ {{ dimension }} ]
|
116
|
+
value: "{{ record['keys'][index] }}"
|
117
|
+
{% endfor %}
|
118
|
+
"""
|
119
|
+
|
120
|
+
dimensions: List[str] = field(default_factory=lambda: [])
|
121
|
+
|
122
|
+
def transform(
|
123
|
+
self,
|
124
|
+
record: Dict[str, Any],
|
125
|
+
config: Optional[Config] = None,
|
126
|
+
stream_state: Optional[StreamState] = None,
|
127
|
+
stream_slice: Optional[StreamSlice] = None,
|
128
|
+
) -> None:
|
129
|
+
for dimension in self.dimensions:
|
130
|
+
record[dimension] = record["keys"].pop(0)
|
131
|
+
|
132
|
+
record.pop("keys")
|
133
|
+
|
134
|
+
|
135
|
+
@dataclass
|
136
|
+
class CustomReportSchemaLoader(SchemaLoader):
|
137
|
+
"""
|
138
|
+
Custom schema loader is needed because Google Search Console's custom reports streams
|
139
|
+
because the schema is dependent on which dimensions are selected in the config. Right now,
|
140
|
+
only DynamicSchemaLoader which is based on the response from an API endpoint supports
|
141
|
+
remapping of types to Airbyte schema types. This CustomReportSchemaLoader functions
|
142
|
+
more like a static schema loader and so we must perform the remapping in this custom component.
|
143
|
+
"""
|
144
|
+
|
145
|
+
DIMENSION_TO_PROPERTY_SCHEMA_MAP = {
|
146
|
+
"country": [{"country": {"type": ["null", "string"]}}],
|
147
|
+
"date": [{"date": {"type": ["null", "string"], "format": "date"}}],
|
148
|
+
"device": [{"device": {"type": ["null", "string"]}}],
|
149
|
+
"page": [{"page": {"type": ["null", "string"]}}],
|
150
|
+
"query": [{"query": {"type": ["null", "string"]}}],
|
151
|
+
}
|
152
|
+
|
153
|
+
dimensions: List[str]
|
154
|
+
|
155
|
+
def get_json_schema(self) -> Mapping[str, Any]:
|
156
|
+
schema: Mapping[str, Any] = {
|
157
|
+
"$schema": "https://json-schema.org/draft-07/schema#",
|
158
|
+
"type": ["null", "object"],
|
159
|
+
"additionalProperties": True,
|
160
|
+
"properties": {
|
161
|
+
# metrics
|
162
|
+
"clicks": {"type": ["null", "integer"]},
|
163
|
+
"ctr": {"type": ["null", "number"], "multipleOf": 1e-25},
|
164
|
+
"impressions": {"type": ["null", "integer"]},
|
165
|
+
"position": {"type": ["null", "number"], "multipleOf": 1e-25},
|
166
|
+
# default fields
|
167
|
+
"search_type": {"type": ["null", "string"]},
|
168
|
+
"site_url": {"type": ["null", "string"]},
|
169
|
+
},
|
170
|
+
}
|
171
|
+
|
172
|
+
# dimensions
|
173
|
+
dimension_properties = self._dimension_to_property_schema()
|
174
|
+
schema["properties"].update(dimension_properties)
|
175
|
+
return schema
|
176
|
+
|
177
|
+
def _dimension_to_property_schema(self) -> dict:
|
178
|
+
properties = {}
|
179
|
+
for dimension in sorted(self.dimensions):
|
180
|
+
fields = self.DIMENSION_TO_PROPERTY_SCHEMA_MAP[dimension]
|
181
|
+
for field in fields:
|
182
|
+
properties = {**properties, **field}
|
183
|
+
return properties
|