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 SurveyMonkey: add survey_collectors and collectors stream #23721

Merged
merged 14 commits into from
Apr 27, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2044,7 +2044,7 @@
- name: SurveyMonkey
sourceDefinitionId: badc5925-0485-42be-8caa-b34096cb71b5
dockerRepository: airbyte/source-surveymonkey
dockerImageTag: 0.1.16
dockerImageTag: 0.2.0
documentationUrl: https://docs.airbyte.com/integrations/sources/surveymonkey
icon: surveymonkey.svg
sourceType: api
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ RUN pip install .
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.1.16
LABEL io.airbyte.version=0.2.0
LABEL io.airbyte.name=airbyte/source-surveymonkey
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@
"sync_mode": "full_refresh",
"cursor_field": [],
"destination_sync_mode": "append"
},
{
"stream": {
"name": "survey_collectors",
"json_schema": {},
"supported_sync_modes": ["full_refresh"],
"source_defined_cursor": true,
"default_cursor_field": []
},
"sync_mode": "full_refresh",
"cursor_field": [],
"destination_sync_mode": "append"
},
{
"stream": {
"name": "collectors",
"json_schema": {},
"supported_sync_modes": ["full_refresh"],
"source_defined_cursor": true,
"default_cursor_field": []
},
"sync_mode": "full_refresh",
"cursor_field": [],
"destination_sync_mode": "append"
}
]
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"streams": [
{
"stream": {
"name": "collectors",
"json_schema": {},
"supported_sync_modes": ["full_refresh"]
},
"sync_mode": "full_refresh",
"destination_sync_mode": "append"
},
{
"stream": {
"name": "survey_collectors",
"json_schema": {},
"supported_sync_modes": ["full_refresh"]
},
"sync_mode": "full_refresh",
"destination_sync_mode": "append"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": true,
"properties": {
"id": {
"type": ["string", "null"]
},
"status": {
"type": ["string", "null"]
},
"survey_id": {
"type": ["string", "null"]
},
"type": {
"type": ["string", "null"]
},
"name": {
"type": ["string", "null"]
},
"thank_you_message": {
"type": ["string", "null"]
},
"thank_you_page": {
"type": ["object", "null"],
"additionalProperties": true,
"properties": {
"is_enabled": {
"type": ["boolean", "null"]
},
"message": {
"type": ["string", "null"]
}
}
},
"disqualification_message": {
"type": ["string", "null"]
},
"disqualification_url": {
"type": ["string", "null"]
},
"close_date": {
"type": ["string", "null"],
"format": "date-time"
},
"closed_page_message": {
"type": ["string", "null"]
},
"redirect_url": {
"type": ["string", "null"]
},
"display_survey_results": {
"type": ["boolean", "null"]
},
"edit_response_type": {
"type": ["string", "null"]
},
"anonymous_type": {
"type": ["string", "null"]
},
"allow_multiple_responses": {
"type": ["boolean", "null"]
},
"date_modified": {
"type": ["string", "null"],
"format": "date-time"
},
"sender_email": {
"type": ["string", "null"]
},
"password_enabled": {
"type": ["boolean", "null"]
},
"response_limit": {
"type": ["number", "null"]
},
"redirect_type": {
"type": ["string", "null"]
},
"respondent_authentication": {
"type": ["boolean", "null"]
},
"href": {
"type": ["string", "null"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": true,
"properties": {
"id": {
"type": ["string", "null"]
},
"name": {
"type": ["string", "null"]
},
"href": {
"type": ["string", "null"]
},
"survey_id": {
"type": ["string", "null"]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http.auth import TokenAuthenticator

from .streams import SurveyPages, SurveyQuestions, SurveyResponses, Surveys
from .streams import Collectors, Surveys, SurveyCollectors, SurveyPages, SurveyQuestions, SurveyResponses


class SourceSurveymonkey(AbstractSource):
Expand Down Expand Up @@ -74,7 +74,14 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]:
start_date = pendulum.parse(config["start_date"])
survey_ids = config.get("survey_ids", [])
args = {"authenticator": authenticator, "start_date": start_date, "survey_ids": survey_ids}
return [Surveys(**args), SurveyPages(**args), SurveyQuestions(**args), SurveyResponses(**args)]
return [
Collectors(**args),
Surveys(**args),
SurveyCollectors(**args),
SurveyPages(**args),
SurveyQuestions(**args),
SurveyResponses(**args),
]

@staticmethod
def get_authenticator(config: Mapping[str, Any]):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,37 @@ def request_params(self, stream_state: Mapping[str, Any], stream_slice: Mapping[
since_value = self._start_date
params["start_modified_at"] = since_value.strftime("%Y-%m-%dT%H:%M:%S")
return params


class SurveyCollectors(SurveyIDSliceMixin, SurveymonkeyStream):
"""
API Docs: https://www.surveymonkey.com/developer/api/v3/#api-endpoints-get-surveys-id-collectors
"""

def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str:
return f"surveys/{ stream_slice['survey_id'] }/collectors"

def parse_response(self, response: requests.Response, stream_state: Mapping[str, Any], **kwargs) -> Iterable[Mapping]:
data = super().parse_response(response=response, stream_state=stream_state, **kwargs)
for record in data:
record["survey_id"] = kwargs.get("stream_slice", {}).get("survey_id")
yield record


class Collectors(SurveymonkeyStream):
"""
API Docs: https://www.surveymonkey.com/developer/api/v3/#api-endpoints-get-collectors-id-
"""

data_field = None

def path(self, stream_slice: Mapping[str, Any] = None, **kwargs) -> str:
return f"collectors/{stream_slice['collector_id']}"

def stream_slices(self, stream_state: Mapping[str, Any] = None, **kwargs):

survey_collectors = SurveyCollectors(start_date=self._start_date, survey_ids=self._survey_ids, authenticator=self.authenticator)
survey_ids = survey_collectors.stream_slices(stream_state, **kwargs)
for slice in survey_ids:
for collector in survey_collectors.read_records(sync_mode=SyncMode.full_refresh, stream_state=stream_state, stream_slice=slice):
yield {"collector_id": collector["id"]}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

def test_source_streams():
streams = SourceSurveymonkey().streams(config=new_source_config)
assert len(streams) == 4
assert len(streams) == 6


def test_source_check_connection_old_config(requests_mock):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pytest
from airbyte_cdk.models import SyncMode
from airbyte_cdk.sources.streams.http.auth import NoAuth
from source_surveymonkey.streams import SurveyIds, SurveyPages, SurveyQuestions, SurveyResponses, Surveys
from source_surveymonkey.streams import SurveyCollectors, SurveyIds, SurveyPages, SurveyQuestions, SurveyResponses, Surveys

args_mock = {"authenticator": NoAuth(), "start_date": pendulum.parse("2000-01-01"), "survey_ids": []}

Expand Down Expand Up @@ -326,10 +326,31 @@ def test_survey_questions(requests_mock):
]


def test_survey_collectors(requests_mock):
requests_mock.get("https://api.surveymonkey.com/v3/surveys/307785415/collectors", json={
"data": [{"name": "Teams Poll", "id": "1", "href": "https://api.surveymonkey.com/v3/collectors/1"}],
"per_page": 50,
"page": 1,
"total": 1,
"links": {
"self": "https://api.surveymonkey.com/v3/surveys/307785415/collectors?page=1&per_page=50"
}
})
args = {**args_mock, **{"survey_ids": ["307785415"]}}
records = SurveyCollectors(**args).read_records(sync_mode=SyncMode.full_refresh, stream_slice={"survey_id": "307785415"})
assert list(records) == [
{
"name": "Teams Poll",
"id": "1",
"href": "https://api.surveymonkey.com/v3/collectors/1",
"survey_id": "307785415"
}
]


def test_surveys_next_page_token():
args = {**args_mock, **{"survey_ids": ["307785415"]}}
stream = SurveyIds(**args)

mockresponse = Mock()
mockresponse.json.return_value = {
"links": {
Expand Down
2 changes: 2 additions & 0 deletions docs/integrations/sources/surveymonkey.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Please read this [docs](https://developer.surveymonkey.com/api/v3/#getting-start
* [SurveyPages](https://developer.surveymonkey.com/api/v3/#surveys-id-pages)
* [SurveyQuestions](https://developer.surveymonkey.com/api/v3/#surveys-id-pages-id-questions)
* [SurveyResponses](https://developer.surveymonkey.com/api/v3/#survey-responses) \(Incremental\)
* [SurveyCollectors](https://developer.surveymonkey.com/api/v3/#api-endpoints-get-surveys-id-collectors)

### Performance considerations

Expand All @@ -65,6 +66,7 @@ To cover more data from this source we use caching.

| Version | Date | Pull Request | Subject |
|:--------| :--------- | :------------------------------------------------------- | :--------------------------------------------------------------------- |
| 0.2.0 | 2023-04-18 | [23721](https://github.com/airbytehq/airbyte/pull/23721) | Add `SurveyCollectors` and `Collectors` stream |
| 0.1.16 | 2023-04-13 | [25080](https://github.com/airbytehq/airbyte/pull/25080) | Fix spec.json required fields and update schema for surveys and survey_responses |
| 0.1.15 | 2023-02-11 | [22865](https://github.com/airbytehq/airbyte/pull/22865) | Specified date formatting in specification |
| 0.1.14 | 2023-01-27 | [22024](https://github.com/airbytehq/airbyte/pull/22024) | Set `AvailabilityStrategy` for streams explicitly to `None` |
Expand Down