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 Zenloop: migrate to lowcode #19624

Merged
merged 13 commits into from
Dec 9, 2022
2 changes: 1 addition & 1 deletion airbyte-integrations/connectors/source-zenloop/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ COPY source_zenloop ./source_zenloop
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.1.3
LABEL io.airbyte.version=0.1.4
LABEL io.airbyte.name=airbyte/source-zenloop
2 changes: 1 addition & 1 deletion airbyte-integrations/connectors/source-zenloop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ docker run --rm -v $(pwd)/secrets:/secrets -v $(pwd)/integration_tests:/integrat
Make sure to familiarize yourself with [pytest test discovery](https://docs.pytest.org/en/latest/goodpractices.html#test-discovery) to know how your test files and methods should be named.
First install test dependencies into your virtual environment:
```
pip install .[tests]
pip install .'[tests]'
```
### Unit Tests
To run unit tests locally, from the connector directory run:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,12 @@ tests:
- config_path: "secrets/config.json"
basic_read:
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_answers.json"
lazebnyi marked this conversation as resolved.
Show resolved Hide resolved
empty_streams: []
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_surveys.json"
empty_streams: []
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_answers_survey_group.json"
empty_streams: []
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_survey_groups.json"
empty_streams: []
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_properties.json"
configured_catalog_path: "integration_tests/configured_catalog.json"
empty_streams: []
incremental:
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_answers.json"
future_state_path: "integration_tests/abnormal_state.json"
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_answers_survey_group.json"
configured_catalog_path: "integration_tests/configured_catalog.json"
future_state_path: "integration_tests/abnormal_state.json"
full_refresh:
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_survey_groups.json"
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_surveys.json"
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog_properties.json"
configured_catalog_path: "integration_tests/configured_catalog.json"
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
{
"streams": [
{
"stream": {
"name": "answers",
"json_schema": {
lazebnyi marked this conversation as resolved.
Show resolved Hide resolved
"$schema": "http://json-schema.org/draft-07/schema#",
"type": ["null", "object"],
"properties": {
"id": {
"type": ["null", "string"]
},
"score_type": {
"type": ["null", "string"]
},
"score": {
"type": ["null", "number"]
},
"sentiment": {
"type": ["null", "string"]
},
"sentiment_per_label_name": {
"type": ["null", "object"]
},
"name": {
"type": ["null", "string"]
},
"recipient_id": {
"type": ["null", "string"]
},
"property_ids": {
"type": ["null", "array"]
},
"metatags": {
"type": ["null", "object"]
},
"labels": {
"type": ["null", "array"]
},
"labels_with_keywords": {
"type": ["null", "object"]
},
"inserted_at": {
"type": ["null", "string"],
"format": "date-time"
},
"email": {
"type": ["null", "string"]
},
"identity": {
"type": ["null", "string"]
},
"identity_type": {
"type": ["null", "string"]
},
"comment": {
"type": ["null", "string"]
},
"translated_comment": {
"type": ["null", "string"]
},
"additional_answers": {
"type": ["null", "array"],
"items": {
"properties": {
"additional_question_id": {
"type": ["null", "string"]
},
"answer": {
"type": ["null", "string"]
},
"inserted_at": {
"type": ["null", "string"],
"format": "date-time"
}
}
}
},
"additional_questions": {
"type": ["null", "object"]
}
}
},
"supported_sync_modes": ["full_refresh", "incremental"]
},
"sync_mode": "incremental",
"destination_sync_mode": "append"
},
{
"stream": {
"name": "surveys",
"json_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": ["null", "object"],
"properties": {
"title": {
"type": ["null", "string"]
},
"status": {
"type": ["null", "string"]
},
"public_hash_id": {
"type": ["null", "string"]
},
"inserted_at": {
"type": ["null", "string"],
"format": "date-time"
}
}
},
"supported_sync_modes": ["full_refresh"]
},
"sync_mode": "full_refresh",
"destination_sync_mode": "overwrite"
},
{
"stream": {
"name": "survey_groups",
"json_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": ["null", "object"],
"properties": {
"surveys": {
"type": ["null", "array"],
"items": {
"properties": {
"title": {
"type": ["null", "string"]
},
"status": {
"type": ["null", "string"]
},
"public_hash_id": {
"type": ["null", "string"]
},
"inserted_at": {
"type": ["null", "string"],
"format": "date-time"
}
}
}
},
"name": {
"type": ["null", "string"]
},
"public_hash_id": {
"type": ["null", "string"]
},
"inserted_at": {
"type": ["null", "string"],
"format": "date-time"
}
}
},
"supported_sync_modes": ["full_refresh"]
},
"sync_mode": "full_refresh",
"destination_sync_mode": "overwrite"
},
{
"stream": {
"name": "answers_survey_group",
"json_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": ["null", "object"],
"properties": {
"id": {
"type": ["null", "string"]
},
"survey_public_hash_id": {
"type": ["null", "string"]
},
"score_type": {
"type": ["null", "string"]
},
"score": {
"type": ["null", "number"]
},
"sentiment": {
"type": ["null", "string"]
},
"sentiment_per_label_name": {
"type": ["null", "object"]
},
"name": {
"type": ["null", "string"]
},
"recipient_id": {
"type": ["null", "string"]
},
"property_ids": {
"type": ["null", "array"]
},
"metatags": {
"type": ["null", "object"]
},
"labels": {
"type": ["null", "array"]
},
"labels_with_keywords": {
"type": ["null", "object"]
},
"inserted_at": {
"type": ["null", "string"],
"format": "date-time"
},
"email": {
"type": ["null", "string"]
},
"identity": {
"type": ["null", "string"]
},
"identity_type": {
"type": ["null", "string"]
},
"comment": {
"type": ["null", "string"]
},
"translated_comment": {
"type": ["null", "string"]
},
"additional_questions": {
"type": ["null", "object"]
}
}
},
"supported_sync_modes": ["full_refresh", "incremental"]
},
"sync_mode": "incremental",
"destination_sync_mode": "append"
},
{
"stream": {
"name": "properties",
"json_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": ["null", "object"],
"properties": {
"id": {
"type": ["null", "string"]
},
"name": {
"type": ["null", "string"]
},
"value": {
"type": ["null", "string"]
}
}
},
"supported_sync_modes": ["full_refresh"]
},
"sync_mode": "full_refresh",
"destination_sync_mode": "overwrite"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#
# Copyright (c) 2022 Airbyte, Inc., all rights reserved.
#


