# # Copyright (c) 2023 Airbyte, Inc., all rights reserved. # from abc import ABC from typing import Any, Iterable, Mapping, MutableMapping, Optional from urllib.parse import parse_qs, urlparse import requests from airbyte_cdk.sources.streams.http import HttpStream # Basic full refresh stream class HellobatonStream(HttpStream, ABC): """ This class represents a stream output by the connector. This is an abstract base class meant to contain all the common functionality at the API level e.g: the API base URL, pagination strategy, parsing responses etc.. """ page_size: int = 100 primary_key: str = "id" def __init__(self, company: str, api_key: str, **kwargs): super().__init__(**kwargs) self.api_key = api_key self.company = company @property def url_base(self) -> str: """ Using this method instead of class init to dynamically generate base url based on config """ company = self.company return f"https://{company}.hellobaton.com/api/" def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]: """ Logic to generate next page token based on the response """ payload = response.json() result_count = payload["count"] if result_count > self.page_size: query_string = urlparse(payload["next"]).query next_page_token = parse_qs(query_string).get("page", None) else: next_page_token = None return next_page_token def request_params( self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None ) -> MutableMapping[str, Any]: """ API request params which expect an api key for auth and any pagination is done using defined in the next_page_token method """ params = {"api_key": self.api_key, "page_size": self.page_size, "page": next_page_token} return params def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]: """ May want to add logic here to unpack foreign keys from urls but tbd For now each response record is accessed through the results key in the JSON payload """ for results in response.json()["results"]: yield results class Activity(HellobatonStream): """ Activity stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "activity" class Companies(HellobatonStream): """ Companies stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "companies" class Milestones(HellobatonStream): """ Milestones stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "milestones" class Phases(HellobatonStream): """ Phases stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "phases" class ProjectAttachments(HellobatonStream): """ Project attachments stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "project_attachments" class Projects(HellobatonStream): """ Projects stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "projects" class Tasks(HellobatonStream): """ Tasks stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "tasks" class TaskAttachments(HellobatonStream): """ Task attachments stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "task_attachments" class Templates(HellobatonStream): """ Templates stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "templates" class TimeEntries(HellobatonStream): """ Time entries stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "time_entries" class Users(HellobatonStream): """ Users stream class """ def path( self, stream_state: Mapping[str, Any] = None, stream_slice: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None ) -> str: return "users"