From 40763d6a975cfa97cb5db4606a69da60a14d9e7f Mon Sep 17 00:00:00 2001 From: mertsaygi Date: Fri, 12 Nov 2021 16:58:42 +0300 Subject: [PATCH] The schedule resource has been created for OpsGenie --- README.md | 13 ++ examples/opsgenie_schedule/README.md | 19 ++ examples/opsgenie_schedule/stack.json | 35 ++++ opsgenie_schedule/.gitignore | 24 +++ opsgenie_schedule/.rpdk-config | 18 ++ .../atlassian-opsgenie-schedule.json | 98 ++++++++++ opsgenie_schedule/pom.xml | 178 ++++++++++++++++++ .../opsgenie/schedule/CallbackContext.java | 12 ++ .../opsgenie/schedule/Configuration.java | 16 ++ .../opsgenie/schedule/CreateHandler.java | 86 +++++++++ .../opsgenie/schedule/DeleteHandler.java | 46 +++++ .../opsgenie/schedule/ListHandler.java | 65 +++++++ .../opsgenie/schedule/ReadHandler.java | 64 +++++++ .../opsgenie/schedule/UpdateHandler.java | 76 ++++++++ .../schedule/client/OpsgenieClient.java | 104 ++++++++++ .../client/OpsgenieClientException.java | 25 +++ .../schedule/model/CreateScheduleRequest.java | 17 ++ .../model/CreateScheduleResponse.java | 11 ++ .../opsgenie/schedule/model/DataModel.java | 23 +++ .../schedule/model/GetScheduleResponse.java | 11 ++ .../schedule/model/ListScheduleResponse.java | 13 ++ .../opsgenie/schedule/model/OwnerTeam.java | 11 ++ .../opsgenie/schedule/model/Participant.java | 10 + .../opsgenie/schedule/model/Restriction.java | 10 + .../opsgenie/schedule/model/Rotation.java | 23 +++ .../schedule/model/TimeRestriction.java | 13 ++ .../schedule/model/UpdateScheduleRequest.java | 18 ++ pom.xml | 1 + schedule_template.yml | 23 +++ 29 files changed, 1063 insertions(+) create mode 100644 examples/opsgenie_schedule/README.md create mode 100644 examples/opsgenie_schedule/stack.json create mode 100644 opsgenie_schedule/.gitignore create mode 100644 opsgenie_schedule/.rpdk-config create mode 100644 opsgenie_schedule/atlassian-opsgenie-schedule.json create mode 100644 opsgenie_schedule/pom.xml create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CallbackContext.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/Configuration.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CreateHandler.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/DeleteHandler.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ListHandler.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ReadHandler.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/UpdateHandler.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClient.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClientException.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleRequest.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleResponse.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/DataModel.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/GetScheduleResponse.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/ListScheduleResponse.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/OwnerTeam.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Participant.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Restriction.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Rotation.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/TimeRestriction.java create mode 100644 opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/UpdateScheduleRequest.java create mode 100644 schedule_template.yml diff --git a/README.md b/README.md index ff5e9d0..a2494b7 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ Old version: *v1.0* - Opsgenie Integration +- Opsgenie Schedule + + ## Installation @@ -61,6 +64,16 @@ aws cloudformation register-type \ ``` +- Opsgenie Schedule resource +``` +aws cloudformation register-type \ +--region us-west-2 \ +--type-name "Atlassian::Opsgenie::Schedule" \ +--schema-handler-package "s3://opsgeniedownloads/cloudformation/atlassian-opsgenie-schedule-v1.1.zip" \ +--type RESOURCE +``` + + ### Install using cfn-cli - clone this repo diff --git a/examples/opsgenie_schedule/README.md b/examples/opsgenie_schedule/README.md new file mode 100644 index 0000000..20696d5 --- /dev/null +++ b/examples/opsgenie_schedule/README.md @@ -0,0 +1,19 @@ +# Opsgenie Schedule + +## Atlassian::Opsgenie::Schedule + +Allows us to create schedule entities in Opsgenie via Amazon Cloudformation.
+To the team, you can associate schedule upon creation.
+ +## Fields + +| Property | Description | Required | Limit | +|--------------------- |-------------------------------------------------------------------------------------------------|---------- |--------------------------------------- | +| OpsgenieApiKey | Your Opsgenie provided API Key | Required | | +| OpsgenieApiEndpoint | Endpoint of API according to your preferred environment | Required | api.eu.opsgenie.com, api.opsgenie.com | +| Name | Name of the schedule | Required | 100 chars | +| Description | The description of team | Optional | 10000 chars | +| Timezone | The timezone of schedule | Optional | 10000 chars | +| Enabled | This parameter is for specifying whether the schedule will be enabled or not. Defaults to true | Optional | Boolean value defaults to true. | +| OwnerTeamId | The identifier of the team associated with the integration. | Required | | +| OwnerTeamName | The name of the team associated with the integration. | Optional | | | diff --git a/examples/opsgenie_schedule/stack.json b/examples/opsgenie_schedule/stack.json new file mode 100644 index 0000000..8cfc81c --- /dev/null +++ b/examples/opsgenie_schedule/stack.json @@ -0,0 +1,35 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Opsgenie Schedule", + "Parameters": { + "OpsgenieApiKey": { + "Description": "Enter Opsgenie Api Key", + "Type": "String" + }, + "OpsgenieEndpoint": { + "Description": "Enter Opsgenie Api URL", + "Type": "String", + "Default": "https://api.opsgenie.com", + "AllowedValues": [ + "https://api.opsgenie.com", + "https://api.eu.opsgenie.com", + "https://api.sandbox.opsgenie.com" + ] + } + }, + "Resources": { + "ScheduleTest": { + "Type": "Atlassian::Opsgenie::Schedule", + "Properties": { + "OpsgenieApiKey": { + "Ref": "OpsgenieApiKey" + }, + "OpsgenieApiEndpoint": { + "Ref": "OpsgenieEndpoint" + }, + "Name": "Test Schedule", + "Enabled":true + } + } + } +} diff --git a/opsgenie_schedule/.gitignore b/opsgenie_schedule/.gitignore new file mode 100644 index 0000000..384990c --- /dev/null +++ b/opsgenie_schedule/.gitignore @@ -0,0 +1,24 @@ +# macOS +.DS_Store +._* + +# Maven outputs +.classpath + +# IntelliJ +*.iml +.idea +out.java +out/ +.settings +.project + +# auto-generated files +target/ +docs/ +resource-role.yaml +.hypothesis/ +.pytest_cache/ + +# our logs +rpdk.log diff --git a/opsgenie_schedule/.rpdk-config b/opsgenie_schedule/.rpdk-config new file mode 100644 index 0000000..a9ad079 --- /dev/null +++ b/opsgenie_schedule/.rpdk-config @@ -0,0 +1,18 @@ +{ + "artifact_type": "RESOURCE", + "typeName": "Atlassian::Opsgenie::Schedule", + "language": "java", + "runtime": "java8", + "entrypoint": "com.atlassian.opsgenie.schedule.HandlerWrapper::handleRequest", + "testEntrypoint": "com.atlassian.opsgenie.schedule.HandlerWrapper::testEntrypoint", + "settings": { + "namespace": [ + "com", + "atlassian", + "opsgenie", + "schedule" + ], + "protocolVersion": "2.0.0" + }, + "executableEntrypoint": "com.atlassian.opsgenie.schedule.HandlerWrapperExecutable" +} diff --git a/opsgenie_schedule/atlassian-opsgenie-schedule.json b/opsgenie_schedule/atlassian-opsgenie-schedule.json new file mode 100644 index 0000000..a91014c --- /dev/null +++ b/opsgenie_schedule/atlassian-opsgenie-schedule.json @@ -0,0 +1,98 @@ +{ + "typeName": "Atlassian::Opsgenie::Schedule", + "description": "Opsgenie Schedule Resource definition", + "sourceUrl": "https://github.com/opsgenie/opsgenie-cloudformation-resources", + "definitions": { + }, + "properties": { + "OpsgenieApiEndpoint": { + "type": "string", + "pattern": "^https:\/\/api(\\.eu|\\.sandbox|)\\.opsgenie\\.com$", + "minLength": 1 + }, + "OpsgenieApiKey": { + "type": "string", + "pattern": "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$", + "minLength": 1 + }, + "ScheduleId": { + "type": "string", + "pattern": "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}" + }, + "Enabled": { + "type": "boolean", + "description": "Schedule status, default is true", + "minLength": 1 + }, + "Name": { + "type": "string", + "pattern": "^[a-zA-Z0-9- _.]+$", + "description": "Integration name", + "minLength": 1 + }, + "Description": { + "type": "string", + "description": "Schedule description", + "minLength": 1 + }, + "OwnerTeamId": { + "type": "string", + "description": "Id of the schedule owner team.", + "minLength": 1 + }, + "OwnerTeamName": { + "type": "string", + "pattern": "^[a-zA-Z0-9-_.]+$", + "description": "Name of the schedule owner team.", + "minLength": 1 + }, + "Timezone": { + "type": "string", + "description": "Schedule timezone", + "minLength": 1 + } + }, + "required": [ + "OpsgenieApiEndpoint", + "OpsgenieApiKey", + "Name" + ], + "primaryIdentifier": [ + "/properties/ScheduleId" + ], + "readOnlyProperties": [ + "/properties/ScheduleId" + ], + "writeOnlyProperties": [ + "/properties/OwnerTeamId" + ], + "handlers": { + "create": { + "permissions": [ + "" + ] + }, + "read": { + "permissions": [ + "" + ] + }, + "update": { + "permissions": [ + "" + ] + }, + "delete": { + "permissions": [ + "" + ] + }, + "list": { + "permissions": [ + "" + ] + } + }, + "additionalProperties": false +} + diff --git a/opsgenie_schedule/pom.xml b/opsgenie_schedule/pom.xml new file mode 100644 index 0000000..9a3ba03 --- /dev/null +++ b/opsgenie_schedule/pom.xml @@ -0,0 +1,178 @@ + + + 4.0.0 + + com.atlassian.opsgenie.schedule + atlassian-opsgenie-schedule-handler + atlassian-opsgenie-schedule-handler + 1.0.1 + jar + + + com.atlassian.opsgenie + opsgenie-cloudformation-resources + 1.0.1 + + + + 1.8 + 1.8 + UTF-8 + UTF-8 + + + + + central + http://central.maven.org/maven2 + + + + + software.amazon.cloudformation + aws-cloudformation-rpdk-java-plugin + 2.0.3 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + -Xlint:all,-options,-processing + -Werror + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + false + + + + package + + shade + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + generate + generate-sources + + exec + + + cfn-cli + generate + ${project.basedir} + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add-source + generate-sources + + add-source + + + + ${project.basedir}/target/generated-sources/rpdk + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.4 + + + maven-surefire-plugin + 3.0.0-M3 + + + org.jacoco + jacoco-maven-plugin + 0.8.4 + + + **/BaseConfiguration* + **/BaseHandler* + **/HandlerWrapper* + **/ResourceModel* + + + + + + prepare-agent + + + + report + test + + report + + + + jacoco-check + + check + + + + + PACKAGE + + + BRANCH + COVEREDRATIO + 0.8 + + + INSTRUCTION + COVEREDRATIO + 0.8 + + + + + + + + + + + + ${project.basedir} + + atlassian-opsgenie-schedule.json + + + + + diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CallbackContext.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CallbackContext.java new file mode 100644 index 0000000..6284ea5 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CallbackContext.java @@ -0,0 +1,12 @@ +package com.atlassian.opsgenie.schedule; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@Builder +public class CallbackContext { + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/Configuration.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/Configuration.java new file mode 100644 index 0000000..d4ee2b6 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/Configuration.java @@ -0,0 +1,16 @@ +package com.atlassian.opsgenie.schedule; + +import org.json.JSONObject; +import org.json.JSONTokener; + +class Configuration extends BaseConfiguration { + + public Configuration() { + super("atlassian-opsgenie-schedule.json"); + } + + public JSONObject resourceSchemaJSONObject() { + return new JSONObject(new JSONTokener(this.getClass().getClassLoader().getResourceAsStream(schemaFilename))); + } + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CreateHandler.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CreateHandler.java new file mode 100644 index 0000000..4438093 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/CreateHandler.java @@ -0,0 +1,86 @@ +package com.atlassian.opsgenie.schedule; + +import software.amazon.cloudformation.proxy.*; +import com.atlassian.opsgenie.schedule.client.OpsgenieClient; +import com.atlassian.opsgenie.schedule.client.OpsgenieClientException; +import com.atlassian.opsgenie.schedule.model.CreateScheduleRequest; +import com.atlassian.opsgenie.schedule.model.CreateScheduleResponse; +import com.atlassian.opsgenie.schedule.model.OwnerTeam; + +import java.io.IOException; +import java.util.Collections; + +public class CreateHandler extends BaseHandler { + + @Override + public ProgressEvent handleRequest( + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest request, + final CallbackContext callbackContext, + final Logger logger) { + + final ResourceModel model = request.getDesiredResourceState(); + OpsgenieClient OGClient = new OpsgenieClient(model.getOpsgenieApiEndpoint(), model.getOpsgenieApiKey()); + CreateScheduleRequest req = new CreateScheduleRequest(); + + req.setName(model.getName()); + req.setDescription(model.getDescription()); + req.setTimezone(model.getTimezone()); + + if (model.getEnabled() == null) { + model.setEnabled(true); + } + + if (model.getOwnerTeamId() != null) { + OwnerTeam ownerTeam = new OwnerTeam(); + ownerTeam.setId(model.getOwnerTeamId()); + req.setOwnerTeam(ownerTeam); + } else if (model.getOwnerTeamName() != null) { + OwnerTeam ownerTeam = new OwnerTeam(); + ownerTeam.setName(model.getOwnerTeamName()); + ownerTeam.setId(null); + req.setOwnerTeam(ownerTeam); + } + + req.setEnabled(model.getEnabled()); + + try { + logger.log("[CREATE] Request data: " + req.toString()); + if(model.getScheduleId()!=null && !model.getScheduleId().equals("")){ + throw new OpsgenieClientException("Invalid request",400); + } + CreateScheduleResponse resp = OGClient.CreateSchedule(req); + } catch (OpsgenieClientException e) { + logger.log(e.getMessage()); + HandlerErrorCode errorCode = HandlerErrorCode.GeneralServiceException; + if (e.getCode() == 422) { + errorCode = HandlerErrorCode.AlreadyExists; + } + if (e.getCode() == 429) { + errorCode = HandlerErrorCode.Throttling; + } + if (e.getCode() == 400) { + errorCode = HandlerErrorCode.InvalidRequest; + } + return ProgressEvent.builder() + .errorCode(errorCode) + .message(e.getMessage()) + .status(OperationStatus.FAILED) + .build(); + } catch (IOException e) { + logger.log(e.getMessage()); + return ProgressEvent.builder() + .errorCode(HandlerErrorCode.InternalFailure) + .message(e.getMessage()) + .status(OperationStatus.FAILED) + .build(); + } + + logger.log("[CREATE] " + model.getScheduleId()); + return ProgressEvent. + builder() + .resourceModel(model) + .status(OperationStatus.SUCCESS) + .build(); + } +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/DeleteHandler.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/DeleteHandler.java new file mode 100644 index 0000000..e2ee474 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/DeleteHandler.java @@ -0,0 +1,46 @@ +package com.atlassian.opsgenie.schedule; + +import software.amazon.cloudformation.proxy.*; +import com.atlassian.opsgenie.schedule.client.OpsgenieClient; +import com.atlassian.opsgenie.schedule.client.OpsgenieClientException; + +import java.io.IOException; + +public class DeleteHandler extends BaseHandler { + + @Override + public ProgressEvent handleRequest( + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest request, + final CallbackContext callbackContext, + final Logger logger) { + + final ResourceModel model = request.getDesiredResourceState(); + OpsgenieClient OGClient = new OpsgenieClient(model.getOpsgenieApiEndpoint(),model.getOpsgenieApiKey()); + + try { + OGClient.DeleteSchedule(model.getScheduleId()); + } catch (IOException e) { + logger.log(e.getMessage()); + return ProgressEvent.builder() + .resourceModel(model) + .status(OperationStatus.FAILED) + .build(); + } catch (OpsgenieClientException e) { + logger.log(e.getMessage()); + HandlerErrorCode errorCode = HandlerErrorCode.GeneralServiceException; + if (e.getCode() == 404) { + errorCode = HandlerErrorCode.NotFound; + } + return ProgressEvent.builder() + .errorCode(errorCode) + .status(OperationStatus.FAILED) + .build(); + } + + logger.log("[DELETE] " + model.getScheduleId()); + return ProgressEvent.builder() + .status(OperationStatus.SUCCESS) + .build(); + } +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ListHandler.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ListHandler.java new file mode 100644 index 0000000..4f9128e --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ListHandler.java @@ -0,0 +1,65 @@ +package com.atlassian.opsgenie.schedule; + +import software.amazon.cloudformation.proxy.*; +import com.atlassian.opsgenie.schedule.model.ListScheduleResponse; +import com.atlassian.opsgenie.schedule.client.OpsgenieClient; +import com.atlassian.opsgenie.schedule.client.OpsgenieClientException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class ListHandler extends BaseHandler { + + @Override + public ProgressEvent handleRequest( + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest request, + final CallbackContext callbackContext, + final Logger logger) { + + final List models = new ArrayList<>(); + + OpsgenieClient OGClient = new OpsgenieClient(request.getDesiredResourceState().getOpsgenieApiEndpoint(), request.getDesiredResourceState().getOpsgenieApiKey()); + + try { + ListScheduleResponse resp = OGClient.ListSchedules(); + logger.log(resp.toString()); + resp.getData().stream().forEach(model -> { + ResourceModel resourceModel = new ResourceModel(); + resourceModel.setName(model.getName()); + resourceModel.setDescription(model.getDescription()); + resourceModel.setTimezone(model.getTimezone()); + resourceModel.setEnabled(model.isEnabled()); + if (model.getOwnerTeam() != null) { + resourceModel.setOwnerTeamId(model.getOwnerTeam().getId()); + resourceModel.setOwnerTeamName(model.getOwnerTeam().getName()); + } + resourceModel.setOpsgenieApiEndpoint(request.getDesiredResourceState().getOpsgenieApiEndpoint()); + resourceModel.setOpsgenieApiKey(request.getDesiredResourceState().getOpsgenieApiKey()); + models.add(resourceModel); + }); + } catch (IOException e) { + logger.log(e.getMessage()); + e.printStackTrace(); + return ProgressEvent.builder() + .status(OperationStatus.FAILED) + .build(); + } catch (OpsgenieClientException e) { + logger.log(e.getMessage()); + HandlerErrorCode errorCode = HandlerErrorCode.GeneralServiceException; + if (e.getCode() == 404) { + errorCode = HandlerErrorCode.NotFound; + } + return ProgressEvent.builder() + .errorCode(errorCode) + .status(OperationStatus.FAILED) + .build(); + } + + return ProgressEvent.builder() + .resourceModels(models) + .status(OperationStatus.SUCCESS) + .build(); + } +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ReadHandler.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ReadHandler.java new file mode 100644 index 0000000..b7a9bc0 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/ReadHandler.java @@ -0,0 +1,64 @@ +package com.atlassian.opsgenie.schedule; + +import software.amazon.cloudformation.proxy.*; +import com.atlassian.opsgenie.schedule.client.OpsgenieClient; +import com.atlassian.opsgenie.schedule.client.OpsgenieClientException; +import com.atlassian.opsgenie.schedule.model.GetScheduleResponse; + +import java.io.IOException; + +public class ReadHandler extends BaseHandler { + + @Override + public ProgressEvent handleRequest( + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest request, + final CallbackContext callbackContext, + final Logger logger) { + + final ResourceModel model = request.getDesiredResourceState(); + + OpsgenieClient OGClient = new OpsgenieClient(model.getOpsgenieApiEndpoint(), model.getOpsgenieApiKey()); + try { + GetScheduleResponse resp = OGClient.GetSchedule(model.getScheduleId()); + model.setName(resp.getData().getName()); + model.setDescription(resp.getData().getDescription()); + model.setTimezone(resp.getData().getTimezone()); + model.setEnabled(resp.getData().isEnabled()); + if (resp.getData().getOwnerTeam() != null) { + model.setOwnerTeamId(resp.getData().getOwnerTeam().getId()); + model.setOwnerTeamName(resp.getData().getOwnerTeam().getName()); + } + + + } catch (OpsgenieClientException e) { + HandlerErrorCode errorCode = HandlerErrorCode.GeneralServiceException; + logger.log(e.getMessage()); + e.printStackTrace(); + if (e.getCode() == 429) { + errorCode = HandlerErrorCode.Throttling; + } + if (e.getCode() == 404) { + errorCode = HandlerErrorCode.NotFound; + } + + return ProgressEvent.builder() + .message(e.getMessage()) + .errorCode(errorCode) + .status(OperationStatus.FAILED) + .build(); + } catch (IOException e) { + e.printStackTrace(); + return ProgressEvent.builder() + .errorCode(HandlerErrorCode.InternalFailure) + .status(OperationStatus.FAILED) + .build(); + } + + logger.log("[READ] " + model.getScheduleId()); + return ProgressEvent.builder() + .resourceModel(model) + .status(OperationStatus.SUCCESS) + .build(); + } +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/UpdateHandler.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/UpdateHandler.java new file mode 100644 index 0000000..dec59e9 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/UpdateHandler.java @@ -0,0 +1,76 @@ +package com.atlassian.opsgenie.schedule; + +import software.amazon.cloudformation.proxy.*; +import com.atlassian.opsgenie.schedule.client.OpsgenieClient; +import com.atlassian.opsgenie.schedule.client.OpsgenieClientException; +import com.atlassian.opsgenie.schedule.model.*; + +import java.io.IOException; + +public class UpdateHandler extends BaseHandler { + + @Override + public ProgressEvent handleRequest( + final AmazonWebServicesClientProxy proxy, + final ResourceHandlerRequest request, + final CallbackContext callbackContext, + final Logger logger) { + + final ResourceModel model = request.getDesiredResourceState(); + + OpsgenieClient OGClient = new OpsgenieClient(model.getOpsgenieApiEndpoint(), model.getOpsgenieApiKey()); + UpdateScheduleRequest req = new UpdateScheduleRequest(); + + if (model.getOwnerTeamId() != null) { + OwnerTeam ownerTeam = new OwnerTeam(); + ownerTeam.setId(model.getOwnerTeamId()); + req.setOwnerTeam(ownerTeam); + } else if (model.getOwnerTeamName() != null) { + OwnerTeam ownerTeam = new OwnerTeam(); + ownerTeam.setName(model.getOwnerTeamName()); + req.setOwnerTeam(ownerTeam); + } + req.setName(model.getName()); + req.setDescription(model.getDescription()); + req.setTimezone(model.getTimezone()); + req.setId(model.getScheduleId()); + req.setEnabled(model.getEnabled()); + + try { + OGClient.UpdateSchedule(req); + + } catch (OpsgenieClientException e) { + logger.log(e.getMessage()+e.getCode()); + HandlerErrorCode errorCode = HandlerErrorCode.GeneralServiceException; + if (e.getCode() == 429) { + errorCode = HandlerErrorCode.Throttling; + } + if (e.getCode() == 404 || e.getCode() == 422 || e.getCode() == 403) { + errorCode = HandlerErrorCode.NotFound; + } + + if (e.getCode() == 409 ) { + errorCode = HandlerErrorCode.ResourceConflict; + } + + return ProgressEvent.builder() + .errorCode(errorCode) + .message(e.getMessage()) + .status(OperationStatus.FAILED) + .build(); + } catch (IOException e) { + logger.log(e.getMessage()); + return ProgressEvent.builder() + .message(e.getMessage()) + .errorCode(HandlerErrorCode.InternalFailure) + .status(OperationStatus.FAILED) + .build(); + } + + logger.log("[UPDATE] " + model.getScheduleId()); + return ProgressEvent.builder() + .resourceModel(model) + .status(OperationStatus.SUCCESS) + .build(); + } +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClient.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClient.java new file mode 100644 index 0000000..7c8a0bf --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClient.java @@ -0,0 +1,104 @@ +package com.atlassian.opsgenie.schedule.client; + +import com.atlassian.opsgenie.schedule.model.CreateScheduleRequest; +import com.atlassian.opsgenie.schedule.model.CreateScheduleResponse; +import com.atlassian.opsgenie.schedule.model.GetScheduleResponse; +import com.atlassian.opsgenie.schedule.model.ListScheduleResponse; +import com.atlassian.opsgenie.schedule.model.UpdateScheduleRequest; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.*; +import okhttp3.Request; + +import java.io.IOException; + +public class OpsgenieClient { + + private String apiKey; + private String endpoint; + private OkHttpClient httpClient; + private String userAgent = "opsgenie-cloudformation-schedule-client"; + + public OpsgenieClient(String endpoint, String apiKey) { + this.apiKey = apiKey; + this.endpoint = endpoint; + this.httpClient = new OkHttpClient(); + } + + private String execute(Request request) throws IOException, OpsgenieClientException { + Response response = this.httpClient.newCall(request).execute(); + if (!(response.code() >= 200 && response.code() <= 299)) { + throw new OpsgenieClientException(response.body().string(), response.code()); + } + return response.body().string(); + + } + + public CreateScheduleResponse CreateSchedule(CreateScheduleRequest req) throws IOException, OpsgenieClientException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + String json = objectMapper.writeValueAsString(req); + RequestBody body = RequestBody.create(json, MediaType.get("application/json")); + Request request = new Request.Builder() + .url(this.endpoint + "/v2/schedules") + .post(body) + .addHeader("Authorization", "GenieKey " + this.apiKey) + .addHeader("User-Agent", userAgent) + .build(); + + return objectMapper.readValue(execute(request), CreateScheduleResponse.class); + } + + public void DeleteSchedule(String Id) throws IOException, OpsgenieClientException { + Request request = new Request.Builder() + .url(this.endpoint + "/v2/schedules/" + Id + "?apiKey=" + this.apiKey) + .addHeader("Authorization", "GenieKey " + this.apiKey) + .addHeader("User-Agent", userAgent) + .delete() + .build(); + execute(request); + } + + public void UpdateSchedule(UpdateScheduleRequest req) throws IOException, OpsgenieClientException { + ObjectMapper objectMapper = new ObjectMapper(); + String json = objectMapper.writeValueAsString(req); + RequestBody body = RequestBody.create(json, MediaType.get("application/json")); + Request request = new Request.Builder() + .url(this.endpoint + "/v2/schedules/" + req.getId()) + .put(body) + .addHeader("Authorization", "GenieKey " + this.apiKey) + .addHeader("User-Agent", userAgent) + .build(); + execute(request); + + } + + public GetScheduleResponse GetSchedule(String Id) throws IOException, OpsgenieClientException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + Request request = new Request.Builder() + .url(this.endpoint + "/v2/schedules/" + Id + "?apiKey=" + this.apiKey) + .get() + .addHeader("Authorization", "GenieKey " + this.apiKey) + .addHeader("User-Agent", userAgent) + .build(); + return objectMapper.readValue(execute(request), GetScheduleResponse.class); + + } + + public ListScheduleResponse ListSchedules() throws IOException, OpsgenieClientException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + Request request = new Request.Builder() + .url(this.endpoint + "/v2/schedules?apiKey=" + this.apiKey) + .get() + .addHeader("Authorization", "GenieKey " + this.apiKey) + .addHeader("User-Agent", userAgent) + .build(); + return objectMapper.readValue(execute(request), ListScheduleResponse.class); + + } + + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClientException.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClientException.java new file mode 100644 index 0000000..9151310 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/client/OpsgenieClientException.java @@ -0,0 +1,25 @@ +package com.atlassian.opsgenie.schedule.client; + +public class OpsgenieClientException extends Exception { + static final long serialVersionUID = -3387516993124229948L; + + /** + * Represents error code returning form OpsGenie service. + */ + private int code; + + /** + * Constructs an OpsGenieClientException with specified error message and error code. + */ + public OpsgenieClientException(String message, int code) { + super(message); + this.code = code; + } + + /** + * Retrieves error code returning form OpsGenie service. + */ + public int getCode() { + return code; + } +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleRequest.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleRequest.java new file mode 100644 index 0000000..328c320 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleRequest.java @@ -0,0 +1,17 @@ +package com.atlassian.opsgenie.schedule.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreateScheduleRequest { + + private String name; + private String description; + private OwnerTeam ownerTeam; + private String timezone; + private boolean enabled; + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleResponse.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleResponse.java new file mode 100644 index 0000000..edea87c --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/CreateScheduleResponse.java @@ -0,0 +1,11 @@ +package com.atlassian.opsgenie.schedule.model; + +import lombok.Data; + +@Data +public class CreateScheduleResponse { + + private DataModel Data; + private String requestId; + private double took; +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/DataModel.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/DataModel.java new file mode 100644 index 0000000..a900867 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/DataModel.java @@ -0,0 +1,23 @@ +package com.atlassian.opsgenie.schedule.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DataModel { + + private String Id; + private String name; + private String description; + private String timezone; + private boolean enabled; + private OwnerTeam ownerTeam; + @JsonProperty("rotations") + private List rotations; + + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/GetScheduleResponse.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/GetScheduleResponse.java new file mode 100644 index 0000000..f81a488 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/GetScheduleResponse.java @@ -0,0 +1,11 @@ +package com.atlassian.opsgenie.schedule.model; + +import lombok.Data; + +@Data +public class GetScheduleResponse { + + private DataModel Data; + private String requestId; + private double took; +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/ListScheduleResponse.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/ListScheduleResponse.java new file mode 100644 index 0000000..6a8d953 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/ListScheduleResponse.java @@ -0,0 +1,13 @@ +package com.atlassian.opsgenie.schedule.model; + +import java.util.List; +import lombok.Data; + +@Data +public class ListScheduleResponse { + + private List data; + private String requestId; + private Double took; + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/OwnerTeam.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/OwnerTeam.java new file mode 100644 index 0000000..5654b0b --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/OwnerTeam.java @@ -0,0 +1,11 @@ +package com.atlassian.opsgenie.schedule.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class OwnerTeam { + private String name; + private String id; +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Participant.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Participant.java new file mode 100644 index 0000000..e4e7355 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Participant.java @@ -0,0 +1,10 @@ +package com.atlassian.opsgenie.schedule.model; + +public class Participant { + + private String type; + private String id; + private String username; + private String name; + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Restriction.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Restriction.java new file mode 100644 index 0000000..b8dbcb6 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Restriction.java @@ -0,0 +1,10 @@ +package com.atlassian.opsgenie.schedule.model; + +public class Restriction { + + private String startHour; + private String endHour; + private String startMin; + private String endMin; + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Rotation.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Rotation.java new file mode 100644 index 0000000..c980eee --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/Rotation.java @@ -0,0 +1,23 @@ +package com.atlassian.opsgenie.schedule.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class Rotation { + + private String Id; + private String name; + private String startDate; + private String endDate; + private String type; + private int length; + @JsonProperty("participants") + private List participants; + private TimeRestriction timeRestriction; + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/TimeRestriction.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/TimeRestriction.java new file mode 100644 index 0000000..476fddf --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/TimeRestriction.java @@ -0,0 +1,13 @@ +package com.atlassian.opsgenie.schedule.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class TimeRestriction { + + private String type; + @JsonProperty("restrictions") + private List restrictions; + +} diff --git a/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/UpdateScheduleRequest.java b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/UpdateScheduleRequest.java new file mode 100644 index 0000000..d83e389 --- /dev/null +++ b/opsgenie_schedule/src/main/java/com/atlassian/opsgenie/schedule/model/UpdateScheduleRequest.java @@ -0,0 +1,18 @@ +package com.atlassian.opsgenie.schedule.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class UpdateScheduleRequest { + + private String Id; + private String name; + private String description; + private OwnerTeam ownerTeam; + private String timezone; + private boolean enabled; +} + diff --git a/pom.xml b/pom.xml index a95e576..036f4b5 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ opsgenie_user opsgenie_team opsgenie_integration + opsgenie_schedule diff --git a/schedule_template.yml b/schedule_template.yml new file mode 100644 index 0000000..ffbe7d1 --- /dev/null +++ b/schedule_template.yml @@ -0,0 +1,23 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: AWS SAM template for the Atlassian::Opsgenie::Schedule resource type + +Globals: + Function: + Timeout: 10 # docker start-up times can be long for SAM CLI + MemorySize: 256 + +Resources: + TypeFunction: + Type: AWS::Serverless::Function + Properties: + Handler: com.atlassian.opsgenie.schedule.HandlerWrapper::handleRequest + Runtime: java8 + CodeUri: opsgenie_schedule/target/atlassian-opsgenie-schedule-handler-1.0-SNAPSHOT.jar + + TestEntrypoint: + Type: AWS::Serverless::Function + Properties: + Handler: com.atlassian.opsgenie.schedule.HandlerWrapper::testEntrypoint + Runtime: java8 + CodeUri: opsgenie_schedule/target/atlassian-opsgenie-schedule-handler-1.0-SNAPSHOT.jar \ No newline at end of file