import math
from dataclasses import dataclass
from typing import Any, Iterable, List, Mapping, Optional

import requests
from airbyte_cdk.models import SyncMode
from airbyte_cdk.sources.declarative.requesters.paginators.strategies.page_increment import PageIncrement
from airbyte_cdk.sources.declarative.stream_slicers.datetime_stream_slicer import DatetimeStreamSlicer
from airbyte_cdk.sources.declarative.stream_slicers.substream_slicer import SubstreamSlicer
from airbyte_cdk.sources.declarative.types import StreamSlice, StreamState


@dataclass
class ZenloopDatetimeStreamSlicer(DatetimeStreamSlicer):
lazebnyi marked this conversation as resolved.
Show resolved Hide resolved
def get_request_params(
self,
*,
stream_state: Optional[StreamState] = None,
stream_slice: Optional[StreamSlice] = None,
next_page_token: Optional[Mapping[str, Any]] = None,
) -> Mapping[str, Any]:
params = super().get_request_params(stream_state=stream_state, stream_slice=stream_slice, next_page_token=next_page_token)
date_shortcut = {"date_shortcut": "custom"}
lazebnyi marked this conversation as resolved.
Show resolved Hide resolved
params.update(date_shortcut)
return params


@dataclass
class ZenloopSubstreamSlicer(SubstreamSlicer):
lazebnyi marked this conversation as resolved.
Show resolved Hide resolved
def stream_slices(self, sync_mode: SyncMode, stream_state: StreamState) -> Iterable[StreamSlice]:
config = self._options.get("config")
parent_field = self._options.get("config_parent_field")
custom_stream_state_value = config.get(parent_field)

if not custom_stream_state_value:
yield from super().stream_slices(sync_mode, stream_state)
else:
for parent_stream_config in self.parent_stream_configs:
stream_state_field = parent_stream_config.stream_slice_field or None
yield {stream_state_field: custom_stream_state_value, "parent_slice": {}}


@dataclass
class ZenloopPageIncrement(PageIncrement):
lazebnyi marked this conversation as resolved.
Show resolved Hide resolved
"""
Starts page from 1 instead of the default value that is 0. Stops Pagination when next page not exist.
"""

def next_page_token(self, response: requests.Response, last_records: List[Mapping[str, Any]]) -> Optional[Any]:
decoded_response = response.json()

current_page = decoded_response["meta"]["page"]
per_page = decoded_response["meta"]["per_page"]
total = decoded_response["meta"]["total"]

next_page_exist = current_page < math.ceil(total / per_page)

if next_page_exist:
self._page += 1
return self._page
else:
return None

def __post_init__(self, options: Mapping[str, Any]):
self._page = 1

def reset(self):
self._page = 1
Loading