1
0
mirror of synced 2026-01-17 12:07:50 -05:00
Files
airbyte/airbyte-integrations/connectors/source-surveymonkey/source_surveymonkey/source.py
Dominik Wagner cb593fae9b 🎉 Source SurveyMonkey: add survey_collectors and collectors stream (#23721)
* add survey_collectors stream

* add stream to docs

* add missing schema

* fix stream and add expected records

* remove unused file

* bump connector version

* fix unit test

* add doc url for surveycollectors

* update expected records

* auto-bump connector version

---------

Co-authored-by: sh4sh <6833405+sh4sh@users.noreply.github.com>
Co-authored-by: marcosmarxm <marcosmarxm@gmail.com>
Co-authored-by: Marcos Marx <marcosmarxm@users.noreply.github.com>
Co-authored-by: Octavia Squidington III <octavia-squidington-iii@users.noreply.github.com>
2023-04-27 07:16:28 -03:00

100 lines
4.0 KiB
Python

#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#
from itertools import groupby
from operator import itemgetter
from typing import Any, List, Mapping, Tuple
import pendulum
import requests
from airbyte_cdk.logger import AirbyteLogger
from airbyte_cdk.sources import AbstractSource
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http.auth import TokenAuthenticator
from .streams import Collectors, SurveyCollectors, SurveyPages, SurveyQuestions, SurveyResponses, Surveys
class SourceSurveymonkey(AbstractSource):
SCOPES = {"responses_read_detail", "surveys_read", "users_read"}
@classmethod
def _check_credentials(cls, config: Mapping[str, Any]) -> Tuple[bool, Any]:
# check if the credentials are provided correctly, because for now these value are not required in spec
if not config.get("access_token"):
credentials = config.get("credentials", {})
if not credentials:
return False, "credentials fields are not provided"
else:
if not credentials.get("auth_method"):
return False, "auth_method in credentials is not provided"
if not credentials.get("access_token"):
return False, "access_token in credentials is not provided"
return True, None
def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, Any]:
is_valid_credentials, msg = self._check_credentials(config)
if not is_valid_credentials:
return is_valid_credentials, msg
authenticator = self.get_authenticator(config)
if "survey_ids" in config:
# Check whether survey id exists and collect errors
errors = []
for survey_id in config["survey_ids"]:
response = requests.head(
url=f"https://api.surveymonkey.com/v3/surveys/{survey_id}/details", headers=authenticator.get_auth_header()
)
try:
response.raise_for_status()
except requests.exceptions.HTTPError:
errors.append((survey_id, f"{response.status_code} {response.reason}"))
if errors:
# Group survey ids by their error type
survey_id_index, error_message_index = 0, 1
msg = "; ".join(
[
f"{error_type}: {', '.join(list(map(itemgetter(survey_id_index), survey_ids)))}"
for error_type, survey_ids in groupby(errors, lambda x: x[error_message_index])
]
)
return False, msg
try:
response = requests.get(url="https://api.surveymonkey.com/v3/users/me", headers=authenticator.get_auth_header())
response.raise_for_status()
return self._check_scopes(response.json())
except Exception as e:
return False, repr(e)
def streams(self, config: Mapping[str, Any]) -> List[Stream]:
authenticator = self.get_authenticator(config)
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 [
Collectors(**args),
Surveys(**args),
SurveyCollectors(**args),
SurveyPages(**args),
SurveyQuestions(**args),
SurveyResponses(**args),
]
@staticmethod
def get_authenticator(config: Mapping[str, Any]):
token = config.get("credentials", {}).get("access_token")
if not token:
token = config["access_token"]
return TokenAuthenticator(token=token)
@classmethod
def _check_scopes(cls, response_json):
granted_scopes = response_json["scopes"]["granted"]
missed_scopes = cls.SCOPES - set(granted_scopes)
if missed_scopes:
return False, "missed required scopes: " + ", ".join(missed_scopes)
return True, None