From 13d162223423d5bea41e15ac0e47d148480e03bc Mon Sep 17 00:00:00 2001 From: Anudeep Kalitkar <50821904+anudeepkalitkar@users.noreply.github.com> Date: Tue, 25 Mar 2025 12:03:47 -0400 Subject: [PATCH 1/4] V2 Changes to handle errors --- README.md | 3 +- bin/client.py | 11 -- examples/example_3.py | 67 ++++++++ zenduty/__init__.py | 19 +- zenduty/api/__init__.py | 11 -- zenduty/api/escalationpolicies_api.py | 69 -------- zenduty/api/events_api.py | 25 --- zenduty/api/incidents_api.py | 69 -------- zenduty/api/integrations_api.py | 60 ------- zenduty/api/members_api.py | 27 --- zenduty/api/schedules_api.py | 68 -------- zenduty/api/services_api.py | 69 -------- zenduty/api/teams_api.py | 32 ---- zenduty/apiV2/__init__.py | 55 ++++++ zenduty/apiV2/_logging.py | 10 -- zenduty/apiV2/accounts/members/__init__.py | 4 +- zenduty/apiV2/accounts/members/models.py | 10 +- .../apiV2/accounts/notifications/__init__.py | 147 ++++++++++++++++ .../apiV2/accounts/notifications/models.py | 59 +++++++ zenduty/apiV2/accounts/roles/__init__.py | 3 +- zenduty/apiV2/accounts/roles/models.py | 5 +- zenduty/apiV2/authentication/__init__.py | 0 .../authentication/zenduty_credential.py | 14 -- zenduty/apiV2/client.py | 162 +++++------------- zenduty/apiV2/events/__init__.py | 4 +- zenduty/apiV2/events/models.py | 20 ++- zenduty/apiV2/events/router/__init__.py | 3 +- zenduty/apiV2/events/router/models.py | 5 + zenduty/apiV2/exceptions.py | 13 ++ zenduty/apiV2/incidents/__init__.py | 14 +- zenduty/apiV2/incidents/models.py | 29 +++- zenduty/apiV2/incidents/notes/__init__.py | 3 +- zenduty/apiV2/incidents/notes/models.py | 5 +- zenduty/apiV2/incidents/tags/__init__.py | 5 +- zenduty/apiV2/incidents/tags/models.py | 6 +- zenduty/apiV2/serializer.py | 6 +- zenduty/apiV2/teams/__init__.py | 21 +-- .../teams/escalation_policies/__init__.py | 5 +- .../apiV2/teams/escalation_policies/models.py | 23 ++- .../apiV2/teams/escalation_policies/rules.py | 2 +- .../teams/escalation_policies/targets.py | 4 - zenduty/apiV2/teams/maintenance/__init__.py | 2 +- zenduty/apiV2/teams/maintenance/models.py | 10 +- zenduty/apiV2/teams/{_models.py => models.py} | 20 ++- zenduty/apiV2/teams/oncall/__init__.py | 48 ++++++ zenduty/apiV2/teams/oncall/models.py | 69 +++++++- zenduty/apiV2/teams/postmortem/__init__.py | 5 +- zenduty/apiV2/teams/postmortem/models.py | 12 +- zenduty/apiV2/teams/priorities/__init__.py | 6 +- zenduty/apiV2/teams/priorities/models.py | 4 + zenduty/apiV2/teams/roles/__init__.py | 6 +- zenduty/apiV2/teams/roles/models.py | 5 +- zenduty/apiV2/teams/schedules/__init__.py | 6 +- zenduty/apiV2/teams/schedules/layers.py | 4 +- zenduty/apiV2/teams/schedules/models.py | 24 ++- zenduty/apiV2/teams/schedules/overrides.py | 1 - zenduty/apiV2/teams/services/__init__.py | 7 +- .../teams/services/integrations/__init__.py | 4 +- .../teams/services/integrations/models.py | 21 ++- zenduty/apiV2/teams/services/models.py | 11 +- zenduty/apiV2/teams/sla/__init__.py | 6 +- zenduty/apiV2/teams/sla/models.py | 6 +- zenduty/apiV2/teams/tags/__init__.py | 6 +- zenduty/apiV2/teams/tags/models.py | 4 + .../apiV2/teams/task_templates/__init__.py | 6 +- zenduty/apiV2/teams/task_templates/models.py | 5 +- zenduty/api_client.py | 15 -- zenduty/configuration.py | 3 - zenduty/exceptions.py | 104 ----------- zenduty/rest.py | 31 ---- setup.py => zenduty/setup.py | 15 +- 71 files changed, 753 insertions(+), 880 deletions(-) delete mode 100644 bin/client.py create mode 100644 examples/example_3.py delete mode 100644 zenduty/api/__init__.py delete mode 100644 zenduty/api/escalationpolicies_api.py delete mode 100644 zenduty/api/events_api.py delete mode 100644 zenduty/api/incidents_api.py delete mode 100644 zenduty/api/integrations_api.py delete mode 100644 zenduty/api/members_api.py delete mode 100644 zenduty/api/schedules_api.py delete mode 100644 zenduty/api/services_api.py delete mode 100644 zenduty/api/teams_api.py delete mode 100644 zenduty/apiV2/_logging.py create mode 100644 zenduty/apiV2/accounts/notifications/__init__.py create mode 100644 zenduty/apiV2/accounts/notifications/models.py delete mode 100644 zenduty/apiV2/authentication/__init__.py delete mode 100644 zenduty/apiV2/authentication/zenduty_credential.py create mode 100644 zenduty/apiV2/exceptions.py rename zenduty/apiV2/teams/{_models.py => models.py} (82%) create mode 100644 zenduty/apiV2/teams/oncall/__init__.py delete mode 100644 zenduty/api_client.py delete mode 100644 zenduty/configuration.py delete mode 100644 zenduty/exceptions.py delete mode 100644 zenduty/rest.py rename setup.py => zenduty/setup.py (55%) diff --git a/README.md b/README.md index 5c0ef2d..7f222a8 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,8 @@ $ git clone https://github.com/Zenduty/zenduty-python-sdk $ python3 setup.py install ``` ## Contents -1) zenduty/api : contains the functions to communicate with zenduty API endpoints +1) zenduty/apiV2 : 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 ## 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_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..93657be 100644 --- a/zenduty/__init__.py +++ b/zenduty/__init__.py @@ -1,20 +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 -from .api.integrations_api import IntegrationsApi -from .api.members_api import MembersApi -from .api.services_api import ServicesApi -from .api.teams_api import TeamsApi -from .api.events_api import EventsApi - -# import ApiClient -from .api_client import ApiClient -from .configuration import Configuration -from .exceptions import OpenApiException -from .exceptions import ApiTypeError -from .exceptions import ApiValueError -from .exceptions import ApiKeyError -from .exceptions import ApiException +from .apiV2 import * +from .apiV2.serializer import JsonSerializable, serialize \ No newline at end of file diff --git a/zenduty/api/__init__.py b/zenduty/api/__init__.py deleted file mode 100644 index af716c4..0000000 --- a/zenduty/api/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import absolute_import - -# flake8: noqa - -# import apis into api package -from .incidents_api import IncidentsApi -from .integrations_api import IntegrationsApi -from .members_api import MembersApi -from .services_api import ServicesApi -from .teams_api import TeamsApi -from .events_api import EventsApi diff --git a/zenduty/api/escalationpolicies_api.py b/zenduty/api/escalationpolicies_api.py deleted file mode 100644 index 9245682..0000000 --- a/zenduty/api/escalationpolicies_api.py +++ /dev/null @@ -1,69 +0,0 @@ -from zenduty.api_client import ApiClient - - -class EscalationPoliciesApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def get_escalation_policies(self, team_id): - # Returns the escalation policies belonging to one team - # params str team_id: unique id of team - return self.api_client.call_api( - "GET", "/api/account/teams/{}/escalation_policies/".format(team_id) - ) - - def create_escalation_policy(self, team_id, body): - # Creates an escalation policy for one team - # params str team_id: unique id of team - # params dict body: contains the required details for creating escalation policy - # Sample body: - # {'name':name, - # 'summary':summary, - # 'description':description, - # 'rules':rules, - # 'unique_id':unique_id, - # 'team':team_id} - return self.api_client.call_api( - "POST", - "/api/account/teams/{}/escalation_policies/".format(team_id), - body=body, - ) - - def get_escalation_policy_by_id(self, team_id, ep_id): - # Returns escalation_policy identified by id - # params str team_id: unique id of team - # params str ep_id: unique id of escalation policy - return self.api_client.call_api( - "GET", - "/api/account/teams/{}/escalation_policies/{}/".format(team_id, ep_id), - ) - - def update_escalation_policy(self, team_id, ep_id, body): - # Updates escalation policy, identified by id - # params str team_id: unique id of team - # params str ep_id: unqiue id of escalation policy - # params dict body: contains all the updated values - # 'rules' is a required part of the body - # Sample body: - # body={'summary':'changes description', - # 'rules':[{"delay":1, - # "targets":[{"target_type":2, - # "target_id":"826032d6-7ccd-4d58-b114-f"}], - # "position":1, - # "unique_id":"c0dad09b-321b-491e-9c23-f816c7bd0339"}]} - return self.api_client.call_api( - "PATCH", - "/api/account/teams/{}/escalation_policies/{}/".format(team_id, ep_id), - body=body, - ) - - def delete_escalation_policy(self, team_id, ep_id): - # Deletes escalation policy, identified by id - # params str team_id: unique id of team - # params str ep_id: unique id of escalation policy - return self.api_client.call_api( - "DELETE", - "/api/account/teams/{}/escalation_policies/{}/".format(team_id, ep_id), - ) diff --git a/zenduty/api/events_api.py b/zenduty/api/events_api.py deleted file mode 100644 index 328d8b7..0000000 --- a/zenduty/api/events_api.py +++ /dev/null @@ -1,25 +0,0 @@ -from zenduty.api_client import ApiClient - - -class EventsApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def create_event(self, integration_key, body): - # Creates an incident event on zenduty - # params str integration_key: unique key provided for your integration - # params dict body: contains the details of the event - # 'message', 'summary' are required fields of the body - # 'alert_type' is "info" by default - # 'suppressed' is false by default - # if no entity_id is provided, Zenduty provides one automatically - # Sample body: - # {'message':message, - # 'summary':summary, - # 'alert_type':alert_type, - # 'supressed':supressed} - return self.api_client.call_api( - "POST", "/api/events/{}/".format(integration_key), body=body - ) diff --git a/zenduty/api/incidents_api.py b/zenduty/api/incidents_api.py deleted file mode 100644 index b2deb5b..0000000 --- a/zenduty/api/incidents_api.py +++ /dev/null @@ -1,69 +0,0 @@ -from zenduty.api_client import ApiClient - - -class IncidentsApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def get_incidents(self, body): - # Returns the incidents from your zenduty account - # params dict body: contains all the required details of your account - # Sample body: - # {'page':1, - # 'status':5, - # 'team_id':['a2c6322b-4c1b-4884-8f7a-a7f270de98cb'], - # 'service_ids':[], - # 'user_ids':[]} - return self.api_client.call_api("GET", "/api/incidents/", body=body) - - def get_incidents_by_number(self, incident_number): - # Returns the incidents belonging to a given incident number - # params int incident_number: incident number of event - return self.api_client.call_api( - "GET", "/api/incidents/{}/".format(incident_number) - ) - - def get_incident_alerts(self, incident_number): - # Returns all alerts of a particular incident - # params int incident_number: incident number of event - return self.api_client.call_api( - "GET", "/api/incidents/{}/alerts/".format(incident_number) - ) - - def get_incident_notes(self, incident_number): - # Gets the notes regarding an incident, identified by incident number - # params int incident_number: incident number of event - return self.api_client.call_api( - "GET", "/api/incidents/{}/note/".format(incident_number) - ) - - def acknowledge_or_resolve_incidents(self, incident_number, body): - # Used to acknowledge or resolve incident, identified by incident number - # params str incident_number: incident number of event - # params dict body: contains the changed values of incident - # Sample body: - # {'status':3, - # 'incident_number':12} - return self.api_client.call_api( - "PATCH", "/api/incidents/{}/".format(incident_number), body=body - ) - - def create_incident(self, body): - # Used to create an incident for a particular service, identified by id - # params dict body: contains necessary details for creating incident - # Sample body: - # {"service":"c7fff4c5-2def-41e8-9120-c63f649a825c", - # "escalation_policy":"a70244c8-e343-4dd0-8d87-2f767115568a", - # "user":null, - # "title":"Name of trial", - # "summary":"summary of trial"} - # escalation_policy,service, title and summary are required fields. - # if escalation_policy is not set (set to None then), then assigned_to is required, as follows - # {"service":"b1559a26-c51f-45a1-886d-f6caeaf0fc7e", - # "escalation_policy":null, - # "assigned_to":"826032d6-7ccd-4d58-b114-f", - # "title":"Name of trial", - # "summary":"Summary of trial"} - return self.api_client.call_api("POST", "/api/incidents/", body=body) diff --git a/zenduty/api/integrations_api.py b/zenduty/api/integrations_api.py deleted file mode 100644 index 55fff37..0000000 --- a/zenduty/api/integrations_api.py +++ /dev/null @@ -1,60 +0,0 @@ -from zenduty.api_client import ApiClient - - -class IntegrationsApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def get_integrations_in_service(self, team_id, service_id): - # Returns the integrations in a service - # params str team_id: unique id of team - # params str service_id: unique id of service - return self.api_client.call_api( - "GET", - "/api/account/teams/{}/services/{}/integrations/".format( - team_id, service_id - ), - ) - - def create_integration(self, team_id, service_id, body): - # Creates a new integration for a given service in a team - # params str team_id: unique id of team - # params str service_id: unique id of service - # params dict body: contains the details of the new integration - # Sample body: - # {"name":"asdf", - # "summary":"asdf", - # "application":"27c9800c-2856-490d-8119-790be1308dd4"} - return self.api_client.call_api( - "POST", - "/api/account/teams/{}/services/{}/integrations/".format( - team_id, service_id - ), - body=body, - ) - - def get_integrations_by_id(self, team_id, service_id, integration_id): - # Returns an integration belonging to a service in a team, identified by id - # params str team_id: unique id of team - # params str service_id: unique id of service - # params str integration_id: unique id of integration - return self.api_client.call_api( - "GET", - "/api/account/teams/{}/services/{}/integrations/{}/".format( - team_id, service_id, integration_id - ), - ) - - def get_alerts_in_integration(self, team_id, service_id, integration_id): - # Retruns alerts in a particular integration - # params str team_id: unique id of team - # params str service_id: unique id of service - # params str integration_id: unique id of integration - return self.api_client.call_api( - "GET", - "/api/account/teams/{}/services/{}/integrations/{}/alerts/".format( - team_id, service_id, integration_id - ), - ) diff --git a/zenduty/api/members_api.py b/zenduty/api/members_api.py deleted file mode 100644 index d888ab5..0000000 --- a/zenduty/api/members_api.py +++ /dev/null @@ -1,27 +0,0 @@ -from zenduty.api_client import ApiClient - - -class MembersApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def add_members_to_team(self, team_id, body): - # Adds a member to a given team, identified by id - # params str team_id: unique id of team - # params dict body: contains the details of the user being added and the team to add to - # Sample body: - # {"team":"d4a777db-5bce-419c-a725-420ebb505c54","user":"af9eeb60-5acb-406c-971e-3"} - return self.api_client.call_api( - "POST", "/api/account/teams/{}/members/".format(team_id), body=body - ) - - def delete_members_from_team(self, team_id, member_id): - # Removes a member from a particular team - # params str team_id: unique id of a team - # params str member_id: unique id of member to be deleted - return self.api_client.call_api( - "DELETE", - "/api/account/teams/{}/members/{}/".format(team_id, member_id), - ) diff --git a/zenduty/api/schedules_api.py b/zenduty/api/schedules_api.py deleted file mode 100644 index 6300485..0000000 --- a/zenduty/api/schedules_api.py +++ /dev/null @@ -1,68 +0,0 @@ -from zenduty.api_client import ApiClient - - -class SchedulesApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def get_schedules(self, team_id): - # Returns the schedules in a particular team, identified by id - # params str team_id: unique id of a team - return self.api_client.call_api( - "GET", "/api/account/teams/{}/schedules/".format(team_id) - ) - - def create_schedule(self, team_id, body): - # Creates a schedule for a team - # params str team_id: unique id of team - # params dict body: contains the details of the schedule to be created - # Sample body: - # {"name":"Name of schedule", - # "summary":"summar of schedule", - # "time_zone":"Asia/Kolkata", - # "team":"d4a777db-5bce-419c-a725-420ebb505c54", - # "layers":[]} - return self.api_client.call_api( - "POST", - "/api/account/teams/{}/schedules/".format(team_id), - body=body, - ) - - def get_schedule_by_id(self, team_id, schedule_id): - # Returns a particular schedule from a team, identifed by id - # params str team_id: unique id of a team - # params schedule_id: unique id of schedule - return self.api_client.call_api( - "GET", - "/api/account/teams/{}/schedules/{}/".format(team_id, schedule_id), - ) - - def update_schedule(self, team_id, schedule_id, body): - # Updates the schedule details for a given team, identified by id - # params str team_id: unique id of a team - # params str schedul_id: unique id of schedule - # params dict body: contains the updated values of schedule - # 'unique_id' and 'team' are required. Other fields are just those which have been changed - # Sample body: - # {"name":"Name of schedule", - # "summary":"summar of schedule", - # "time_zone":"Asia/Kamchatka", - # "team":"d4a777db-5bce-419c-a725-420ebb505c54", - # "unique_id":"f9b34bd3-818a-4b98-9d8a-04d8bd501cd0", - # "layers":[]} - return self.api_client.call_api( - "PATCH", - "/api/account/teams/{}/schedules/{}/".format(team_id, schedule_id), - body=body, - ) - - def delete_schedule(self, team_id, schedule_id): - # Deletes a schedule from a team - # params str team_id:unique id of team - # params str schedule_id: unique id of schedule - return self.api_client.call_api( - "DELETE", - "/api/account/teams/{}/schedules/{}/".format(team_id, schedule_id), - ) diff --git a/zenduty/api/services_api.py b/zenduty/api/services_api.py deleted file mode 100644 index fcc4d09..0000000 --- a/zenduty/api/services_api.py +++ /dev/null @@ -1,69 +0,0 @@ -from zenduty.api_client import ApiClient - - -class ServicesApi(object): - def __init__(self, api_client=None): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def get_service_for_team(self, team_id): - # Returns all the services in a team - # params str team_id: unnique id of team - return self.api_client.call_api( - "GET", "/api/account/teams/{}/services/".format(team_id) - ) - - def add_new_service_in_team(self, team_id, body): - # Adds a new servie to a give team, identified by id - # params str team_id: unique id of team - # params dict body: contains the details of the new service to be added - # Sample body - # {"name":"Name of service", - # "description":"Description of service", - # "integrations":[{"application":"27c9800c-2856-490d-8119-790be1308dd4", - # "name":"API", - # "summary":"Edit summary for this integration"}], - # "escalation_policy":"5c9b6288-c105-418d-970b-91a93d0e919a", - # "acknowledgement_timeout":1, - # "auto_resolve_timeout":1} - return self.api_client.call_api( - "POST", - "/api/account/teams/{}/services/".format(team_id), - body=body, - ) - - def get_services_by_id(self, team_id, service_id): - # Returns a particular service from a team, identified by id - # params str team_id: unique id of team - # params str service_id: unique id of service - return self.api_client.call_api( - "GET", - "/api/account/teams/{}/services/{}/".format(team_id, service_id), - ) - - def update_service(self, team_id, service_id, body): - # Updates the existing service in a team - # params str team_id: unique id of team - # params str service_id: unique id of service - # params dict body: contains the updated details of services - # Sample body: - # {"unique_id":"bc808ce3-46c0-41d0-bf1f-f405fdd0c1c3", - # "auto_resolve_timeout":0, - # "acknowledgement_timeout":0, - # "status":1, - # "escalation_policy":"5c9b6288-c105-418d-970b-91a93d0e919a"} - return self.api_client.call_api( - "PATCH", - "/api/account/teams/{}/services/{}/".format(team_id, service_id), - body=body, - ) - - def delete_service_from_team(self, team_id, service_id): - # Deletes a particular service from a team - # params str team_id: unique id of team - # params str service_id: unnique id of service - return self.api_client.call_api( - "DELETE", - "/api/account/teams/{}/services/{}/".format(team_id, service_id), - ) diff --git a/zenduty/api/teams_api.py b/zenduty/api/teams_api.py deleted file mode 100644 index 30e2ad4..0000000 --- a/zenduty/api/teams_api.py +++ /dev/null @@ -1,32 +0,0 @@ -from zenduty.api_client import ApiClient - - -class TeamsApi(object): - def __init__(self, api_client): - if api_client is None: - api_client = ApiClient() - self.api_client = api_client - - def get_teams(self): - # Returns all the teams and their details from your Zenduty account - return self.api_client.call_api("GET", "/api/account/teams/") - - def create_team(self, body): - # Creates a new team for your zenduty account - # params dict body: contains the details for your new team - # Sample body: - # 'name' is a required field - # {'name':name} - return self.api_client.call_api("POST", "/api/account/teams/", body) - - def get_team_details(self, team_id): - # Returns a team form your zenduty acocunt, identified by id - # params str team_id: unique id of team - return self.api_client.call_api("GET", "/api/account/teams/{}/".format(team_id)) - - def delete_team(self, team_id): - # Deletes a team form your zenduty account - # params str team_id: unique id of team - return self.api_client.call_api( - "DELETE", "/api/account/teams/{}/".format(team_id) - ) 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/authentication/__init__.py b/zenduty/apiV2/authentication/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/zenduty/apiV2/authentication/zenduty_credential.py b/zenduty/apiV2/authentication/zenduty_credential.py deleted file mode 100644 index 11a6707..0000000 --- a/zenduty/apiV2/authentication/zenduty_credential.py +++ /dev/null @@ -1,14 +0,0 @@ -import os - - -class ZendutyCredential: - _api_key: str - - def __init__(self, api_key: str = os.environ.get("ZENDUTY_API_KEY")): - if api_key == "": - raise ValueError("error: api_key length must be greater than 0") - else: - self._api_key = api_key - - def get_api_key(self) -> str: - return self._api_key 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..44b9d65 100644 --- a/zenduty/apiV2/incidents/__init__.py +++ b/zenduty/apiV2/incidents/__init__.py @@ -1,9 +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: @@ -90,7 +90,11 @@ def get_all_incidents( request_payload=payload, success_code=200, ) - return response.get("results", []) + incidents = [] + for incident in response.get('results',[]): + incidents.append(self.get_incident_by_unique_id_or_incident_number(incident['incident_number'])) + + return incidents def get_incident_by_unique_id_or_incident_number(self, incident_id: str) -> Incident: """Return a Incident by its unique_id 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..303d92c 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 Team from .models import Schedule +from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod class ScheduleClient: 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/zenduty/api_client.py b/zenduty/api_client.py deleted file mode 100644 index 0610638..0000000 --- a/zenduty/api_client.py +++ /dev/null @@ -1,15 +0,0 @@ -from .rest import RESTClientObject -from .configuration import Configuration - - -class ApiClient: - def __init__(self, access_token): - self.configuration = Configuration(access_token) - self.rest_client = RESTClientObject() - - def call_api(self, method, url, body={}, headers={}): - # building the header - url = "https://www.zenduty.com" + url - headers["Authorization"] = "Token " + self.configuration.access_token - # making the request through RESTClientObject - return self.rest_client.request(method, url, body, headers) diff --git a/zenduty/configuration.py b/zenduty/configuration.py deleted file mode 100644 index 2a9b05e..0000000 --- a/zenduty/configuration.py +++ /dev/null @@ -1,3 +0,0 @@ -class Configuration(object): - def __init__(self, access_token): - self.access_token = access_token diff --git a/zenduty/exceptions.py b/zenduty/exceptions.py deleted file mode 100644 index 7cf8737..0000000 --- a/zenduty/exceptions.py +++ /dev/null @@ -1,104 +0,0 @@ -import six - - -class OpenApiException(Exception): - """The base exception class for all OpenAPIExceptions""" - - -class ApiTypeError(OpenApiException, TypeError): - def __init__(self, msg, path_to_item=None, valid_classes=None, key_type=None): - """Raises an exception for TypeErrors - - Args: - msg (str): the exception message - - Keyword Args: - path_to_item (list): a list of keys an indices to get to the - current_item - None if unset - valid_classes (tuple): the primitive classes that current item - should be an instance of - None if unset - key_type (bool): False if our value is a value in a dict - True if it is a key in a dict - False if our item is an item in a list - None if unset - """ - self.path_to_item = path_to_item - self.valid_classes = valid_classes - self.key_type = key_type - full_msg = msg - if path_to_item: - full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) - super(ApiTypeError, self).__init__(full_msg) - - -class ApiValueError(OpenApiException, ValueError): - def __init__(self, msg, path_to_item=None): - """ - Args: - msg (str): the exception message - - Keyword Args: - path_to_item (list) the path to the exception in the - received_data dict. None if unset - """ - - self.path_to_item = path_to_item - full_msg = msg - if path_to_item: - full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) - super(ApiValueError, self).__init__(full_msg) - - -class ApiKeyError(OpenApiException, KeyError): - def __init__(self, msg, path_to_item=None): - """ - Args: - msg (str): the exception message - - Keyword Args: - path_to_item (None/list) the path to the exception in the - received_data dict - """ - self.path_to_item = path_to_item - full_msg = msg - if path_to_item: - full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) - super(ApiKeyError, self).__init__(full_msg) - - -class ApiException(OpenApiException): - def __init__(self, status=None, reason=None, http_resp=None): - if http_resp: - self.status = http_resp.status - self.reason = http_resp.reason - self.body = http_resp.data - self.headers = http_resp.getheaders() - else: - self.status = status - self.reason = reason - self.body = None - self.headers = None - - def __str__(self): - """Custom error messages for exception""" - error_message = "({0})\n" "Reason: {1}\n".format(self.status, self.reason) - if self.headers: - error_message += "HTTP response headers: {0}\n".format(self.headers) - - if self.body: - error_message += "HTTP response body: {0}\n".format(self.body) - - return error_message - - -def render_path(path_to_item): - """Returns a string representation of a path""" - result = "" - for pth in path_to_item: - if isinstance(pth, six.integer_types): - result += "[{0}]".format(pth) - else: - result += "['{0}']".format(pth) - return result diff --git a/zenduty/rest.py b/zenduty/rest.py deleted file mode 100644 index bd97a8b..0000000 --- a/zenduty/rest.py +++ /dev/null @@ -1,31 +0,0 @@ -import urllib3 -import json - -from .exceptions import ApiException, ApiValueError - - -class RESTClientObject(object): - def __init__(self): - self.pool_manager = urllib3.PoolManager() - - def request(self, method, url, body, headers): - # verifying the method - method = method.upper() - assert method in ["GET", "DELETE", "POST", "PATCH"] - # verifying Content-type - if "Content-Type" not in headers: - headers["Content-Type"] = "application/json" - # Making the request - try: - if method == "GET": - return self.pool_manager.request(method, url, headers=headers) - else: - request_body = None - if body is not None: - request_body = json.dumps(body) - return self.pool_manager.request( - method, url, body=request_body, headers=headers - ) - except urllib3.exceptions.SSLError as e: - msg = "{0}\n{1}".format(type(e).__name__, str(e)) - raise ApiException(status=0, reason=msg) diff --git a/setup.py b/zenduty/setup.py similarity index 55% rename from setup.py rename to zenduty/setup.py index ceab627..609f30c 100644 --- a/setup.py +++ b/zenduty/setup.py @@ -2,20 +2,15 @@ setup( name="zenduty-api", - version="0.8", + version="0.9", description="Python SDK wrapper for the Zenduty API", long_description="Python SDK wrapper for the Zenduty API", long_description_content_type="text/x-rst", - author="Vishwa Krishnakumar", - author_email="vishwa@yellowant.com", + author="Anudeep Kalitkar", + author_email="anudeep.kalitkar@gmail.com", packages=find_packages(), install_requires=[ - "requests==2.32.3", - "urllib3==2.2.2", - "six==1.9.0", - "charset-normalizer==3.3.2", - "idna==3.7", + "urllib3==2.3.0", "certifi==2024.7.4" - ], - scripts=["bin/client.py"], + ] ) \ No newline at end of file From 8623778cd671e125d29ccdfd8d3309f205e2ed8f Mon Sep 17 00:00:00 2001 From: Anudeep Kalitkar <50821904+anudeepkalitkar@users.noreply.github.com> Date: Thu, 27 Mar 2025 08:44:57 -0400 Subject: [PATCH 2/4] Added V1 code back --- README.md | 5 +- examples/example_2.py | 2 +- zenduty/__init__.py | 21 +++- zenduty/api/__init__.py | 11 ++ zenduty/api/escalationpolicies_api.py | 69 ++++++++++++ zenduty/api/events_api.py | 25 +++++ zenduty/api/incidents_api.py | 69 ++++++++++++ zenduty/api/integrations_api.py | 60 ++++++++++ zenduty/api/members_api.py | 27 +++++ zenduty/api/schedules_api.py | 68 ++++++++++++ zenduty/api/services_api.py | 69 ++++++++++++ zenduty/api/teams_api.py | 32 ++++++ zenduty/apiV2/authentication/__init__.py | 0 .../authentication/zenduty_credential.py | 14 +++ zenduty/api_client.py | 15 +++ zenduty/configuration.py | 3 + zenduty/exceptions.py | 104 ++++++++++++++++++ zenduty/rest.py | 31 ++++++ 18 files changed, 620 insertions(+), 5 deletions(-) create mode 100644 zenduty/api/__init__.py create mode 100644 zenduty/api/escalationpolicies_api.py create mode 100644 zenduty/api/events_api.py create mode 100644 zenduty/api/incidents_api.py create mode 100644 zenduty/api/integrations_api.py create mode 100644 zenduty/api/members_api.py create mode 100644 zenduty/api/schedules_api.py create mode 100644 zenduty/api/services_api.py create mode 100644 zenduty/api/teams_api.py create mode 100644 zenduty/apiV2/authentication/__init__.py create mode 100644 zenduty/apiV2/authentication/zenduty_credential.py create mode 100644 zenduty/api_client.py create mode 100644 zenduty/configuration.py create mode 100644 zenduty/exceptions.py create mode 100644 zenduty/rest.py diff --git a/README.md b/README.md index 7f222a8..f71832c 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,9 @@ $ git clone https://github.com/Zenduty/zenduty-python-sdk $ python3 setup.py install ``` ## Contents -1) zenduty/apiV2 : contains the functions to communicate with zenduty API endpoints -2) zenduty/ : contains the common required files +1) zenduty/api : contains the functions to communicate with zenduty API endpoints +2) zenduty/apiV2 : contains the functions to communicate with zenduty API endpoints +3) zenduty/ : contains the common required files ## Getting started 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/zenduty/__init__.py b/zenduty/__init__.py index 93657be..ab5d341 100644 --- a/zenduty/__init__.py +++ b/zenduty/__init__.py @@ -2,6 +2,23 @@ __version__ = "1.2.0" + # import apis into sdk package -from .apiV2 import * -from .apiV2.serializer import JsonSerializable, serialize \ No newline at end of file +from .api.incidents_api import IncidentsApi +from .api.integrations_api import IntegrationsApi +from .api.members_api import MembersApi +from .api.services_api import ServicesApi +from .api.teams_api import TeamsApi +from .api.events_api import EventsApi + +# import ApiClient +from .api_client import ApiClient +from .configuration import Configuration +from .exceptions import OpenApiException +from .exceptions import ApiTypeError +from .exceptions import ApiValueError +from .exceptions import ApiKeyError +from .exceptions import ApiException + +# import V2 APIs +import apiV2 diff --git a/zenduty/api/__init__.py b/zenduty/api/__init__.py new file mode 100644 index 0000000..af716c4 --- /dev/null +++ b/zenduty/api/__init__.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +# flake8: noqa + +# import apis into api package +from .incidents_api import IncidentsApi +from .integrations_api import IntegrationsApi +from .members_api import MembersApi +from .services_api import ServicesApi +from .teams_api import TeamsApi +from .events_api import EventsApi diff --git a/zenduty/api/escalationpolicies_api.py b/zenduty/api/escalationpolicies_api.py new file mode 100644 index 0000000..9245682 --- /dev/null +++ b/zenduty/api/escalationpolicies_api.py @@ -0,0 +1,69 @@ +from zenduty.api_client import ApiClient + + +class EscalationPoliciesApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def get_escalation_policies(self, team_id): + # Returns the escalation policies belonging to one team + # params str team_id: unique id of team + return self.api_client.call_api( + "GET", "/api/account/teams/{}/escalation_policies/".format(team_id) + ) + + def create_escalation_policy(self, team_id, body): + # Creates an escalation policy for one team + # params str team_id: unique id of team + # params dict body: contains the required details for creating escalation policy + # Sample body: + # {'name':name, + # 'summary':summary, + # 'description':description, + # 'rules':rules, + # 'unique_id':unique_id, + # 'team':team_id} + return self.api_client.call_api( + "POST", + "/api/account/teams/{}/escalation_policies/".format(team_id), + body=body, + ) + + def get_escalation_policy_by_id(self, team_id, ep_id): + # Returns escalation_policy identified by id + # params str team_id: unique id of team + # params str ep_id: unique id of escalation policy + return self.api_client.call_api( + "GET", + "/api/account/teams/{}/escalation_policies/{}/".format(team_id, ep_id), + ) + + def update_escalation_policy(self, team_id, ep_id, body): + # Updates escalation policy, identified by id + # params str team_id: unique id of team + # params str ep_id: unqiue id of escalation policy + # params dict body: contains all the updated values + # 'rules' is a required part of the body + # Sample body: + # body={'summary':'changes description', + # 'rules':[{"delay":1, + # "targets":[{"target_type":2, + # "target_id":"826032d6-7ccd-4d58-b114-f"}], + # "position":1, + # "unique_id":"c0dad09b-321b-491e-9c23-f816c7bd0339"}]} + return self.api_client.call_api( + "PATCH", + "/api/account/teams/{}/escalation_policies/{}/".format(team_id, ep_id), + body=body, + ) + + def delete_escalation_policy(self, team_id, ep_id): + # Deletes escalation policy, identified by id + # params str team_id: unique id of team + # params str ep_id: unique id of escalation policy + return self.api_client.call_api( + "DELETE", + "/api/account/teams/{}/escalation_policies/{}/".format(team_id, ep_id), + ) diff --git a/zenduty/api/events_api.py b/zenduty/api/events_api.py new file mode 100644 index 0000000..328d8b7 --- /dev/null +++ b/zenduty/api/events_api.py @@ -0,0 +1,25 @@ +from zenduty.api_client import ApiClient + + +class EventsApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def create_event(self, integration_key, body): + # Creates an incident event on zenduty + # params str integration_key: unique key provided for your integration + # params dict body: contains the details of the event + # 'message', 'summary' are required fields of the body + # 'alert_type' is "info" by default + # 'suppressed' is false by default + # if no entity_id is provided, Zenduty provides one automatically + # Sample body: + # {'message':message, + # 'summary':summary, + # 'alert_type':alert_type, + # 'supressed':supressed} + return self.api_client.call_api( + "POST", "/api/events/{}/".format(integration_key), body=body + ) diff --git a/zenduty/api/incidents_api.py b/zenduty/api/incidents_api.py new file mode 100644 index 0000000..b2deb5b --- /dev/null +++ b/zenduty/api/incidents_api.py @@ -0,0 +1,69 @@ +from zenduty.api_client import ApiClient + + +class IncidentsApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def get_incidents(self, body): + # Returns the incidents from your zenduty account + # params dict body: contains all the required details of your account + # Sample body: + # {'page':1, + # 'status':5, + # 'team_id':['a2c6322b-4c1b-4884-8f7a-a7f270de98cb'], + # 'service_ids':[], + # 'user_ids':[]} + return self.api_client.call_api("GET", "/api/incidents/", body=body) + + def get_incidents_by_number(self, incident_number): + # Returns the incidents belonging to a given incident number + # params int incident_number: incident number of event + return self.api_client.call_api( + "GET", "/api/incidents/{}/".format(incident_number) + ) + + def get_incident_alerts(self, incident_number): + # Returns all alerts of a particular incident + # params int incident_number: incident number of event + return self.api_client.call_api( + "GET", "/api/incidents/{}/alerts/".format(incident_number) + ) + + def get_incident_notes(self, incident_number): + # Gets the notes regarding an incident, identified by incident number + # params int incident_number: incident number of event + return self.api_client.call_api( + "GET", "/api/incidents/{}/note/".format(incident_number) + ) + + def acknowledge_or_resolve_incidents(self, incident_number, body): + # Used to acknowledge or resolve incident, identified by incident number + # params str incident_number: incident number of event + # params dict body: contains the changed values of incident + # Sample body: + # {'status':3, + # 'incident_number':12} + return self.api_client.call_api( + "PATCH", "/api/incidents/{}/".format(incident_number), body=body + ) + + def create_incident(self, body): + # Used to create an incident for a particular service, identified by id + # params dict body: contains necessary details for creating incident + # Sample body: + # {"service":"c7fff4c5-2def-41e8-9120-c63f649a825c", + # "escalation_policy":"a70244c8-e343-4dd0-8d87-2f767115568a", + # "user":null, + # "title":"Name of trial", + # "summary":"summary of trial"} + # escalation_policy,service, title and summary are required fields. + # if escalation_policy is not set (set to None then), then assigned_to is required, as follows + # {"service":"b1559a26-c51f-45a1-886d-f6caeaf0fc7e", + # "escalation_policy":null, + # "assigned_to":"826032d6-7ccd-4d58-b114-f", + # "title":"Name of trial", + # "summary":"Summary of trial"} + return self.api_client.call_api("POST", "/api/incidents/", body=body) diff --git a/zenduty/api/integrations_api.py b/zenduty/api/integrations_api.py new file mode 100644 index 0000000..55fff37 --- /dev/null +++ b/zenduty/api/integrations_api.py @@ -0,0 +1,60 @@ +from zenduty.api_client import ApiClient + + +class IntegrationsApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def get_integrations_in_service(self, team_id, service_id): + # Returns the integrations in a service + # params str team_id: unique id of team + # params str service_id: unique id of service + return self.api_client.call_api( + "GET", + "/api/account/teams/{}/services/{}/integrations/".format( + team_id, service_id + ), + ) + + def create_integration(self, team_id, service_id, body): + # Creates a new integration for a given service in a team + # params str team_id: unique id of team + # params str service_id: unique id of service + # params dict body: contains the details of the new integration + # Sample body: + # {"name":"asdf", + # "summary":"asdf", + # "application":"27c9800c-2856-490d-8119-790be1308dd4"} + return self.api_client.call_api( + "POST", + "/api/account/teams/{}/services/{}/integrations/".format( + team_id, service_id + ), + body=body, + ) + + def get_integrations_by_id(self, team_id, service_id, integration_id): + # Returns an integration belonging to a service in a team, identified by id + # params str team_id: unique id of team + # params str service_id: unique id of service + # params str integration_id: unique id of integration + return self.api_client.call_api( + "GET", + "/api/account/teams/{}/services/{}/integrations/{}/".format( + team_id, service_id, integration_id + ), + ) + + def get_alerts_in_integration(self, team_id, service_id, integration_id): + # Retruns alerts in a particular integration + # params str team_id: unique id of team + # params str service_id: unique id of service + # params str integration_id: unique id of integration + return self.api_client.call_api( + "GET", + "/api/account/teams/{}/services/{}/integrations/{}/alerts/".format( + team_id, service_id, integration_id + ), + ) diff --git a/zenduty/api/members_api.py b/zenduty/api/members_api.py new file mode 100644 index 0000000..d888ab5 --- /dev/null +++ b/zenduty/api/members_api.py @@ -0,0 +1,27 @@ +from zenduty.api_client import ApiClient + + +class MembersApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def add_members_to_team(self, team_id, body): + # Adds a member to a given team, identified by id + # params str team_id: unique id of team + # params dict body: contains the details of the user being added and the team to add to + # Sample body: + # {"team":"d4a777db-5bce-419c-a725-420ebb505c54","user":"af9eeb60-5acb-406c-971e-3"} + return self.api_client.call_api( + "POST", "/api/account/teams/{}/members/".format(team_id), body=body + ) + + def delete_members_from_team(self, team_id, member_id): + # Removes a member from a particular team + # params str team_id: unique id of a team + # params str member_id: unique id of member to be deleted + return self.api_client.call_api( + "DELETE", + "/api/account/teams/{}/members/{}/".format(team_id, member_id), + ) diff --git a/zenduty/api/schedules_api.py b/zenduty/api/schedules_api.py new file mode 100644 index 0000000..6300485 --- /dev/null +++ b/zenduty/api/schedules_api.py @@ -0,0 +1,68 @@ +from zenduty.api_client import ApiClient + + +class SchedulesApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def get_schedules(self, team_id): + # Returns the schedules in a particular team, identified by id + # params str team_id: unique id of a team + return self.api_client.call_api( + "GET", "/api/account/teams/{}/schedules/".format(team_id) + ) + + def create_schedule(self, team_id, body): + # Creates a schedule for a team + # params str team_id: unique id of team + # params dict body: contains the details of the schedule to be created + # Sample body: + # {"name":"Name of schedule", + # "summary":"summar of schedule", + # "time_zone":"Asia/Kolkata", + # "team":"d4a777db-5bce-419c-a725-420ebb505c54", + # "layers":[]} + return self.api_client.call_api( + "POST", + "/api/account/teams/{}/schedules/".format(team_id), + body=body, + ) + + def get_schedule_by_id(self, team_id, schedule_id): + # Returns a particular schedule from a team, identifed by id + # params str team_id: unique id of a team + # params schedule_id: unique id of schedule + return self.api_client.call_api( + "GET", + "/api/account/teams/{}/schedules/{}/".format(team_id, schedule_id), + ) + + def update_schedule(self, team_id, schedule_id, body): + # Updates the schedule details for a given team, identified by id + # params str team_id: unique id of a team + # params str schedul_id: unique id of schedule + # params dict body: contains the updated values of schedule + # 'unique_id' and 'team' are required. Other fields are just those which have been changed + # Sample body: + # {"name":"Name of schedule", + # "summary":"summar of schedule", + # "time_zone":"Asia/Kamchatka", + # "team":"d4a777db-5bce-419c-a725-420ebb505c54", + # "unique_id":"f9b34bd3-818a-4b98-9d8a-04d8bd501cd0", + # "layers":[]} + return self.api_client.call_api( + "PATCH", + "/api/account/teams/{}/schedules/{}/".format(team_id, schedule_id), + body=body, + ) + + def delete_schedule(self, team_id, schedule_id): + # Deletes a schedule from a team + # params str team_id:unique id of team + # params str schedule_id: unique id of schedule + return self.api_client.call_api( + "DELETE", + "/api/account/teams/{}/schedules/{}/".format(team_id, schedule_id), + ) diff --git a/zenduty/api/services_api.py b/zenduty/api/services_api.py new file mode 100644 index 0000000..fcc4d09 --- /dev/null +++ b/zenduty/api/services_api.py @@ -0,0 +1,69 @@ +from zenduty.api_client import ApiClient + + +class ServicesApi(object): + def __init__(self, api_client=None): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def get_service_for_team(self, team_id): + # Returns all the services in a team + # params str team_id: unnique id of team + return self.api_client.call_api( + "GET", "/api/account/teams/{}/services/".format(team_id) + ) + + def add_new_service_in_team(self, team_id, body): + # Adds a new servie to a give team, identified by id + # params str team_id: unique id of team + # params dict body: contains the details of the new service to be added + # Sample body + # {"name":"Name of service", + # "description":"Description of service", + # "integrations":[{"application":"27c9800c-2856-490d-8119-790be1308dd4", + # "name":"API", + # "summary":"Edit summary for this integration"}], + # "escalation_policy":"5c9b6288-c105-418d-970b-91a93d0e919a", + # "acknowledgement_timeout":1, + # "auto_resolve_timeout":1} + return self.api_client.call_api( + "POST", + "/api/account/teams/{}/services/".format(team_id), + body=body, + ) + + def get_services_by_id(self, team_id, service_id): + # Returns a particular service from a team, identified by id + # params str team_id: unique id of team + # params str service_id: unique id of service + return self.api_client.call_api( + "GET", + "/api/account/teams/{}/services/{}/".format(team_id, service_id), + ) + + def update_service(self, team_id, service_id, body): + # Updates the existing service in a team + # params str team_id: unique id of team + # params str service_id: unique id of service + # params dict body: contains the updated details of services + # Sample body: + # {"unique_id":"bc808ce3-46c0-41d0-bf1f-f405fdd0c1c3", + # "auto_resolve_timeout":0, + # "acknowledgement_timeout":0, + # "status":1, + # "escalation_policy":"5c9b6288-c105-418d-970b-91a93d0e919a"} + return self.api_client.call_api( + "PATCH", + "/api/account/teams/{}/services/{}/".format(team_id, service_id), + body=body, + ) + + def delete_service_from_team(self, team_id, service_id): + # Deletes a particular service from a team + # params str team_id: unique id of team + # params str service_id: unnique id of service + return self.api_client.call_api( + "DELETE", + "/api/account/teams/{}/services/{}/".format(team_id, service_id), + ) diff --git a/zenduty/api/teams_api.py b/zenduty/api/teams_api.py new file mode 100644 index 0000000..30e2ad4 --- /dev/null +++ b/zenduty/api/teams_api.py @@ -0,0 +1,32 @@ +from zenduty.api_client import ApiClient + + +class TeamsApi(object): + def __init__(self, api_client): + if api_client is None: + api_client = ApiClient() + self.api_client = api_client + + def get_teams(self): + # Returns all the teams and their details from your Zenduty account + return self.api_client.call_api("GET", "/api/account/teams/") + + def create_team(self, body): + # Creates a new team for your zenduty account + # params dict body: contains the details for your new team + # Sample body: + # 'name' is a required field + # {'name':name} + return self.api_client.call_api("POST", "/api/account/teams/", body) + + def get_team_details(self, team_id): + # Returns a team form your zenduty acocunt, identified by id + # params str team_id: unique id of team + return self.api_client.call_api("GET", "/api/account/teams/{}/".format(team_id)) + + def delete_team(self, team_id): + # Deletes a team form your zenduty account + # params str team_id: unique id of team + return self.api_client.call_api( + "DELETE", "/api/account/teams/{}/".format(team_id) + ) diff --git a/zenduty/apiV2/authentication/__init__.py b/zenduty/apiV2/authentication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zenduty/apiV2/authentication/zenduty_credential.py b/zenduty/apiV2/authentication/zenduty_credential.py new file mode 100644 index 0000000..11a6707 --- /dev/null +++ b/zenduty/apiV2/authentication/zenduty_credential.py @@ -0,0 +1,14 @@ +import os + + +class ZendutyCredential: + _api_key: str + + def __init__(self, api_key: str = os.environ.get("ZENDUTY_API_KEY")): + if api_key == "": + raise ValueError("error: api_key length must be greater than 0") + else: + self._api_key = api_key + + def get_api_key(self) -> str: + return self._api_key diff --git a/zenduty/api_client.py b/zenduty/api_client.py new file mode 100644 index 0000000..0610638 --- /dev/null +++ b/zenduty/api_client.py @@ -0,0 +1,15 @@ +from .rest import RESTClientObject +from .configuration import Configuration + + +class ApiClient: + def __init__(self, access_token): + self.configuration = Configuration(access_token) + self.rest_client = RESTClientObject() + + def call_api(self, method, url, body={}, headers={}): + # building the header + url = "https://www.zenduty.com" + url + headers["Authorization"] = "Token " + self.configuration.access_token + # making the request through RESTClientObject + return self.rest_client.request(method, url, body, headers) diff --git a/zenduty/configuration.py b/zenduty/configuration.py new file mode 100644 index 0000000..2a9b05e --- /dev/null +++ b/zenduty/configuration.py @@ -0,0 +1,3 @@ +class Configuration(object): + def __init__(self, access_token): + self.access_token = access_token diff --git a/zenduty/exceptions.py b/zenduty/exceptions.py new file mode 100644 index 0000000..7cf8737 --- /dev/null +++ b/zenduty/exceptions.py @@ -0,0 +1,104 @@ +import six + + +class OpenApiException(Exception): + """The base exception class for all OpenAPIExceptions""" + + +class ApiTypeError(OpenApiException, TypeError): + def __init__(self, msg, path_to_item=None, valid_classes=None, key_type=None): + """Raises an exception for TypeErrors + + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list): a list of keys an indices to get to the + current_item + None if unset + valid_classes (tuple): the primitive classes that current item + should be an instance of + None if unset + key_type (bool): False if our value is a value in a dict + True if it is a key in a dict + False if our item is an item in a list + None if unset + """ + self.path_to_item = path_to_item + self.valid_classes = valid_classes + self.key_type = key_type + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiTypeError, self).__init__(full_msg) + + +class ApiValueError(OpenApiException, ValueError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (list) the path to the exception in the + received_data dict. None if unset + """ + + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiValueError, self).__init__(full_msg) + + +class ApiKeyError(OpenApiException, KeyError): + def __init__(self, msg, path_to_item=None): + """ + Args: + msg (str): the exception message + + Keyword Args: + path_to_item (None/list) the path to the exception in the + received_data dict + """ + self.path_to_item = path_to_item + full_msg = msg + if path_to_item: + full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) + super(ApiKeyError, self).__init__(full_msg) + + +class ApiException(OpenApiException): + def __init__(self, status=None, reason=None, http_resp=None): + if http_resp: + self.status = http_resp.status + self.reason = http_resp.reason + self.body = http_resp.data + self.headers = http_resp.getheaders() + else: + self.status = status + self.reason = reason + self.body = None + self.headers = None + + def __str__(self): + """Custom error messages for exception""" + error_message = "({0})\n" "Reason: {1}\n".format(self.status, self.reason) + if self.headers: + error_message += "HTTP response headers: {0}\n".format(self.headers) + + if self.body: + error_message += "HTTP response body: {0}\n".format(self.body) + + return error_message + + +def render_path(path_to_item): + """Returns a string representation of a path""" + result = "" + for pth in path_to_item: + if isinstance(pth, six.integer_types): + result += "[{0}]".format(pth) + else: + result += "['{0}']".format(pth) + return result diff --git a/zenduty/rest.py b/zenduty/rest.py new file mode 100644 index 0000000..bd97a8b --- /dev/null +++ b/zenduty/rest.py @@ -0,0 +1,31 @@ +import urllib3 +import json + +from .exceptions import ApiException, ApiValueError + + +class RESTClientObject(object): + def __init__(self): + self.pool_manager = urllib3.PoolManager() + + def request(self, method, url, body, headers): + # verifying the method + method = method.upper() + assert method in ["GET", "DELETE", "POST", "PATCH"] + # verifying Content-type + if "Content-Type" not in headers: + headers["Content-Type"] = "application/json" + # Making the request + try: + if method == "GET": + return self.pool_manager.request(method, url, headers=headers) + else: + request_body = None + if body is not None: + request_body = json.dumps(body) + return self.pool_manager.request( + method, url, body=request_body, headers=headers + ) + except urllib3.exceptions.SSLError as e: + msg = "{0}\n{1}".format(type(e).__name__, str(e)) + raise ApiException(status=0, reason=msg) From fdbad0f05dd61859ab8b51824d5097d5f4289a62 Mon Sep 17 00:00:00 2001 From: Anudeep Kalitkar <50821904+anudeepkalitkar@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:42:42 -0400 Subject: [PATCH 3/4] reverted setup.py changes --- zenduty/setup.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/zenduty/setup.py b/zenduty/setup.py index 609f30c..f76153a 100644 --- a/zenduty/setup.py +++ b/zenduty/setup.py @@ -2,15 +2,19 @@ setup( name="zenduty-api", - version="0.9", + version="0.8", description="Python SDK wrapper for the Zenduty API", long_description="Python SDK wrapper for the Zenduty API", long_description_content_type="text/x-rst", - author="Anudeep Kalitkar", - author_email="anudeep.kalitkar@gmail.com", + author="Vishwa Krishnakumar", + author_email="vishwa@yellowant.com", packages=find_packages(), install_requires=[ - "urllib3==2.3.0", + "requests==2.32.3", + "urllib3==2.2.2", + "six==1.9.0", + "charset-normalizer==3.3.2", + "idna==3.7", "certifi==2024.7.4" - ] + ], ) \ No newline at end of file From c9b44f268133637b4659dbc4c537295a28fd9737 Mon Sep 17 00:00:00 2001 From: Anudeep Kalitkar <50821904+anudeepkalitkar@users.noreply.github.com> Date: Wed, 2 Apr 2025 12:43:22 -0400 Subject: [PATCH 4/4] incident and overridechanges --- zenduty/apiV2/incidents/__init__.py | 37 ++++++++++++++++++----- zenduty/apiV2/teams/schedules/__init__.py | 24 ++++++++++++++- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/zenduty/apiV2/incidents/__init__.py b/zenduty/apiV2/incidents/__init__.py index 44b9d65..f943191 100644 --- a/zenduty/apiV2/incidents/__init__.py +++ b/zenduty/apiV2/incidents/__init__.py @@ -5,7 +5,6 @@ from zenduty.apiV2.events import Event from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod - class IncidentClient: def __init__(self, client: ZendutyClient): """The constructor for an incident client @@ -90,11 +89,22 @@ def get_all_incidents( request_payload=payload, success_code=200, ) - incidents = [] - for incident in response.get('results',[]): - incidents.append(self.get_incident_by_unique_id_or_incident_number(incident['incident_number'])) - - return incidents + 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 @@ -112,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: @@ -128,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/teams/schedules/__init__.py b/zenduty/apiV2/teams/schedules/__init__.py index 303d92c..d54300e 100644 --- a/zenduty/apiV2/teams/schedules/__init__.py +++ b/zenduty/apiV2/teams/schedules/__init__.py @@ -1,7 +1,7 @@ from uuid import UUID from ..models import Team -from .models import Schedule +from .models import Schedule, Override from zenduty.apiV2.client import ZendutyClient, ZendutyClientRequestMethod @@ -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)