diff --git a/README.md b/README.md index 5c0ef2d..f71832c 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ $ python3 setup.py install ``` ## Contents 1) zenduty/api : contains the functions to communicate with zenduty API endpoints -2) zenduty/ : contains the common required files -3) bin/ : contains sample script to run zenduty functions +2) zenduty/apiV2 : contains the functions to communicate with zenduty API endpoints +3) zenduty/ : contains the common required files ## Getting started diff --git a/bin/client.py b/bin/client.py deleted file mode 100644 index d09e957..0000000 --- a/bin/client.py +++ /dev/null @@ -1,11 +0,0 @@ -import zenduty -from zenduty.exceptions import ApiException - -api_instance = zenduty.TeamsApi(zenduty.ApiClient("ENTER-YOUR-ACCESS-TOKEN-HERE")) - -try: - # Get Incidents - api_response = api_instance.get_teams() - print(api_response.data) -except ApiException as e: - print("Exception when calling TeamsApi->api_account_teams_get: %s\n" % e) diff --git a/examples/example_2.py b/examples/example_2.py index bf60048..ca5d2a1 100644 --- a/examples/example_2.py +++ b/examples/example_2.py @@ -1,7 +1,7 @@ from uuid import UUID from zenduty.apiV2.authentication.zenduty_credential import ZendutyCredential from zenduty.apiV2.client import ZendutyClient -from zenduty.apiV2.incidents import IncidentClient +from zenduty.apiV2 import IncidentClient cred = ZendutyCredential("f3ab5c762c914dacca2c0c530b260fdf9fff0cc7") diff --git a/examples/example_3.py b/examples/example_3.py new file mode 100644 index 0000000..54a842c --- /dev/null +++ b/examples/example_3.py @@ -0,0 +1,67 @@ +from zenduty import ZendutyClient + +zenduty_client = ZendutyClient( + api_key="f3ab5c762c914dacca2c0c530b260fdf9fff0cc7", use_https=True +) # Default credentials to ZENDUTY_API_KEY env variable if not provided. (use export ZENDUTY_API_KEY="") + + +from zenduty import AccountMemberClient +members = AccountMemberClient(zenduty_client).get_all_members() +test_member = members[0] +from zenduty import AccountNotificationClient +member_contact_methods = AccountNotificationClient(zenduty_client,test_member).list_member_contact_methods() +member_notification_rules = AccountNotificationClient(zenduty_client, test_member).list_member_notification_rules() + +from zenduty import AccountRoleClient +account_roles = AccountRoleClient(zenduty_client).list_account_roles() + +from zenduty import EventClient + +from zenduty import RouterClient +all_routers = RouterClient(zenduty_client).get_all_routers() + +from zenduty import IncidentClient +all_incidents = IncidentClient(zenduty_client).get_all_incidents(status=3) + +from zenduty import IncidentNoteClient +all_incident_notes = IncidentNoteClient(zenduty_client, all_incidents[0]).get_all_incident_notes() + +from zenduty import IncidentTagClient +all_tags = IncidentTagClient(zenduty_client, all_incidents[0]).get_all_tags() + +from zenduty import TeamsClient +teams = TeamsClient(zenduty_client).list_teams() +team_members = TeamsClient(zenduty_client).list_team_members(teams[0]) +team_permissions = TeamsClient(zenduty_client).fetch_team_permissions(teams[0]) +oncall = TeamsClient(zenduty_client).get_all_oncall(teams[0]) + +from zenduty import EscalationPolicyClient +eps = EscalationPolicyClient(zenduty_client, teams[0]).get_all_policies() + +from zenduty import TeamMaintenanceClient +team_maintenace = TeamMaintenanceClient(zenduty_client, teams[0]).get_all_maintenance() + +from zenduty import OncallClient +oncall_v2 = OncallClient(zenduty_client, teams[0]).list_team_oncall_v2() +oncall = OncallClient(zenduty_client, teams[0]).get_all_oncall() + +from zenduty import PostmortemClient +pm = PostmortemClient(zenduty_client, teams[0]).get_all_postmortem() + +from zenduty import PriorityClient +p = PriorityClient(zenduty_client, teams[0]).get_all_priorities() + +from zenduty import IncidentRoleClient +ir = IncidentRoleClient(zenduty_client, teams[0]).get_all_roles() + +from zenduty import ScheduleClient +schedules = ScheduleClient(zenduty_client, teams[0]).get_all_schedules() + +from zenduty import ServiceClient +sercives = ServiceClient(zenduty_client, teams[0]).get_all_services() + +from zenduty import IntegrationClient +intergrations = IntegrationClient(zenduty_client, teams[0], sercives[0]).get_all_integrations() + +from zenduty import SLAClient +sla = SLAClient(zenduty_client, teams[0]).get_all_slas() \ No newline at end of file diff --git a/zenduty/__init__.py b/zenduty/__init__.py index 2f4a936..ab5d341 100644 --- a/zenduty/__init__.py +++ b/zenduty/__init__.py @@ -1,6 +1,7 @@ from __future__ import absolute_import -__version__ = "1.0.0" +__version__ = "1.2.0" + # import apis into sdk package from .api.incidents_api import IncidentsApi @@ -18,3 +19,6 @@ from .exceptions import ApiValueError from .exceptions import ApiKeyError from .exceptions import ApiException + +# import V2 APIs +import apiV2 diff --git a/zenduty/apiV2/__init__.py b/zenduty/apiV2/__init__.py index e69de29..993f9eb 100644 --- a/zenduty/apiV2/__init__.py +++ b/zenduty/apiV2/__init__.py @@ -0,0 +1,55 @@ +from __future__ import absolute_import + +__version__ = "1.2.0" + +# All Model Imports +from .accounts.members.models import AccountMember, User +from .accounts.notifications.models import NotificationRule, ContactMethod +from .accounts.roles.models import AccountRole +from .events.models import Event +from .events.router.models import Router +from .incidents.models import Incident, IncidentAlert, IntegrationObject +from .incidents.notes.models import IncidentNote +from .incidents.tags.models import Tag as IncidentTag +from .teams.models import Team +from .teams.models import Member as TeamMember +from .teams.escalation_policies.models import EscalationPolicy +from .teams.maintenance.models import TeamMaintenance +from .teams.oncall.models import OnCall, OnCallV2 +from .teams.postmortem.models import Postmortem +from .teams.priorities.models import Priority +from .teams.roles.models import IncidentRole +from .teams.schedules.models import Schedule +from .teams.services.models import Service +from .teams.services.integrations.models import Integration, IntegrationAlert, IntegrationObject +from .teams.sla.models import SLA +from .teams.tags.models import Tag +from .teams.task_templates.models import TaskTemplate + + +# All Clients imports +from .client import ZendutyClient, ZendutyClientRequestMethod +from .accounts.members import AccountMemberClient +from .accounts.notifications import AccountNotificationClient +from .accounts.roles import AccountRoleClient +from .events import EventClient +from .events.router import RouterClient +from .incidents import IncidentClient +from .incidents.notes import IncidentNoteClient +from .incidents.tags import IncidentTagClient +from .teams import TeamsClient +from .teams.escalation_policies import EscalationPolicyClient +from .teams.maintenance import TeamMaintenanceClient +from .teams.oncall import OncallClient +from .teams.postmortem import PostmortemClient +from .teams.priorities import PriorityClient +from .teams.roles import IncidentRoleClient +from .teams.schedules import ScheduleClient +from .teams.services import ServiceClient +from .teams.services.integrations import IntegrationClient +from .teams.sla import SLAClient +from .teams.tags import TagClient +from .teams.task_templates import TaskTemplateClient + +#all exception imports +from .exceptions import APIException \ No newline at end of file diff --git a/zenduty/apiV2/_logging.py b/zenduty/apiV2/_logging.py deleted file mode 100644 index 8e8bb0b..0000000 --- a/zenduty/apiV2/_logging.py +++ /dev/null @@ -1,10 +0,0 @@ -import json -import logging - - -class Logging: - """logging for internals""" - - def info(self, data: dict): - msg = json.dumps(data) - logging.info(msg) diff --git a/zenduty/apiV2/accounts/members/__init__.py b/zenduty/apiV2/accounts/members/__init__.py index eaf896b..a19a6f3 100644 --- a/zenduty/apiV2/accounts/members/__init__.py +++ b/zenduty/apiV2/accounts/members/__init__.py @@ -1,9 +1,7 @@ -import json from uuid import UUID +from .models import AccountMember, User from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod -from .models import AccountMember - class AccountMemberClient: def __init__(self, client: ZendutyClient): diff --git a/zenduty/apiV2/accounts/members/models.py b/zenduty/apiV2/accounts/members/models.py index 370b0a6..be108ff 100644 --- a/zenduty/apiV2/accounts/members/models.py +++ b/zenduty/apiV2/accounts/members/models.py @@ -1,5 +1,6 @@ -from typing import Optional +import logging from uuid import UUID +from typing import Optional from datetime import datetime from zenduty.apiV2.serializer import JsonSerializable @@ -12,12 +13,14 @@ class User(JsonSerializable): email: str def __init__( - self, username: str, first_name: str, last_name: str, email: str + self, username: str, first_name: str, last_name: str, email: str, **kwargs ) -> None: self.username = username self.first_name = first_name self.last_name = last_name self.email = email + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') class AccountMember(JsonSerializable): @@ -39,6 +42,7 @@ def __init__( is_verified: bool, team: Optional[UUID] = None, custom_role_id: Optional[str] = None, + **kwargs ) -> None: self.unique_id = unique_id if isinstance(unique_id, UUID) else unique_id self.time_zone = time_zone @@ -50,3 +54,5 @@ def __init__( self.is_verified = is_verified self.team = team if isinstance(team, UUID) or team is None else UUID(team) self.custom_role_id = custom_role_id + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') diff --git a/zenduty/apiV2/accounts/notifications/__init__.py b/zenduty/apiV2/accounts/notifications/__init__.py new file mode 100644 index 0000000..959e796 --- /dev/null +++ b/zenduty/apiV2/accounts/notifications/__init__.py @@ -0,0 +1,147 @@ +from zenduty.apiV2.accounts.members import AccountMember +from .models import ContactMethod, NotificationRule +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod + + +class AccountNotificationClient: + def __init__(self, client: ZendutyClient, member: AccountMember): + self._client = client + self._member = member + + def list_member_contact_methods(self) -> list[ContactMethod] : + """list members contact methods + Returns: + list [ContactMethodObject]: list of Contact Method objects + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/account/users/{self._member.user.username}/contacts/" + ) + + return [ContactMethod(**contact) for contact in response] + + def get_member_contact_method(self, contact_method_id: str) -> ContactMethod: + """Get member contact method object by contact_method_id + + Args: + contact_method_id (str): the unique id of contact method + + Returns: + ContactMethod: Contact Method object + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/account/users/{self._member.user.username}/contacts/{contact_method_id}/" + ) + return ContactMethod(**response) + + def create_member_contact_method(self, name: str, value: str, contact_type: int) -> ContactMethod: + """Create member contact method + + Args: + contact_method_id (str): the unique id of contact method + name (str): name of the contact method + value (str): value for the contact method + contact_type (int): contact type + + Returns: + ContactMethod: Contact Method object + """ + + response = self._client.execute( + method=ZendutyClientRequestMethod.POST, + endpoint=f"/api/account/users/{self._member.user.username}/contacts/", + request_payload={ + "name":name, + "value": value, + "contact_type": contact_type + }, + success_code=201 + ) + return ContactMethod(**response) + + def delete_member_contact_method(self, contact_method_id: str) -> None: + """Delete member contact method + + Args: + contact_method_id (str): the unique id of contact method + + Returns: + None + """ + + response = self._client.execute( + method=ZendutyClientRequestMethod.DELETE, + endpoint=f"/api/account/users/{self._member.user.username}/contacts/{contact_method_id}", + ) + return None + + def list_member_notification_rules(self) -> list[NotificationRule]: + + """list members notification rules + Returns: + list [NotificationRules]: list of Notification Rule objects + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/account/users/{self._member.user.username}/notification_rules/" + ) + return [NotificationRule(**contact) for contact in response] + + def get_member_notification_rules(self, notification_rule_id: str) -> NotificationRule: + """Get member nottification rule object by notification_rule_id + + Args: + notification_rule_id (str): the unique id of notification rule + + Returns: + ContactMethod: Contact Method object + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/account/users/{self._member.user.username}/notification_rules/{notification_rule_id}/" + ) + + return NotificationRule(**response) + + def create_member_notification_rules(self, start_delay: int, contact_method: str, urgency: int) -> NotificationRule: + """create member nottification rule object by notification_rule_id + + Args: + start_delay (int): the unique id of notification rule + contact_method (ContactMethod): + urgency (int): urgency + + Returns: + ContactMethod: Contact Method object + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.POST, + endpoint=f"/api/account/users/{self._member.user.username}/notification_rules/", + request_payload = { + "contact" : contact_method, + "start_delay": start_delay, + "urgency": urgency + }, + success_code=201 + ) + + return NotificationRule(**response) + + + def delete_member_notification_rule(self, notification_rule_id: str) -> None: + """Delete member notification rule + + Args: + notification_rule_id (str): the unique id of notification rule + + Returns: + None + """ + + response = self._client.execute( + method=ZendutyClientRequestMethod.DELETE, + endpoint=f"/api/account/users/{self._member.user.username}/notification_rules/{notification_rule_id}", + success_code=204 + ) + return None \ No newline at end of file diff --git a/zenduty/apiV2/accounts/notifications/models.py b/zenduty/apiV2/accounts/notifications/models.py new file mode 100644 index 0000000..a61af85 --- /dev/null +++ b/zenduty/apiV2/accounts/notifications/models.py @@ -0,0 +1,59 @@ +import logging +from uuid import UUID +from datetime import datetime + +from zenduty.apiV2.serializer import JsonSerializable + +class ContactMethod(JsonSerializable): + unique_id: UUID + name: str + creation_date: datetime + value: str + contact_type: int + + def __init__( + self, + unique_id: str, + name: str, + creation_date: str, + value: str, + contact_type: int, + **kwargs) -> None: + self.unique_id = unique_id if isinstance(unique_id, UUID) else UUID(unique_id) + self.name = name + self.creation_date = creation_date if isinstance(creation_date, datetime) else datetime.strptime(creation_date, '%Y-%m-%d') + self.value = value + self.contact_type = contact_type + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') + +class NotificationRule(JsonSerializable): + unique_id: UUID + creation_date: datetime + start_delay: int + type: str + contact: UUID + urgency: int + user: str + + def __init__( + self, + unique_id: str, + creation_date: str, + start_delay: int, + type: str, + contact: str, + urgency: int, + user: str, + **kwargs + ): + self.unique_id = unique_id if isinstance(unique_id, UUID) else UUID(unique_id) + self.creation_date = creation_date if isinstance(creation_date, datetime) else datetime.strptime(creation_date, '%Y-%m-%dT%H:%M:%S.%fZ') + self.start_delay = start_delay + self.type = type + self.contact = contact if isinstance(contact, UUID) else UUID(contact) + self.urgency = urgency + self.user = user + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') + \ No newline at end of file diff --git a/zenduty/apiV2/accounts/roles/__init__.py b/zenduty/apiV2/accounts/roles/__init__.py index f45dab8..af2417b 100644 --- a/zenduty/apiV2/accounts/roles/__init__.py +++ b/zenduty/apiV2/accounts/roles/__init__.py @@ -1,6 +1,5 @@ -import json -from ...client import ZendutyClient, ZendutyClientRequestMethod from .models import AccountRole +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class AccountRoleClient: diff --git a/zenduty/apiV2/accounts/roles/models.py b/zenduty/apiV2/accounts/roles/models.py index d5ac5a8..18bcbc6 100644 --- a/zenduty/apiV2/accounts/roles/models.py +++ b/zenduty/apiV2/accounts/roles/models.py @@ -1,9 +1,9 @@ +import logging from uuid import UUID from typing import List from zenduty.apiV2.serializer import JsonSerializable - class AccountRole(JsonSerializable): unique_id: UUID name: str @@ -16,8 +16,11 @@ def __init__( name: str, description: str, permissions: List[str], + **kwargs ) -> None: self.unique_id = unique_id if isinstance(unique_id, UUID) else UUID(unique_id) self.name = name self.description = description self.permissions = permissions + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') diff --git a/zenduty/apiV2/client.py b/zenduty/apiV2/client.py index 7a5ed41..757dd00 100644 --- a/zenduty/apiV2/client.py +++ b/zenduty/apiV2/client.py @@ -1,13 +1,9 @@ -import json +import json, re, urllib3 from uuid import UUID -import requests -from .authentication.zenduty_credential import ZendutyCredential -import regex -from ._logging import Logging from typing import Optional, Union, Any from enum import Enum from json import JSONEncoder - +from .exceptions import APIException def _remove_nulls(d): return { @@ -16,7 +12,6 @@ def _remove_nulls(d): if v is not None or (isinstance(v, str) and len(v) > 0) } - class _ZendutyClientSerializer(JSONEncoder): def default(self, value: Any) -> str: """JSON serialization conversion function.""" @@ -24,7 +19,6 @@ def default(self, value: Any) -> str: return str(value) return super(_ZendutyClientSerializer, self).default(value) - class ZendutyClientRequestMethod(Enum): GET = "GET" POST = "POST" @@ -32,44 +26,10 @@ class ZendutyClientRequestMethod(Enum): PATCH = "PATCH" DELETE = "DELETE" - def clean_json_of_nulls(value: str) -> str: val = json.loads(value, object_hook=_remove_nulls) return json.dumps(val) - -class APIException(Exception): - def __init__(self, code: int, message: Optional[str] = None): - self._code = code - self._message = message - - def __str__(self): - if self._message is None: - return f"error: received code [%d]" % self._code - else: - return f"error: received code [%d] with message: %s" % ( - self._code, - self._message, - ) - - -# def load_data( -# method: ZendutyClientRequestMethod, -# endpoint: str, -# response_data: dict = {}): -# p = None -# with open("response.json", "r") as response: -# payload = response.read() -# p = json.loads(payload) -# if p.get(str(method)) is None: -# p[str(method)] = dict() -# if p[str(method)].get(endpoint) is None: -# p[str(method)][endpoint] = dict() -# p[str(method)][endpoint] = response_data -# with open("response.json", "w") as response: -# response.write(json.dumps(p)) - - class ZendutyClient: """Zenduty client acts as an adapter for Zenduty APIs @@ -77,49 +37,41 @@ class ZendutyClient: APIException: thrown when the api responds back with a non success code """ - _url: str - def __init__( - self, - credential: ZendutyCredential, - base_url: str = "www.zenduty.com", + self, + api_key: str, use_https: bool = True, + base_url: str = "www.zenduty.com", + cert_verify: bool = True ) -> None: - """Constructor for zenduty client - - Args: - credential (ZendutyCredential): credentials class that provies client support - base_url (str, optional): Zenduty contact base url. Defaults to "www.zenduty.com". - use_https (bool, optional): Enable or disable use_https for API requests. Defaults to True. - - Raises: - ValueError: thrown when invalid credentials are supplied - ValueError: thrown when incorrect base url is supplied - """ - if credential is None: - raise ValueError("error: credential must not be None") - - elif not regex.match( - "^([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-]+$", base_url - ): - raise ValueError( - f"error: {base_url} must be a base url. example: zenduty.com" - ) - scheme = "https" if use_https else "http" - self._url = scheme + "://" + base_url - self._headers = {"Authorization": f"Token {credential.get_api_key()}"} - self._logger = Logging() + if cert_verify: + self.pool_manager = urllib3.PoolManager() + + else: + self.pool_manager = urllib3.PoolManager(cert_reqs='CERT_NONE', assert_hostname=False) + + if api_key is None: + raise ValueError("error: api_key must not be None") + + self.bearer_token = api_key + self.headers = { + 'Authorization': f'Token {self.bearer_token}', + 'Content-Type': 'application/json' + } + + if not re.match('^([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-]+$', base_url): + raise ValueError(f"error: {base_url} must be a base url. example: zenduty.com") + + self.base_url = f'https://{base_url}' if use_https else f'http://{base_url}' def execute( self, method: ZendutyClientRequestMethod, endpoint: str, - request_payload: Optional[Union[dict, str]] = {}, - query_params: dict = {}, + request_payload: Optional[Union[dict, str]] = None, success_code: int = 200, ) -> Union[list[dict], dict]: """Execute a Zenduty client request - Args: method (ZendutyClientRequestMethod): HTTP method to use endpoint (str): API endpoint to contact @@ -133,47 +85,23 @@ def execute( Returns: Union[list[dict], dict]: results relevant parsed json payload """ - req = requests.PreparedRequest() - req.prepare_url(f"{self._url}{endpoint}", query_params) - self._logger.info( - { - "class": self.__class__.__name__, - "method": method.value, - "endpoint": req.url, - } - ) - response = requests.request( - headers={ - **self._headers, - "Content-Type": "application/json", - }, - method=method.value, - url=req.url, - data=clean_json_of_nulls( - json.dumps( - request_payload, - cls=_ZendutyClientSerializer, - ) - ) - if isinstance(request_payload, dict) - else request_payload, - ) + url = self.base_url + endpoint + if method.value == "GET": + response = self.pool_manager.request(method=method.value, url=url, headers=self.headers) + + else: + request_payload = json.dumps(request_payload) if request_payload is not None else None + response = self.pool_manager.request(method=method.value, url=url, body=request_payload, headers=self.headers) + try: - data = response.json() - - # load_data(method, endpoint, data) - if response.status_code == success_code: - return data - else: - print(data) - raise APIException( - response.status_code, - data.get("detail", None) if type(data) is dict else None, - ) - except APIException as error: - raise error - except Exception as error: - if response.status_code == success_code: - # load_data(method, endpoint) - return {} - raise APIException(response.status_code, error.__cause__.__str__()) + response_data = json.loads(response.data.decode('utf-8')) + + except: + response_data = {} + + if response.status == success_code: + return response_data + raise APIException( + response.status, + response_data.get("detail", None) if isinstance(response_data, dict) else None + ) \ No newline at end of file diff --git a/zenduty/apiV2/events/__init__.py b/zenduty/apiV2/events/__init__.py index 470f590..7662577 100644 --- a/zenduty/apiV2/events/__init__.py +++ b/zenduty/apiV2/events/__init__.py @@ -1,6 +1,6 @@ -from .router import RouterClient -from ..client import ZendutyClient, ZendutyClientRequestMethod +from zenduty.apiV2.events.router import RouterClient from .models import Event +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class EventClient: diff --git a/zenduty/apiV2/events/models.py b/zenduty/apiV2/events/models.py index 055b612..fa94fb4 100644 --- a/zenduty/apiV2/events/models.py +++ b/zenduty/apiV2/events/models.py @@ -1,7 +1,9 @@ -from datetime import datetime +import json +import logging from uuid import UUID +from datetime import datetime from typing import List, Any, Union -import json + from zenduty.apiV2.serializer import JsonSerializable @@ -27,6 +29,7 @@ def __init__( integration_key: str, is_enabled: bool, integration_type: int, + **kwargs ) -> None: self.name = name self.creation_date = ( @@ -41,6 +44,8 @@ def __init__( self.integration_key = integration_key self.is_enabled = is_enabled self.integration_type = integration_type + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') class Payload: @@ -48,10 +53,12 @@ class Payload: severity: str project: str - def __init__(self, status: str, severity: str, project: str) -> None: + def __init__(self, status: str, severity: str, project: str, **kwargs) -> None: self.status = status self.severity = severity self.project = project + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self) -> str: return json.dumps(self.__dict__) @@ -61,9 +68,11 @@ class URL: link_url: str link_text: str - def __init__(self, link_url: str, link_text: str) -> None: + def __init__(self, link_url: str, link_text: str, **kwargs) -> None: self.link_url = link_url self.link_text = link_text + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self) -> str: return json.dumps(self.__dict__) @@ -103,6 +112,7 @@ def __init__( urls: List[Union[URL, dict]], payload: Union[Payload, dict] = None, incident_created: bool = None, + **kwargs ) -> None: self.integration_object = ( integration_object @@ -127,3 +137,5 @@ def __init__( self.payload = payload self.urls = urls self.incident_created = incident_created + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') diff --git a/zenduty/apiV2/events/router/__init__.py b/zenduty/apiV2/events/router/__init__.py index 0b9a2ea..e7bb373 100644 --- a/zenduty/apiV2/events/router/__init__.py +++ b/zenduty/apiV2/events/router/__init__.py @@ -1,6 +1,7 @@ from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod + from .models import Router +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class RouterClient: diff --git a/zenduty/apiV2/events/router/models.py b/zenduty/apiV2/events/router/models.py index 3027cbc..bf454f4 100644 --- a/zenduty/apiV2/events/router/models.py +++ b/zenduty/apiV2/events/router/models.py @@ -1,3 +1,4 @@ +import logging from uuid import UUID from zenduty.apiV2.serializer import JsonSerializable @@ -23,6 +24,7 @@ def __init__( integration_key: str, created_at: str, account_identifier: str, + **kwargs ) -> None: self.unique_id = unique_id if isinstance(unique_id, UUID) else UUID(unique_id) self.name = name @@ -32,3 +34,6 @@ def __init__( self.integration_key = integration_key self.created_at = created_at self.account_identifier = account_identifier + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') + diff --git a/zenduty/apiV2/exceptions.py b/zenduty/apiV2/exceptions.py new file mode 100644 index 0000000..473bc22 --- /dev/null +++ b/zenduty/apiV2/exceptions.py @@ -0,0 +1,13 @@ +from typing import Optional +class APIException(Exception): + def __init__(self, code: int, message: Optional[str] = None): + self._code = code + self._message = message + + def __str__(self): + if self._message is None: + return f"error: received code [%d]" % self._code + else: + return f"error: received code [%d] with message: %s" % ( + self._code, + ) \ No newline at end of file diff --git a/zenduty/apiV2/incidents/__init__.py b/zenduty/apiV2/incidents/__init__.py index e538655..f943191 100644 --- a/zenduty/apiV2/incidents/__init__.py +++ b/zenduty/apiV2/incidents/__init__.py @@ -1,10 +1,9 @@ from uuid import UUID -from .notes import IncidentNoteClient -from .tags import IncidentTagClient -from ..client import ZendutyClient, ZendutyClientRequestMethod from .models import Incident -from ..events.models import Event - +from .tags import IncidentTagClient +from .notes import IncidentNoteClient +from zenduty.apiV2.events import Event +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class IncidentClient: def __init__(self, client: ZendutyClient): @@ -90,7 +89,22 @@ def get_all_incidents( request_payload=payload, success_code=200, ) - return response.get("results", []) + for incident in response.get("results", []): + incident['summary'] = "" + incident['incident_key'] = "" + incident['service'] = None + incident['urgency'] = 0 + incident['merged_with'] = None + incident['escalation_policy'] = None + incident['escalation_policy_object'] = None + incident['context_window_start'] = None + incident['context_window_end'] = None + incident['team_priority'] = None + incident['parent_incident'] = None + incident['postmortem_assignee'] = None + incident['service_object'] = None + + return [Incident(**incident) for incident in response.get("results", [])] def get_incident_by_unique_id_or_incident_number(self, incident_id: str) -> Incident: """Return a Incident by its unique_id @@ -108,7 +122,7 @@ def get_incident_by_unique_id_or_incident_number(self, incident_id: str) -> Inci ) return Incident(**response) - def create_incident(self, title: str, service: UUID) -> Incident: + def create_incident(self, title: str, service: UUID, summary: str = None, escalation_policy: UUID = None, assigned_to: str = None, sla: str = None, team_priority: str = None) -> Incident: """Create a new incident Args: @@ -124,10 +138,21 @@ def create_incident(self, title: str, service: UUID) -> Incident: Returns: Incident: Incident object created """ + request_payload={"title": title, "service": (service)} + if summary: + request_payload["summary"] = summary + if assigned_to: + request_payload["assigned_to"] = assigned_to + if escalation_policy: + request_payload["escalation_policy"] = escalation_policy + if sla: + request_payload["sla"] = sla + if team_priority: + request_payload["team_priority"] = team_priority response = self._client.execute( method=ZendutyClientRequestMethod.POST, endpoint="/api/incidents/", - request_payload={"title": title, "service": (service)}, + request_payload=request_payload, success_code=201, ) return Incident(**response) diff --git a/zenduty/apiV2/incidents/models.py b/zenduty/apiV2/incidents/models.py index 8bb4eab..1fc6b65 100644 --- a/zenduty/apiV2/incidents/models.py +++ b/zenduty/apiV2/incidents/models.py @@ -1,8 +1,10 @@ +import json +import logging from uuid import UUID from datetime import datetime from typing import List, Any, Optional -from ..serializer import serialize, JsonSerializable -import json + +from zenduty.apiV2.serializer import serialize, JsonSerializable class IntegrationObject(JsonSerializable): @@ -27,6 +29,7 @@ def __init__( integration_key: str, is_enabled: bool, integration_type: int, + **kwargs ) -> None: self.name = name self.creation_date = ( @@ -41,6 +44,8 @@ def __init__( self.integration_key = integration_key self.is_enabled = is_enabled self.integration_type = integration_type + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') class IncidentAlert(JsonSerializable): @@ -73,6 +78,7 @@ def __init__( images: List[Any], urls: List[Any], notes: List[Any], + **kwargs ) -> None: self.integration_object = ( integration_object @@ -95,6 +101,8 @@ def __init__( self.images = images self.urls = urls self.notes = notes + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') class EscalationPolicyObject(JsonSerializable): @@ -102,10 +110,12 @@ class EscalationPolicyObject(JsonSerializable): name: str team: UUID - def __init__(self, unique_id: UUID, name: str, team: UUID) -> None: + def __init__(self, unique_id: UUID, name: str, team: UUID, **kwargs) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.name = name self.team = team if type(team) is not str else UUID(team) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -152,6 +162,7 @@ def __init__( under_maintenance: bool, team_name: Optional[str], acknowledgement_timeout_enabled: bool, + **kwargs ) -> None: self.name = name self.creation_date = ( @@ -176,6 +187,8 @@ def __init__( self.under_maintenance = under_maintenance self.team_name = team_name self.acknowledgement_timeout_enabled = acknowledgement_timeout_enabled + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') class SlaObject(JsonSerializable): @@ -194,6 +207,7 @@ def __init__( acknowledge_time: int, resolve_time: int, creation_date: datetime, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.name = name @@ -205,6 +219,8 @@ def __init__( if type(creation_date) is datetime else datetime.fromisoformat(creation_date.replace("Z", "+00:00")) ) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -216,11 +232,13 @@ class TeamPriorityObject(JsonSerializable): description: str color: str - def __init__(self, unique_id: UUID, name: str, description: str, color: str) -> None: + def __init__(self, unique_id: UUID, name: str, description: str, color: str, **kwargs) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.name = name self.description = description self.color = color + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -292,6 +310,7 @@ def __init__( postmortems: List[Any], postmortem_assignee: None, is_child_incident: bool = None, + **kwargs ) -> None: self.summary = summary self.incident_number = incident_number @@ -336,6 +355,8 @@ def __init__( self.postmortems = postmortems self.postmortem_assignee = postmortem_assignee self.is_child_incident = is_child_incident + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) diff --git a/zenduty/apiV2/incidents/notes/__init__.py b/zenduty/apiV2/incidents/notes/__init__.py index abb59f4..dffdf58 100644 --- a/zenduty/apiV2/incidents/notes/__init__.py +++ b/zenduty/apiV2/incidents/notes/__init__.py @@ -1,7 +1,6 @@ -import json -from ...client import ZendutyClient, ZendutyClientRequestMethod from ..models import Incident from .models import IncidentNote +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class __IncidentNoteItr__: diff --git a/zenduty/apiV2/incidents/notes/models.py b/zenduty/apiV2/incidents/notes/models.py index 0b95f40..fed9662 100644 --- a/zenduty/apiV2/incidents/notes/models.py +++ b/zenduty/apiV2/incidents/notes/models.py @@ -1,5 +1,5 @@ +import logging from datetime import datetime - from zenduty.apiV2.serializer import JsonSerializable @@ -19,6 +19,7 @@ def __init__( note: str, user_name: str, creation_date: datetime, + **kwargs ) -> None: self.unique_id = unique_id self.incident = incident @@ -30,3 +31,5 @@ def __init__( if type(creation_date) is datetime else datetime.fromisoformat(creation_date.replace("Z", "+00:00")) ) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') diff --git a/zenduty/apiV2/incidents/tags/__init__.py b/zenduty/apiV2/incidents/tags/__init__.py index 1594fe6..4b6bab6 100644 --- a/zenduty/apiV2/incidents/tags/__init__.py +++ b/zenduty/apiV2/incidents/tags/__init__.py @@ -1,9 +1,8 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod +from .models import Tag from ..models import Incident -from .models import Tag +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class IncidentTagClient: diff --git a/zenduty/apiV2/incidents/tags/models.py b/zenduty/apiV2/incidents/tags/models.py index 355af40..30ea4f4 100644 --- a/zenduty/apiV2/incidents/tags/models.py +++ b/zenduty/apiV2/incidents/tags/models.py @@ -1,5 +1,6 @@ -from datetime import datetime +import logging from uuid import UUID +from datetime import datetime from zenduty.apiV2.serializer import JsonSerializable @@ -21,6 +22,7 @@ def __init__( color: str, tag_id: UUID, creation_date: datetime, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.incident = incident @@ -33,3 +35,5 @@ def __init__( if type(creation_date) is datetime else datetime.fromisoformat(creation_date.replace("Z", "+00:00")) ) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') \ No newline at end of file diff --git a/zenduty/apiV2/serializer.py b/zenduty/apiV2/serializer.py index 1c6a265..9b86977 100644 --- a/zenduty/apiV2/serializer.py +++ b/zenduty/apiV2/serializer.py @@ -5,7 +5,7 @@ class JsonSerializable: def to_json(self): - return json.dumps(self, default=serialize, sort_keys=True, indent=4) + return json.loads(self, default=serialize, sort_keys=True, indent=4) def __repr__(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -15,9 +15,9 @@ def __str__(self): def serialize(o): - if type(o) is datetime: + if isinstance(o, datetime): return o.isoformat() - elif type(o) is UUID: + elif isinstance(o, UUID): return str(o) else: return o.__dict__ diff --git a/zenduty/apiV2/teams/__init__.py b/zenduty/apiV2/teams/__init__.py index 7ac1366..6d4ffea 100644 --- a/zenduty/apiV2/teams/__init__.py +++ b/zenduty/apiV2/teams/__init__.py @@ -1,18 +1,19 @@ -from .maintenance import TeamMaintenanceClient -from .oncall.models import OnCall -from .postmortem import PostmortemClient -from .priorities import PriorityClient -from .roles import IncidentRoleClient +from uuid import UUID + from .sla import SLAClient from .tags import TagClient -from .task_templates import TaskTemplateClient -from ..client import ZendutyClient, ZendutyClientRequestMethod -from ._models import Team, Member -from uuid import UUID +from .models import Team, Member +from .oncall.models import OnCall +from .services import ServiceClient +from .roles import IncidentRoleClient from .schedules import ScheduleClient +from .priorities import PriorityClient +from .postmortem import PostmortemClient +from .maintenance import TeamMaintenanceClient +from .task_templates import TaskTemplateClient from .escalation_policies import EscalationPolicyClient -from .services import ServiceClient +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class TeamsClient: def __init__(self, client: ZendutyClient): diff --git a/zenduty/apiV2/teams/escalation_policies/__init__.py b/zenduty/apiV2/teams/escalation_policies/__init__.py index d7d200c..fea3c27 100644 --- a/zenduty/apiV2/teams/escalation_policies/__init__.py +++ b/zenduty/apiV2/teams/escalation_policies/__init__.py @@ -1,8 +1,7 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team +from ..models import Team from .models import EscalationPolicy, Rule +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class EscalationPolicyClient: diff --git a/zenduty/apiV2/teams/escalation_policies/models.py b/zenduty/apiV2/teams/escalation_policies/models.py index e9de3c8..2ab3b67 100644 --- a/zenduty/apiV2/teams/escalation_policies/models.py +++ b/zenduty/apiV2/teams/escalation_policies/models.py @@ -1,16 +1,21 @@ -from typing import Union -from uuid import UUID import json -from ...serializer import serialize, JsonSerializable +import logging +from uuid import UUID +from typing import Union + +from zenduty.apiV2.serializer import serialize, JsonSerializable + class Target(JsonSerializable): target_type: int target_id: str - def __init__(self, target_type: int, target_id: str) -> None: + def __init__(self, target_type: int, target_id: str, **kwargs) -> None: self.target_type = target_type self.target_id = target_id + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -21,10 +26,12 @@ class AssignmentSettings(JsonSerializable): assignee_strategy: int assignment_index: int - def __init__(self, unique_id: Union[UUID, str], assignee_strategy: int, assignment_index: int) -> None: + def __init__(self, unique_id: Union[UUID, str], assignee_strategy: int, assignment_index: int, **kwargs) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.assignee_strategy = assignee_strategy self.assignment_index = assignment_index + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -42,11 +49,14 @@ def __init__( targets: Union[list[Target], list[dict]], position: int, unique_id: Union[UUID, str], + **kwargs ) -> None: self.delay = delay self.targets = targets if type(targets) is not list[dict] else [Target(**l) for l in targets] self.position = position self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -78,6 +88,7 @@ def __init__( global_ep: bool, connections: int, assignment_settings: AssignmentSettings, + **kwargs ) -> None: self.name = name self.summary = summary @@ -90,6 +101,8 @@ def __init__( self.global_ep = global_ep self.connections = connections self.assignment_settings = assignment_settings + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) diff --git a/zenduty/apiV2/teams/escalation_policies/rules.py b/zenduty/apiV2/teams/escalation_policies/rules.py index e412a38..353e337 100644 --- a/zenduty/apiV2/teams/escalation_policies/rules.py +++ b/zenduty/apiV2/teams/escalation_policies/rules.py @@ -1,5 +1,5 @@ -from datetime import datetime, timedelta from uuid import UUID +from datetime import timedelta class RuleBuilder: diff --git a/zenduty/apiV2/teams/escalation_policies/targets.py b/zenduty/apiV2/teams/escalation_policies/targets.py index e306fea..a71aa85 100644 --- a/zenduty/apiV2/teams/escalation_policies/targets.py +++ b/zenduty/apiV2/teams/escalation_policies/targets.py @@ -1,7 +1,3 @@ -from datetime import datetime, timedelta -from uuid import UUID - - class TargetBuilder: def __init__(self): self.targets = [] diff --git a/zenduty/apiV2/teams/maintenance/__init__.py b/zenduty/apiV2/teams/maintenance/__init__.py index 16bac9a..b73076c 100644 --- a/zenduty/apiV2/teams/maintenance/__init__.py +++ b/zenduty/apiV2/teams/maintenance/__init__.py @@ -2,7 +2,7 @@ from typing import Optional from uuid import UUID from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team +from ..models import Team from .models import TeamMaintenance from datetime import datetime diff --git a/zenduty/apiV2/teams/maintenance/models.py b/zenduty/apiV2/teams/maintenance/models.py index 4643973..a2500a0 100644 --- a/zenduty/apiV2/teams/maintenance/models.py +++ b/zenduty/apiV2/teams/maintenance/models.py @@ -1,16 +1,19 @@ +import logging from uuid import UUID from datetime import datetime from typing import List, Optional -from ...serializer import JsonSerializable +from zenduty.apiV2.serializer import JsonSerializable class Service(JsonSerializable): unique_id: UUID service: UUID - def __init__(self, unique_id: Optional[UUID], service: UUID) -> None: + def __init__(self, unique_id: Optional[UUID], service: UUID, **kwargs) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.service = service if type(service) is not str else UUID(service) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') class TeamMaintenance(JsonSerializable): @@ -35,6 +38,7 @@ def __init__( name: str, time_zone: str, repeat_until: Optional[int], + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.start_time = ( @@ -66,3 +70,5 @@ def __init__( if type(repeat_until) is datetime or repeat_until is None else datetime.fromisoformat(repeat_until.replace("Z", "+00:00")) ) + if kwargs: + logging.info(f'Received unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}') \ No newline at end of file diff --git a/zenduty/apiV2/teams/_models.py b/zenduty/apiV2/teams/models.py similarity index 82% rename from zenduty/apiV2/teams/_models.py rename to zenduty/apiV2/teams/models.py index 48e458b..6c85396 100644 --- a/zenduty/apiV2/teams/_models.py +++ b/zenduty/apiV2/teams/models.py @@ -1,9 +1,10 @@ +import json +import logging from uuid import UUID -from datetime import datetime from typing import Union -import json -from ..serializer import serialize, JsonSerializable +from datetime import datetime +from zenduty.apiV2.serializer import serialize, JsonSerializable class User(JsonSerializable): username: str @@ -12,12 +13,14 @@ class User(JsonSerializable): email: str def __init__( - self, username: str, first_name: str, last_name: str, email: str + self, username: str, first_name: str, last_name: str, email: str, **kwargs ) -> None: self.username = username self.first_name = first_name self.last_name = last_name self.email = email + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -37,6 +40,7 @@ def __init__( user: Union[User, dict], joining_date: Union[datetime, str], role: int, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.team = team if type(team) is not str else UUID(team) @@ -47,6 +51,8 @@ def __init__( else datetime.fromisoformat(joining_date.replace("Z", "+00:00")) ) self.role = role + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class Role(JsonSerializable): @@ -65,6 +71,7 @@ def __init__( description: str, creation_date: Union[datetime, str], rank: int, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is UUID else UUID(unique_id) self.team = team if type(team) is not str else UUID(team) @@ -76,6 +83,8 @@ def __init__( else datetime.fromisoformat(creation_date.replace("Z", "+00:00")) ) self.rank = rank + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class Team(JsonSerializable): @@ -99,6 +108,7 @@ def __init__( roles: Union[list[Role], list[dict]], private: bool, account_permissions: list[dict], + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.name = name @@ -114,3 +124,5 @@ def __init__( self.owner = owner self.roles = roles if type(roles) is list[Role] else [Role(**r) for r in roles] self.private = private + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/zenduty/apiV2/teams/oncall/__init__.py b/zenduty/apiV2/teams/oncall/__init__.py new file mode 100644 index 0000000..85f2463 --- /dev/null +++ b/zenduty/apiV2/teams/oncall/__init__.py @@ -0,0 +1,48 @@ + +from ..models import Team +from .models import OnCallV2, OnCall +from ..escalation_policies.models import EscalationPolicy +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod +class OncallClient: + def __init__(self, client: ZendutyClient, team: Team): + self._client = client + self._team = team + + + def get_all_oncall(self) -> list[OnCall]: + """Get all members on call for a team + + Returns: + list[OnCall]: List of OnCall objects. + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/account/teams/{str(self._team.unique_id)}/oncall/", + success_code=200, + ) + return [OnCall(**oncall) for oncall in response] + + def get_team_oncall_v2(self, escalation_policy: EscalationPolicy) -> OnCallV2: + """Get all members on call for a team + + Returns: + list[OnCall]: List of OnCall objects. + """ + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/v2/account/teams/{str(self._team.unique_id)}/escalation_policies/{str(escalation_policy.unique_id)}/oncall/", + ) + return OnCallV2(**response[0]) + + def list_team_oncall_v2(self)-> list[OnCallV2]: + """Get all members on call for a team + + Returns: + list[OnCall]: List of OnCall objects. + """ + + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint=f"/api/v2/account/teams/{str(self._team.unique_id)}/oncall/", + ) + return[ OnCallV2(**oncall) for oncall in response] diff --git a/zenduty/apiV2/teams/oncall/models.py b/zenduty/apiV2/teams/oncall/models.py index ca49002..c429ae1 100644 --- a/zenduty/apiV2/teams/oncall/models.py +++ b/zenduty/apiV2/teams/oncall/models.py @@ -1,8 +1,8 @@ +import logging +from uuid import UUID from typing import List - from zenduty.apiV2.serializer import JsonSerializable - class EscalationPolicy(JsonSerializable): name: str summary: str @@ -21,6 +21,7 @@ def __init__( repeat_policy: int, move_to_next: bool, global_ep: bool, + **kwargs ) -> None: self.name = name self.summary = summary @@ -29,15 +30,19 @@ def __init__( self.repeat_policy = repeat_policy self.move_to_next = move_to_next self.global_ep = global_ep + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class Team(JsonSerializable): unique_id: str name: str - def __init__(self, unique_id: str, name: str) -> None: + def __init__(self, unique_id: str, name: str, **kwargs) -> None: self.unique_id = unique_id self.name = name + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class User(JsonSerializable): @@ -47,12 +52,14 @@ class User(JsonSerializable): last_name: str def __init__( - self, username: str, first_name: str, email: str, last_name: str + self, username: str, first_name: str, email: str, last_name: str, **kwargs ) -> None: self.username = username self.first_name = first_name self.email = email self.last_name = last_name + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class OnCall(JsonSerializable): @@ -65,6 +72,7 @@ def __init__( escalation_policy: EscalationPolicy, team: Team, users: list[User], + **kwargs ) -> None: self.escalation_policy = ( escalation_policy @@ -75,3 +83,56 @@ def __init__( self.users = ( users if type(users) is list[User] else [User(**user) for user in users] ) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") + + +class OnCallV2(JsonSerializable): + class OnCallUsers(JsonSerializable): + ep_rule: UUID + position: int + delay: int + oncalls: List[User] + + def __init__( + self, + ep_rule: str, + position: int, + delay: int, + oncalls: list, + **kwargs + ) -> None: + self.ep_rule = ( + ep_rule + if isinstance(ep_rule, UUID) + else UUID(ep_rule) + ) + self.position = position + self.delay = delay + self.users = ( + oncalls if type(oncalls) is list[User] else [User(**user) for user in oncalls] + ) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") + + unique_id: UUID + name: str + oncalls: List[OnCallUsers] + def __init__( + self, + unique_id: str, + name: int, + oncalls: list, + **kwargs + ) -> None: + self.ep_unique_id = ( + unique_id + if isinstance(unique_id, UUID) + else UUID(unique_id) + ) + self.ep_name = name + self.oncalls = ( + oncalls if type(oncalls) is list[self.OnCallUsers] else [self.OnCallUsers(**user) for user in oncalls] + ) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") \ No newline at end of file diff --git a/zenduty/apiV2/teams/postmortem/__init__.py b/zenduty/apiV2/teams/postmortem/__init__.py index 01a6ebf..d4918b4 100644 --- a/zenduty/apiV2/teams/postmortem/__init__.py +++ b/zenduty/apiV2/teams/postmortem/__init__.py @@ -1,8 +1,7 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team +from ..models import Team from .models import Postmortem +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class PostmortemClient: diff --git a/zenduty/apiV2/teams/postmortem/models.py b/zenduty/apiV2/teams/postmortem/models.py index 3cb6d04..ee5a231 100644 --- a/zenduty/apiV2/teams/postmortem/models.py +++ b/zenduty/apiV2/teams/postmortem/models.py @@ -1,3 +1,4 @@ +import logging from uuid import UUID from typing import List, Optional, Union from datetime import datetime @@ -10,19 +11,23 @@ class IncidentIncident(JsonSerializable): title: str incident_number: int - def __init__(self, unique_id: str, title: str, incident_number: int) -> None: + def __init__(self, unique_id: str, title: str, incident_number: int, **kwargs) -> None: self.unique_id = unique_id self.title = title self.incident_number = incident_number + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class IncidentElement(JsonSerializable): unique_id: UUID incident: IncidentIncident - def __init__(self, unique_id: UUID, incident: Union[IncidentIncident, dict]) -> None: + def __init__(self, unique_id: UUID, incident: Union[IncidentIncident, dict], **kwargs) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.incident = incident if type(incident) is IncidentIncident else IncidentIncident(**incident) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class Postmortem(JsonSerializable): @@ -52,6 +57,7 @@ def __init__( amazon_link: str, creation_date: datetime, updated_at: datetime, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.author = author @@ -72,3 +78,5 @@ def __init__( self.updated_at = ( updated_at if type(updated_at) is datetime else datetime.fromisoformat(updated_at.replace("Z", "+00:00")) ) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/zenduty/apiV2/teams/priorities/__init__.py b/zenduty/apiV2/teams/priorities/__init__.py index 9e007d3..0dd5099 100644 --- a/zenduty/apiV2/teams/priorities/__init__.py +++ b/zenduty/apiV2/teams/priorities/__init__.py @@ -1,8 +1,8 @@ -import json + from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team +from ..models import Team from .models import Priority +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class PriorityClient: diff --git a/zenduty/apiV2/teams/priorities/models.py b/zenduty/apiV2/teams/priorities/models.py index 7fe3e7c..ff73e2c 100644 --- a/zenduty/apiV2/teams/priorities/models.py +++ b/zenduty/apiV2/teams/priorities/models.py @@ -1,3 +1,4 @@ +import logging from uuid import UUID from datetime import datetime @@ -20,6 +21,7 @@ def __init__( creation_date: datetime, color: str, team: int, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.name = name @@ -31,3 +33,5 @@ def __init__( ) self.color = color self.team = team + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/zenduty/apiV2/teams/roles/__init__.py b/zenduty/apiV2/teams/roles/__init__.py index 1b5fe24..eaeae28 100644 --- a/zenduty/apiV2/teams/roles/__init__.py +++ b/zenduty/apiV2/teams/roles/__init__.py @@ -1,9 +1,7 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team +from ..models import Team from .models import IncidentRole - +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class IncidentRoleClient: def __init__(self, client: ZendutyClient, team: Team): diff --git a/zenduty/apiV2/teams/roles/models.py b/zenduty/apiV2/teams/roles/models.py index 648188b..0c9fa51 100644 --- a/zenduty/apiV2/teams/roles/models.py +++ b/zenduty/apiV2/teams/roles/models.py @@ -1,8 +1,8 @@ +import logging from uuid import UUID from datetime import datetime from zenduty.apiV2.serializer import JsonSerializable - class IncidentRole(JsonSerializable): unique_id: UUID title: str @@ -19,6 +19,7 @@ def __init__( creation_date: datetime, rank: int, team: UUID = None, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.title = title @@ -30,3 +31,5 @@ def __init__( ) self.rank = rank self.team = team if type(team) is not str else UUID(team) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/zenduty/apiV2/teams/schedules/__init__.py b/zenduty/apiV2/teams/schedules/__init__.py index d6532ee..d54300e 100644 --- a/zenduty/apiV2/teams/schedules/__init__.py +++ b/zenduty/apiV2/teams/schedules/__init__.py @@ -1,8 +1,8 @@ -import json + from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team -from .models import Schedule +from ..models import Team +from .models import Schedule, Override +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class ScheduleClient: @@ -119,3 +119,25 @@ def delete_schedule(self, schedule: Schedule): endpoint="/api/account/teams/%s/schedules/%s/" % (str(self._team.unique_id), str(schedule.unique_id)), success_code=204, ) + + def list_overrides(self, schedule: Schedule) -> list[Override]: + response = self._client.execute( + method=ZendutyClientRequestMethod.GET, + endpoint="/api/v2/account/teams/{}/schedules/{}/overrides/" % str(self._team.unique_id) % str(schedule.unique_id), + ) + return [Override(**override) for override in response] + + def create_override(self, schedule: Schedule, override_name: str, user: str, start_time: str, end_time: str ) -> Override: + request_payload = { + "name": override_name, + "user": user, + "start_time": start_time, + "end_time": end_time, + } + response = self._client.execute( + method=ZendutyClientRequestMethod.POST, + endpoint="/api/v2/account/teams/{}/schedules/{}/overrides/" % str(self._team.unique_id) % str(schedule.unique_id), + request_payload=request_payload, + success_code=200, + ) + return Override(**response) diff --git a/zenduty/apiV2/teams/schedules/layers.py b/zenduty/apiV2/teams/schedules/layers.py index e29bdb2..d3fd564 100644 --- a/zenduty/apiV2/teams/schedules/layers.py +++ b/zenduty/apiV2/teams/schedules/layers.py @@ -1,7 +1,7 @@ -from .models import Layer, User, Restriction from datetime import datetime, timedelta +from .models import Layer, User, Restriction -from ...serializer import JsonSerializable +from zenduty.apiV2.serializer import JsonSerializable class LayersBuilder(JsonSerializable): diff --git a/zenduty/apiV2/teams/schedules/models.py b/zenduty/apiV2/teams/schedules/models.py index b4716ff..dbf3e56 100644 --- a/zenduty/apiV2/teams/schedules/models.py +++ b/zenduty/apiV2/teams/schedules/models.py @@ -1,8 +1,10 @@ -from datetime import datetime +import json +import logging from uuid import UUID from typing import Union -import json -from ...serializer import serialize, JsonSerializable +from datetime import datetime +from zenduty.apiV2.serializer import serialize, JsonSerializable + class Restriction(JsonSerializable): @@ -17,11 +19,14 @@ def __init__( start_day_of_week: int, start_time_of_day: str, unique_id: Union[UUID, str], + **kwargs ) -> None: self.duration = duration self.start_day_of_week = start_day_of_week self.start_time_of_day = start_time_of_day self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -31,9 +36,11 @@ class User(JsonSerializable): user: str position: int - def __init__(self, user: str, position: int, unique_id: Union[UUID, str]) -> None: + def __init__(self, user: str, position: int, unique_id: Union[UUID, str], **kwargs) -> None: self.user = user self.position = position + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -63,6 +70,7 @@ def __init__( last_edited: Union[str, datetime, None], restriction_type: int, is_active: bool, + **kwargs ) -> None: self.shift_length = shift_length self.restrictions = ( @@ -88,6 +96,8 @@ def __init__( ) self.restriction_type = restriction_type self.is_active = is_active + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -107,6 +117,7 @@ def __init__( start_time: Union[str, datetime], end_time: Union[str, datetime], unique_id: Union[UUID, str], + **kwargs ) -> None: self.name = name self.user = user @@ -117,6 +128,8 @@ def __init__( end_time if type(end_time) is datetime else datetime.fromisoformat(end_time.replace("Z", "+00:00")) ) self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -146,6 +159,7 @@ def __init__( unique_id: Union[UUID, str], connections: int, shift_time_in_dst: bool, + **kwargs ) -> None: self.name = name self.summary = summary @@ -157,6 +171,8 @@ def __init__( self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.connections = connections self.shift_time_in_dst = shift_time_in_dst + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) diff --git a/zenduty/apiV2/teams/schedules/overrides.py b/zenduty/apiV2/teams/schedules/overrides.py index ca036d2..5ae5e11 100644 --- a/zenduty/apiV2/teams/schedules/overrides.py +++ b/zenduty/apiV2/teams/schedules/overrides.py @@ -1,6 +1,5 @@ from datetime import datetime - class OverrideBuilder: def __init__(self): self.override = [] diff --git a/zenduty/apiV2/teams/services/__init__.py b/zenduty/apiV2/teams/services/__init__.py index a8da4d9..c9f4ed7 100644 --- a/zenduty/apiV2/teams/services/__init__.py +++ b/zenduty/apiV2/teams/services/__init__.py @@ -1,10 +1,9 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team + +from ..models import Team from .models import Service -from datetime import datetime from .integrations import IntegrationClient +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class ServiceClient: diff --git a/zenduty/apiV2/teams/services/integrations/__init__.py b/zenduty/apiV2/teams/services/integrations/__init__.py index c0b74e9..814901c 100644 --- a/zenduty/apiV2/teams/services/integrations/__init__.py +++ b/zenduty/apiV2/teams/services/integrations/__init__.py @@ -1,8 +1,8 @@ from uuid import UUID -from ....client import ZendutyClient, ZendutyClientRequestMethod -from ..._models import Team +from ...models import Team from ..models import Service from .models import Integration +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class IntegrationClient: diff --git a/zenduty/apiV2/teams/services/integrations/models.py b/zenduty/apiV2/teams/services/integrations/models.py index 1d8a7fd..c18a31e 100644 --- a/zenduty/apiV2/teams/services/integrations/models.py +++ b/zenduty/apiV2/teams/services/integrations/models.py @@ -1,13 +1,11 @@ -from uuid import UUID -from datetime import datetime import json -from typing import Optional -from ....serializer import serialize, JsonSerializable - -from datetime import datetime +import logging from uuid import UUID +from datetime import datetime from typing import Optional, List, Any +from zenduty.apiV2.serializer import serialize, JsonSerializable + class IntegrationObject: name: str @@ -31,6 +29,7 @@ def __init__( integration_key: UUID, is_enabled: bool, integration_type: int, + **kwargs ) -> None: self.name = name self.creation_date = creation_date @@ -45,6 +44,8 @@ def __init__( ) self.is_enabled = is_enabled self.integration_type = integration_type + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class IntegrationAlert: @@ -77,6 +78,7 @@ def __init__( images: List[Any], urls: List[Any], notes: List[Any], + **kwargs ) -> None: self.integration_object = integration_object self.summary = summary @@ -93,6 +95,8 @@ def __init__( self.images = images self.urls = urls self.notes = notes + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") class ApplicationReference: @@ -121,6 +125,7 @@ def __init__( application_type: int, categories: str, documentation_link: str, + **kwargs ) -> None: self.name = name self.icon_url = icon_url @@ -133,6 +138,8 @@ def __init__( self.application_type = application_type self.categories = categories self.documentation_link = documentation_link + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) @@ -201,6 +208,8 @@ def __init__( self.webhook_url = webhook_url if kwargs.get("auto_resolve_timeout", None) is not None: self.auto_resolve_timeout = kwargs.get("auto_resolve_timeout") + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) diff --git a/zenduty/apiV2/teams/services/models.py b/zenduty/apiV2/teams/services/models.py index 85f1159..99e0b7c 100644 --- a/zenduty/apiV2/teams/services/models.py +++ b/zenduty/apiV2/teams/services/models.py @@ -1,7 +1,10 @@ -from datetime import datetime -from uuid import UUID import json -from ...serializer import serialize, JsonSerializable +import logging +from uuid import UUID +from datetime import datetime + +from zenduty.apiV2.serializer import serialize, JsonSerializable + class Service(JsonSerializable): @@ -75,6 +78,8 @@ def __init__( self.collation_time = collation_time if kwargs.get("team_name", None) is not None: self.team_name = kwargs.get("team_name", None) + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") def to_json(self): return json.dumps(self, default=serialize, sort_keys=True, indent=4) diff --git a/zenduty/apiV2/teams/sla/__init__.py b/zenduty/apiV2/teams/sla/__init__.py index c5f80b1..51e56bd 100644 --- a/zenduty/apiV2/teams/sla/__init__.py +++ b/zenduty/apiV2/teams/sla/__init__.py @@ -1,8 +1,8 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team + from .models import SLA +from ..models import Team +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class SLAClient: diff --git a/zenduty/apiV2/teams/sla/models.py b/zenduty/apiV2/teams/sla/models.py index 3befa25..addbfac 100644 --- a/zenduty/apiV2/teams/sla/models.py +++ b/zenduty/apiV2/teams/sla/models.py @@ -1,6 +1,7 @@ +import logging +from uuid import UUID from datetime import datetime from typing import List, Any, Optional -from uuid import UUID from zenduty.apiV2.serializer import JsonSerializable @@ -31,6 +32,7 @@ def __init__( team: Optional[int] = None, summary: Optional[str] = "", time_zone: Optional[str] = None, + **kwargs ) -> None: self.escalations = escalations self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) @@ -47,3 +49,5 @@ def __init__( else datetime.fromisoformat(creation_date.replace("Z", "+00:00")) ) self.summary = summary + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/zenduty/apiV2/teams/tags/__init__.py b/zenduty/apiV2/teams/tags/__init__.py index 2d1aaff..edf983b 100644 --- a/zenduty/apiV2/teams/tags/__init__.py +++ b/zenduty/apiV2/teams/tags/__init__.py @@ -1,8 +1,8 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team + from .models import Tag +from ..models import Team +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class TagClient: diff --git a/zenduty/apiV2/teams/tags/models.py b/zenduty/apiV2/teams/tags/models.py index e399ce6..6fc2fb7 100644 --- a/zenduty/apiV2/teams/tags/models.py +++ b/zenduty/apiV2/teams/tags/models.py @@ -1,3 +1,4 @@ +import logging from uuid import UUID from datetime import datetime @@ -18,6 +19,7 @@ def __init__( name: str, creation_date: datetime, color: str, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.team = team if type(team) is not str else UUID(team) @@ -28,3 +30,5 @@ def __init__( else datetime.fromisoformat(creation_date.replace("Z", "+00:00")) ) self.color = color + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/zenduty/apiV2/teams/task_templates/__init__.py b/zenduty/apiV2/teams/task_templates/__init__.py index e3f27aa..74be17a 100644 --- a/zenduty/apiV2/teams/task_templates/__init__.py +++ b/zenduty/apiV2/teams/task_templates/__init__.py @@ -1,8 +1,8 @@ -import json from uuid import UUID -from ...client import ZendutyClient, ZendutyClientRequestMethod -from .._models import Team + +from ..models import Team from .models import TaskTemplate +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class TaskTemplateClient: diff --git a/zenduty/apiV2/teams/task_templates/models.py b/zenduty/apiV2/teams/task_templates/models.py index 5bb826e..5c290b1 100644 --- a/zenduty/apiV2/teams/task_templates/models.py +++ b/zenduty/apiV2/teams/task_templates/models.py @@ -1,9 +1,9 @@ +import logging from uuid import UUID from datetime import datetime from zenduty.apiV2.serializer import JsonSerializable - class TaskTemplate(JsonSerializable): unique_id: UUID team: UUID @@ -20,6 +20,7 @@ def __init__( creation_date: datetime, summary: str, due_immediately: int, + **kwargs ) -> None: self.unique_id = unique_id if type(unique_id) is not str else UUID(unique_id) self.team = team if type(team) is not str else UUID(team) @@ -31,3 +32,5 @@ def __init__( ) self.summary = summary self.due_immediately = due_immediately + if kwargs: + logging.info(f"We have unexpected return values for {self.__class__.__name__}: {list(kwargs.keys())}") diff --git a/setup.py b/zenduty/setup.py similarity index 94% rename from setup.py rename to zenduty/setup.py index ceab627..f76153a 100644 --- a/setup.py +++ b/zenduty/setup.py @@ -17,5 +17,4 @@ "idna==3.7", "certifi==2024.7.4" ], - scripts=["bin/client.py"], ) \ No newline at end of file