* 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>
100 lines
4.0 KiB
Python
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
|