From bf99d752c1ed1b3fbd32aab3fd1649f34c8a970d Mon Sep 17 00:00:00 2001 From: Serhat Can Date: Mon, 12 Jun 2017 14:40:58 +0300 Subject: [PATCH] Initial commit --- .gitignore | 67 ++++++ LICENSE.md | 201 ++++++++++++++++++ README.md | 1 + pom.xml | 49 +++++ scenario-generator-agent/pom.xml | 58 +++++ .../ScenarioGeneratorAgentApplication.java | 19 ++ .../config/ScenarioServiceConfig.java | 44 ++++ .../EasyTravelConfigurationProperties.java | 41 ++++ .../properties/ScenarioConfigProperty.java | 38 ++++ .../controller/AgentRestController.java | 27 +++ .../controller/ScenarioRestController.java | 48 +++++ .../scenario/entity/BadgesScenario.java | 14 ++ .../scenario/entity/EasyTravelScenario.java | 44 ++++ .../service/BadgesScenarioService.java | 32 +++ .../service/EasyTravelScenarioService.java | 171 +++++++++++++++ .../scenario/service/ScenarioService.java | 55 +++++ .../main/resources/application-dev.properties | 22 ++ scenario-generator-common/pom.xml | 32 +++ .../scenarioGenerator/entity/Scenario.java | 120 +++++++++++ .../scenarioGenerator/util/JSON.java | 106 +++++++++ scenario-generator/pom.xml | 58 +++++ .../ScenarioGeneratorApplication.java | 31 +++ .../AgentsConfigurationProperties.java | 28 +++ .../controller/AgentRestController.java | 31 +++ .../RestResponseEntityExceptionHandler.java | 49 +++++ .../controller/ScenarioRestController.java | 60 ++++++ .../controller/dto/StartScenarioRequest.java | 21 ++ .../scenarioGenerator/entity/Agent.java | 84 ++++++++ .../entity/ScenarioStatus.java | 120 +++++++++++ .../exception/ErrorResponse.java | 40 ++++ .../exception/RestTemplateErrorHandler.java | 11 + .../service/agent/AgentService.java | 16 ++ .../ApplicationPropertiesAgentService.java | 38 ++++ .../scenario/OnlyOneInAllScenarioRunner.java | 163 ++++++++++++++ .../service/scenario/ScenarioRunner.java | 18 ++ .../service/scenario/ScenarioService.java | 80 +++++++ .../ScenarioAlreadyInProgressException.java | 21 ++ .../exception/ScenarioDoesNotExist.java | 12 ++ .../main/resources/application-dev.properties | 13 ++ .../src/main/resources/static/css/app.css | 171 +++++++++++++++ .../src/main/resources/static/favicon.ico | Bin 0 -> 1150 bytes .../src/main/resources/static/index.html | 62 ++++++ .../src/main/resources/static/js/app.js | 167 +++++++++++++++ .../src/main/resources/static/modal.html | 49 +++++ .../OnlyOneInAllScenarioRunnerTest.java | 126 +++++++++++ .../service/scenario/ScenarioServiceTest.java | 105 +++++++++ 46 files changed, 2763 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 pom.xml create mode 100644 scenario-generator-agent/pom.xml create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/ScenarioGeneratorAgentApplication.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/ScenarioServiceConfig.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/EasyTravelConfigurationProperties.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/ScenarioConfigProperty.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/AgentRestController.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/ScenarioRestController.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/BadgesScenario.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/EasyTravelScenario.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/BadgesScenarioService.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/EasyTravelScenarioService.java create mode 100644 scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/ScenarioService.java create mode 100644 scenario-generator-agent/src/main/resources/application-dev.properties create mode 100644 scenario-generator-common/pom.xml create mode 100644 scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Scenario.java create mode 100644 scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/util/JSON.java create mode 100644 scenario-generator/pom.xml create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/ScenarioGeneratorApplication.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/config/properties/AgentsConfigurationProperties.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/AgentRestController.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/RestResponseEntityExceptionHandler.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/ScenarioRestController.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/dto/StartScenarioRequest.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Agent.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/ScenarioStatus.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/ErrorResponse.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/RestTemplateErrorHandler.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/AgentService.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/ApplicationPropertiesAgentService.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunner.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioRunner.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioService.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioAlreadyInProgressException.java create mode 100644 scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioDoesNotExist.java create mode 100644 scenario-generator/src/main/resources/application-dev.properties create mode 100644 scenario-generator/src/main/resources/static/css/app.css create mode 100644 scenario-generator/src/main/resources/static/favicon.ico create mode 100644 scenario-generator/src/main/resources/static/index.html create mode 100644 scenario-generator/src/main/resources/static/js/app.js create mode 100644 scenario-generator/src/main/resources/static/modal.html create mode 100644 scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunnerTest.java create mode 100644 scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioServiceTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd4589f --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +### IntelliJ IDEA ### +.idea/ +*.iws +*.iml +*.ipr + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# custom + + +### Maven ### +.mvn/ + +application.properties + +target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +# custom +scripts/ \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..df5726c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 OpsGenie + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d673fb5 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# OpsGenie Playground Scenario Generator \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9553d0c --- /dev/null +++ b/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + com.opsgenie.playground + scenario-generator-parent + pom + 1.0-SNAPSHOT + + + 2.8.7 + 2.6.6 + + + + scenario-generator + scenario-generator-agent + scenario-generator-common + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + true + forked-path + @{project.version} + clean test + package + + + + + + \ No newline at end of file diff --git a/scenario-generator-agent/pom.xml b/scenario-generator-agent/pom.xml new file mode 100644 index 0000000..bdd1213 --- /dev/null +++ b/scenario-generator-agent/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + scenario-generator-agent + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 1.5.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + com.opsgenie.playground + scenario-generator-common + 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/ScenarioGeneratorAgentApplication.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/ScenarioGeneratorAgentApplication.java new file mode 100644 index 0000000..b347b44 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/ScenarioGeneratorAgentApplication.java @@ -0,0 +1,19 @@ +package com.opsgenie.playground.scenarioGeneratorAgent; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +@EnableAutoConfiguration +@SpringBootApplication +public class ScenarioGeneratorAgentApplication { + + public static void main(String[] args) { + SpringApplication.run(ScenarioGeneratorAgentApplication.class, args); + } + +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/ScenarioServiceConfig.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/ScenarioServiceConfig.java new file mode 100644 index 0000000..b5dbf95 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/ScenarioServiceConfig.java @@ -0,0 +1,44 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.config; + +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.config.properties.EasyTravelConfigurationProperties; +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.service.BadgesScenarioService; +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.service.EasyTravelScenarioService; +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.service.ScenarioService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +@Configuration +public class ScenarioServiceConfig { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private EasyTravelConfigurationProperties easyTravelConfigurationProperties; + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } + + @Bean + @ConditionalOnProperty(name = "scenario.application", havingValue = "easyTravel", matchIfMissing = true) + ScenarioService easyTravelScenarioService() { + return new EasyTravelScenarioService(restTemplate, easyTravelConfigurationProperties); + } + + @Bean + @ConditionalOnProperty(name = "scenario.application", havingValue = "badges") + ScenarioService badgesScenarioService() { + return new BadgesScenarioService(); + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/EasyTravelConfigurationProperties.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/EasyTravelConfigurationProperties.java new file mode 100644 index 0000000..4d9723e --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/EasyTravelConfigurationProperties.java @@ -0,0 +1,41 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +@Component +@ConfigurationProperties("easyTravel") +public class EasyTravelConfigurationProperties { + + private String apiUrl; + + private List availableScenarios = new ArrayList<>(); + + public String getApiUrl() { + return apiUrl; + } + + public void setApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + } + + public List getAvailableScenarios() { + return availableScenarios; + } + + public void setAvailableScenarios(List availableScenarios) { + this.availableScenarios = availableScenarios; + } + + public List getAvailableScenarioNames() { + return availableScenarios.stream().map(ScenarioConfigProperty::getName).collect(Collectors.toList()); + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/ScenarioConfigProperty.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/ScenarioConfigProperty.java new file mode 100644 index 0000000..9a7dce5 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/config/properties/ScenarioConfigProperty.java @@ -0,0 +1,38 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.config.properties; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +public class ScenarioConfigProperty { + + private String name; + + private int minTime = 60; + + private int maxTime = 180; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getMinTime() { + return minTime; + } + + public void setMinTime(int minTime) { + this.minTime = minTime; + } + + public int getMaxTime() { + return maxTime; + } + + public void setMaxTime(int maxTime) { + this.maxTime = maxTime; + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/AgentRestController.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/AgentRestController.java new file mode 100644 index 0000000..9d7c763 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/AgentRestController.java @@ -0,0 +1,27 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.controller; + +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.service.ScenarioService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +/** + * @author Serhat Can + * @version 08/06/17 + */ +@RestController +public class AgentRestController { + + private final ScenarioService scenarioService; + + @Autowired + public AgentRestController(ScenarioService scenarioService) { + this.scenarioService = scenarioService; + } + + @ResponseStatus(HttpStatus.OK) + @RequestMapping(value = "/reset", method = {RequestMethod.POST, RequestMethod.PUT}) + public void disableScenario() throws Exception { + scenarioService.reset(); + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/ScenarioRestController.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/ScenarioRestController.java new file mode 100644 index 0000000..80bffd8 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/controller/ScenarioRestController.java @@ -0,0 +1,48 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.controller; + +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.service.ScenarioService; + +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +@RestController +@RequestMapping("/scenarios") +public class ScenarioRestController { + + private final ScenarioService scenarioService; + + @Autowired + public ScenarioRestController(ScenarioService scenarioService) { + this.scenarioService = scenarioService; + } + + @RequestMapping(method = RequestMethod.GET) + public List getScenarios() throws Exception { + return scenarioService.getScenarios(); + } + + @ResponseStatus(HttpStatus.OK) + @RequestMapping(value = "/{scenario}/enable", method = {RequestMethod.POST, RequestMethod.PUT}) + public boolean enableScenario(@NotBlank @PathVariable("scenario") String scenarioName) throws Exception { + return scenarioService.enable(scenarioName); + } + + @ResponseStatus(HttpStatus.OK) + @RequestMapping(value = "/{scenario}/disable", method = {RequestMethod.POST, RequestMethod.PUT}) + public boolean disableScenario(@NotBlank @PathVariable("scenario") String scenarioName) throws Exception { + return scenarioService.disable(scenarioName); + } + +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/BadgesScenario.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/BadgesScenario.java new file mode 100644 index 0000000..fa733cb --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/BadgesScenario.java @@ -0,0 +1,14 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.entity; + +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +public class BadgesScenario extends Scenario { + + public BadgesScenario(String name) { + super(name); + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/EasyTravelScenario.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/EasyTravelScenario.java new file mode 100644 index 0000000..aeb29d9 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/entity/EasyTravelScenario.java @@ -0,0 +1,44 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.entity; + +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +public class EasyTravelScenario extends Scenario { + + private String area; + + private String group; + + public EasyTravelScenario(String name, String area, String group, String description) { + super(name, description); + this.area = area; + this.group = group; + } + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + @Override + public String toString() { + return "EasyTravelScenario{" + + "area='" + area + '\'' + + ", group='" + group + '\'' + + '}'; + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/BadgesScenarioService.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/BadgesScenarioService.java new file mode 100644 index 0000000..7ae19f1 --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/BadgesScenarioService.java @@ -0,0 +1,32 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.service; + +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.entity.BadgesScenario; + +import java.util.List; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +public class BadgesScenarioService implements ScenarioService { + + @Override + public List getScenarios() { + return null; + } + + @Override + public boolean enable(String scenarioName) { + return false; + } + + @Override + public boolean disable(String scenarioName) { + return false; + } + + @Override + public void reset() { + + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/EasyTravelScenarioService.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/EasyTravelScenarioService.java new file mode 100644 index 0000000..be653ae --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/EasyTravelScenarioService.java @@ -0,0 +1,171 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.service; + +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.entity.EasyTravelScenario; +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.config.properties.EasyTravelConfigurationProperties; +import com.opsgenie.playground.scenarioGeneratorAgent.scenario.config.properties.ScenarioConfigProperty; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +public class EasyTravelScenarioService implements ScenarioService { + + private static final Logger log = LoggerFactory.getLogger(EasyTravelScenarioService.class); + + private static final String CONFIGURATION_SERVICE = "/services/ConfigurationService"; + + private RestTemplate restTemplate; + + private DocumentBuilderFactory documentBuilderFactory; + + private XPathFactory xPathFactory; + + private String apiUrl; + + private Map availableScenarioConfigsMap; + private List availableScenarioNames; + + public EasyTravelScenarioService(RestTemplate restTemplate, EasyTravelConfigurationProperties conf) { + this.restTemplate = restTemplate; + this.xPathFactory = XPathFactory.newInstance(); + this.documentBuilderFactory = DocumentBuilderFactory.newInstance(); + + this.apiUrl = conf.getApiUrl(); + this.availableScenarioConfigsMap = conf.getAvailableScenarios().stream().collect(Collectors.toMap(ScenarioConfigProperty::getName, sc -> sc)); + this.availableScenarioNames = conf.getAvailableScenarioNames(); + } + + @Override + public List getScenarios() throws Exception { + List allAvailableScenarios = getAllScenarios(); + + List enabledScenarios = getEnabledPluginNames(); + + allAvailableScenarios.forEach(pl -> { + + if (enabledScenarios.contains(pl.getName())) { + pl.setEnabled(true); + } + }); + + return allAvailableScenarios; + } + + private List getAllScenarios() throws Exception { + String pluginNamesString = restTemplate.getForObject(apiUrl + CONFIGURATION_SERVICE + "/getAllPlugins", String.class); + List scenarios = getScenariosFromResponse(pluginNamesString, "/getAllPluginsResponse/return/text()"); + + final List availableScenarios = new ArrayList<>(); + + for (EasyTravelScenario scenario : scenarios) { + if (availableScenarioConfigsMap.containsKey(scenario.getName())) { + final ScenarioConfigProperty scenarioConfigProperty = availableScenarioConfigsMap.get(scenario.getName()); + scenario.setMaxTime(scenarioConfigProperty.getMaxTime()); + scenario.setMinTime(scenarioConfigProperty.getMinTime()); + + availableScenarios.add(scenario); + } + } + + return availableScenarios; + } + + private List getScenariosFromResponse(String objectString, String expression) throws Exception { + NodeList nodes = getNodeListForDocAndExpression(objectString, expression); + + List scenarios = new ArrayList<>(); + for (int i = 0; i < nodes.getLength(); i++) { + String nodeValue = nodes.item(i).getNodeValue(); + String[] values = nodeValue.split(":");// expected to return 4 value - no need to check + scenarios.add(new EasyTravelScenario(values[0], values[1], values[2], values.length > 3 ? values[3] : "")); + } + return scenarios; + } + + private NodeList getNodeListForDocAndExpression(String objectString, String expression) throws Exception { + DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder(); + InputSource source = new InputSource(new StringReader(objectString)); + Document doc = builder.parse(source); + XPath xpath = xPathFactory.newXPath(); + return (NodeList) xpath.compile(expression).evaluate(doc, XPathConstants.NODESET); + } + + private List getEnabledPluginNames() throws Exception { + String pluginNamesString = restTemplate.getForObject(apiUrl + CONFIGURATION_SERVICE + "/getEnabledPluginNames", String.class); + NodeList nodes = getNodeListForDocAndExpression(pluginNamesString, "/getEnabledPluginNamesResponse/return/text()"); + List pluginNames = new ArrayList<>(); + for (int i = 0; i < nodes.getLength(); i++) { + pluginNames.add(nodes.item(i).getNodeValue()); + } + + + return pluginNames.stream().filter(pluginName -> availableScenarioNames.contains(pluginName)).collect(Collectors.toList()); + } + + @Override + public boolean enable(String scenarioName) throws Exception { + return setScenarioEnabled(scenarioName, true); + } + + @Override + public boolean disable(String scenarioName) throws Exception { + return setScenarioEnabled(scenarioName, false); + } + + private boolean setScenarioEnabled(String scenarioName, boolean enabled) throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.set("Accept", MediaType.APPLICATION_JSON_VALUE); + + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(apiUrl + CONFIGURATION_SERVICE + "/setPluginEnabled") + .queryParam("name", scenarioName) + .queryParam("enabled", enabled); + + HttpEntity entity = new HttpEntity<>(headers); + + HttpEntity response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, entity, String.class); + + if (!((ResponseEntity) response).getStatusCode().is2xxSuccessful()) { + log.error("Response from plugin enable is not successful", response); + throw new HttpServerErrorException(((ResponseEntity) response).getStatusCode()); + } + + return enabled; + } + + @Override + public void reset() throws Exception { + log.info("Application context refresh called in EasyTravel scenario service. Should disable all available plugins now..."); + + final List allScenarios = getAllScenarios(); + + for (EasyTravelScenario scenario : allScenarios) { + disable(scenario.getName()); + } + } +} diff --git a/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/ScenarioService.java b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/ScenarioService.java new file mode 100644 index 0000000..0d3d65f --- /dev/null +++ b/scenario-generator-agent/src/main/java/com/opsgenie/playground/scenarioGeneratorAgent/scenario/service/ScenarioService.java @@ -0,0 +1,55 @@ +package com.opsgenie.playground.scenarioGeneratorAgent.scenario.service; + +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; + +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; + +import java.util.List; + +/** + * The interface Scenario service. + * + * @param the type parameter + * @author Serhat Can + * @version 31 /05/17 + */ +public interface ScenarioService { + + + /** + * Gets all available scenarios + * + * @return the scenarios + * @throws Exception the exception + */ + List getScenarios() throws Exception; + + /** + * Enable a scenario + * + * @param scenarioName the scenario name + * @return the boolean + * @throws Exception the exception + */ + boolean enable(String scenarioName) throws Exception; + + /** + * Disable a scenario + * + * @param scenarioName the scenario name + * @return the boolean + * @throws Exception the exception + */ + boolean disable(String scenarioName) throws Exception; + + /** + * As we currently do not keep state in a remote storage, we will need to reset/disable all the + * enabled scenarios if exists on application context refresh + * + * @throws Exception the exception + */ + @EventListener(ContextRefreshedEvent.class) + void reset() throws Exception; + +} diff --git a/scenario-generator-agent/src/main/resources/application-dev.properties b/scenario-generator-agent/src/main/resources/application-dev.properties new file mode 100644 index 0000000..416db76 --- /dev/null +++ b/scenario-generator-agent/src/main/resources/application-dev.properties @@ -0,0 +1,22 @@ +# Copy this to the same path as application.properties and update accordingly + +# server properties +server.port = 9008 + +# scenario configurations +scenario.application=easyTravel + +# EasyTravel config +easyTravel.apiUrl=http://localhost:8091 + +## min and max times are 1 and 3 minutes by default +easyTravel.availableScenarios[0].name=CPULoad +easyTravel.availableScenarios[0].minTime=20 +easyTravel.availableScenarios[0].maxTime=200 + +easyTravel.availableScenarios[1].name=CrashCouchDB +easyTravel.availableScenarios[2].name=CreditCardCheckError500 +easyTravel.availableScenarios[3].name=DatabaseSlowdown +easyTravel.availableScenarios[4].name=GarbageCollectionEvery10Seconds + +logging.level.=DEBUG \ No newline at end of file diff --git a/scenario-generator-common/pom.xml b/scenario-generator-common/pom.xml new file mode 100644 index 0000000..8c5a147 --- /dev/null +++ b/scenario-generator-common/pom.xml @@ -0,0 +1,32 @@ + + + + scenario-generator-parent + com.opsgenie.playground + 1.0-SNAPSHOT + + 4.0.0 + + scenario-generator-common + + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + compile + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + compile + + + + + \ No newline at end of file diff --git a/scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Scenario.java b/scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Scenario.java new file mode 100644 index 0000000..7f5c602 --- /dev/null +++ b/scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Scenario.java @@ -0,0 +1,120 @@ +package com.opsgenie.playground.scenarioGenerator.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * This class might be abstract later but it is used directly by scenario generator as a class. If + * we want to do something specific for apps later, we can make this abstract again, then agent + * applications need to know about the type of the scenario and cast accordingly + * + * @author Serhat Can + * @version 31/05/17 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Scenario { + + protected String name; + + protected String description; + + protected boolean enabled = false; + + protected int minTime = 60; + + protected int maxTime = 180; + + public Scenario() { + } + + public Scenario(String name) { + this.name = name; + } + + public Scenario(String name, String description) { + this.name = name; + this.description = description; + } + + public Scenario(String name, String description, boolean enabled, int minTime, int maxTime) { + this.name = name; + this.description = description; + this.enabled = enabled; + this.minTime = minTime; + this.maxTime = maxTime; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getMinTime() { + return minTime; + } + + public void setMinTime(int minTime) { + this.minTime = minTime; + } + + public int getMaxTime() { + return maxTime; + } + + public void setMaxTime(int maxTime) { + this.maxTime = maxTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Scenario scenario = (Scenario) o; + + if (enabled != scenario.enabled) return false; + if (minTime != scenario.minTime) return false; + if (maxTime != scenario.maxTime) return false; + if (name != null ? !name.equals(scenario.name) : scenario.name != null) return false; + return description != null ? description.equals(scenario.description) : scenario.description == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (description != null ? description.hashCode() : 0); + result = 31 * result + (enabled ? 1 : 0); + result = 31 * result + minTime; + result = 31 * result + maxTime; + return result; + } + + @Override + public String toString() { + return "Scenario{" + + "name='" + name + '\'' + + ", description='" + description + '\'' + + ", enabled=" + enabled + + ", minTime=" + minTime + + ", maxTime=" + maxTime + + '}'; + } +} diff --git a/scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/util/JSON.java b/scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/util/JSON.java new file mode 100644 index 0000000..1ba1ced --- /dev/null +++ b/scenario-generator-common/src/main/java/com/opsgenie/playground/scenarioGenerator/util/JSON.java @@ -0,0 +1,106 @@ +package com.opsgenie.playground.scenarioGenerator.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * @author Serhat Can + * @version 03/06/17 + */ +public class JSON { + + private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper(); + private static final ObjectMapper FAIL_IGNORE_MAPPER = new ObjectMapper().configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + + public static Map parse(String json) throws IOException { + JsonParser parser = DEFAULT_MAPPER.getFactory().createParser(json); + Map result = _parse(parser); + parser.close(); + return result; + } + + public static Map parse(byte[] json) throws IOException { + JsonParser parser = DEFAULT_MAPPER.getFactory().createParser(json); + Map result = _parse(parser); + parser.close(); + return result; + } + + private static Map _parse(JsonParser parser) throws IOException { + TypeReference> typeRef = new TypeReference>() { + }; + ObjectReader reader = DEFAULT_MAPPER.reader(typeRef); + Map result = reader.readValue(parser); + if (result == null) { + result = new HashMap<>(); + } + return result; + } + + public static List parseAsList(String json, Class clazz) throws IOException { + JavaType type = DEFAULT_MAPPER.getTypeFactory(). + constructCollectionType(List.class, clazz); + + return DEFAULT_MAPPER.readValue(json, type); + } + + public static T convertValue(Object object, TypeReference typeRef) { + return DEFAULT_MAPPER.convertValue(object, typeRef); + } + + public static T parse(String json, Class klass) throws IOException { + ObjectReader reader = DEFAULT_MAPPER.reader(klass); + return reader.readValue(json); + } + + public static T parse(byte[] json, Class klass) throws IOException { + ObjectReader reader = DEFAULT_MAPPER.reader(klass); + return reader.readValue(json); + } + + public static String toJson(Object jsonContent) throws IOException { + ObjectWriter writer = DEFAULT_MAPPER.writer(); + return writer.writeValueAsString(jsonContent); + } + + + public static Map toMap(Object object) { + return DEFAULT_MAPPER.convertValue(object, Map.class); + } + + public static Map toMapIgnoreErrors(Object object) { + return FAIL_IGNORE_MAPPER.convertValue(object, Map.class); + } + + public static String toJsonIgnoreErrors(Object jsonContent) throws IOException { + ObjectWriter writer = FAIL_IGNORE_MAPPER.writer(); + return writer.writeValueAsString(jsonContent); + } + + public static String toPrettyJsonIgnoreErrors(Object jsonContent) throws IOException { + ObjectWriter writer = FAIL_IGNORE_MAPPER.writer(); + return writer.withDefaultPrettyPrinter().writeValueAsString(jsonContent); + } + + public static String toPrettyJson(Object jsonContent) throws IOException { + ObjectWriter writer = DEFAULT_MAPPER.writer(); + return writer.withDefaultPrettyPrinter().writeValueAsString(jsonContent); + } + + public static byte[] toJsonAsBytes(Object jsonContent) throws IOException { + ObjectWriter writer = DEFAULT_MAPPER.writer(); + return writer.writeValueAsBytes(jsonContent); + } + +} diff --git a/scenario-generator/pom.xml b/scenario-generator/pom.xml new file mode 100644 index 0000000..48309b2 --- /dev/null +++ b/scenario-generator/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + + scenario-generator + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 1.5.3.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + com.opsgenie.playground + scenario-generator-common + 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-devtools + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/ScenarioGeneratorApplication.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/ScenarioGeneratorApplication.java new file mode 100644 index 0000000..d34a60d --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/ScenarioGeneratorApplication.java @@ -0,0 +1,31 @@ +package com.opsgenie.playground.scenarioGenerator; + +import com.opsgenie.playground.scenarioGenerator.exception.RestTemplateErrorHandler; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.client.RestTemplate; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +@EnableAutoConfiguration +@SpringBootApplication +@EnableScheduling +public class ScenarioGeneratorApplication { + + public static void main(String[] args) { + SpringApplication.run(ScenarioGeneratorApplication.class, args); + } + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.errorHandler(new RestTemplateErrorHandler()).build(); + } + +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/config/properties/AgentsConfigurationProperties.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/config/properties/AgentsConfigurationProperties.java new file mode 100644 index 0000000..bb902d0 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/config/properties/AgentsConfigurationProperties.java @@ -0,0 +1,28 @@ +package com.opsgenie.playground.scenarioGenerator.config.properties; + +import com.opsgenie.playground.scenarioGenerator.entity.Agent; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +@Component +@ConfigurationProperties +public class AgentsConfigurationProperties { + + private Map agents = new HashMap<>(); + + public Map getAgents() { + return agents; + } + + public void setAgents(Map agents) { + this.agents = agents; + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/AgentRestController.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/AgentRestController.java new file mode 100644 index 0000000..4c84d8a --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/AgentRestController.java @@ -0,0 +1,31 @@ +package com.opsgenie.playground.scenarioGenerator.controller; + +import com.opsgenie.playground.scenarioGenerator.service.agent.AgentService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +@RestController +@RequestMapping("/agents") +public class AgentRestController { + + private final AgentService agentService; + + @Autowired + public AgentRestController(AgentService agentService) { + this.agentService = agentService; + } + + @RequestMapping(method = RequestMethod.GET) + public List getAgents() { + return agentService.getAgents(); + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/RestResponseEntityExceptionHandler.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/RestResponseEntityExceptionHandler.java new file mode 100644 index 0000000..7a73340 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/RestResponseEntityExceptionHandler.java @@ -0,0 +1,49 @@ +package com.opsgenie.playground.scenarioGenerator.controller; + +import com.opsgenie.playground.scenarioGenerator.exception.ErrorResponse; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.net.ConnectException; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author Serhat Can + * @version 09/06/17 + */ +@ControllerAdvice +public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + @ResponseStatus(HttpStatus.FAILED_DEPENDENCY) + @ExceptionHandler(HttpClientErrorException.class) + public ResponseEntity handleStatusExceptions(HttpClientErrorException e) { + return ResponseEntity.status(HttpStatus.FAILED_DEPENDENCY) + .contentType(MediaType.APPLICATION_JSON) + .body(new ErrorResponse(e.getMessage())); + } + + @ResponseStatus(HttpStatus.FAILED_DEPENDENCY) + @ExceptionHandler(ConnectException.class) + public ResponseEntity handleConnectExceptions(ResourceAccessException e) { + return ResponseEntity.status(HttpStatus.FAILED_DEPENDENCY) + .contentType(MediaType.APPLICATION_JSON) + .body(new ErrorResponse(e.getMessage())); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity exceptionHandler(HttpServletRequest request, Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .contentType(MediaType.APPLICATION_JSON) + .body(new ErrorResponse(e.getMessage())); + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/ScenarioRestController.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/ScenarioRestController.java new file mode 100644 index 0000000..76e7b0f --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/ScenarioRestController.java @@ -0,0 +1,60 @@ +package com.opsgenie.playground.scenarioGenerator.controller; + +import com.opsgenie.playground.scenarioGenerator.controller.dto.StartScenarioRequest; +import com.opsgenie.playground.scenarioGenerator.entity.ScenarioStatus; +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; +import com.opsgenie.playground.scenarioGenerator.service.scenario.ScenarioRunner; +import com.opsgenie.playground.scenarioGenerator.service.scenario.ScenarioService; + +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * @author Serhat Can + * @version 31/05/17 + */ +@RestController +@RequestMapping("/agents/{agentName}") +public class ScenarioRestController { + + private final ScenarioService scenarioService; + + private final ScenarioRunner scenarioRunner; + + @Autowired + public ScenarioRestController(ScenarioService scenarioService, ScenarioRunner scenarioRunner) { + this.scenarioService = scenarioService; + this.scenarioRunner = scenarioRunner; + } + + @RequestMapping("/scenarios") + public List getAgentsScenarios(@NotBlank @PathVariable("agentName") String agentName) throws Exception { + + return scenarioService.getScenarios(agentName); + } + + @RequestMapping("/scenarioStatus") + public ScenarioStatus getAgentsScenarioStatus(@NotBlank @PathVariable("agentName") String agentName) throws Exception { + + return scenarioRunner.status(agentName); + } + + @ResponseStatus(HttpStatus.OK) + @RequestMapping(value = "/scenarios/{scenarioName}/start", method = {RequestMethod.POST, RequestMethod.PUT}) + public ScenarioStatus startScenario(@NotBlank @PathVariable("agentName") String agentName, + @NotBlank @PathVariable("scenarioName") String scenarioName, + @RequestBody StartScenarioRequest request) throws Exception { + + return scenarioRunner.start(agentName, scenarioName, request.getRequestedTime()); + } + +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/dto/StartScenarioRequest.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/dto/StartScenarioRequest.java new file mode 100644 index 0000000..e4c6f88 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/controller/dto/StartScenarioRequest.java @@ -0,0 +1,21 @@ +package com.opsgenie.playground.scenarioGenerator.controller.dto; + +/** + * Created by Serhat Can + */ +public class StartScenarioRequest { + + private int requestedTime; + + public StartScenarioRequest(int requestedTime) { + this.requestedTime = requestedTime; + } + + public int getRequestedTime() { + return requestedTime; + } + + public void setRequestedTime(int requestedTime) { + this.requestedTime = requestedTime; + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Agent.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Agent.java new file mode 100644 index 0000000..430347f --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/Agent.java @@ -0,0 +1,84 @@ +package com.opsgenie.playground.scenarioGenerator.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +public class Agent { + + private String name; + + @JsonIgnore + private String url; + + private String description; + + public Agent() { + } + + public Agent(String name, String url) { + this.name = name; + this.url = url; + } + + public Agent(String name, String url, String description) { + this.name = name; + this.url = url; + this.description = description; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Agent agent = (Agent) o; + + if (name != null ? !name.equals(agent.name) : agent.name != null) return false; + if (url != null ? !url.equals(agent.url) : agent.url != null) return false; + return description != null ? description.equals(agent.description) : agent.description == null; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (url != null ? url.hashCode() : 0); + result = 31 * result + (description != null ? description.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Agent{" + + "name='" + name + '\'' + + ", url='" + url + '\'' + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/ScenarioStatus.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/ScenarioStatus.java new file mode 100644 index 0000000..6138c7f --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/entity/ScenarioStatus.java @@ -0,0 +1,120 @@ +package com.opsgenie.playground.scenarioGenerator.entity; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +public class ScenarioStatus { + + public static final String AVAILABLE = "Available"; + public static final String IN_PROGRESS = "In progress"; + + private boolean running = false; + + private String status = ""; + + private Scenario runningScenario; + + private int remainingSeconds; + + private int requestedSeconds; + + public ScenarioStatus() { + } + + public static ScenarioStatus createAvailableStatus() { + return new ScenarioStatus(AVAILABLE); + } + + public static ScenarioStatus createInProgressStatus(Scenario runningScenario, int remainingSeconds, int requestedSeconds) { + return new ScenarioStatus(true, IN_PROGRESS,runningScenario, remainingSeconds, requestedSeconds); + } + + private ScenarioStatus(String status) { + this.status = status; + } + + private ScenarioStatus(boolean running, String status, Scenario runningScenario, int remainingSeconds, int requestedSeconds) { + this.running = running; + this.status = status; + this.runningScenario = runningScenario; + this.remainingSeconds = remainingSeconds; + this.requestedSeconds = requestedSeconds; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Scenario getRunningScenario() { + return runningScenario; + } + + public void setRunningScenario(Scenario runningScenario) { + this.runningScenario = runningScenario; + } + + public int getRemainingSeconds() { + return remainingSeconds; + } + + public void setRemainingSeconds(int remainingSeconds) { + this.remainingSeconds = remainingSeconds; + } + + public boolean isRunning() { + return running; + } + + public void setRunning(boolean running) { + this.running = running; + } + + public int getRequestedSeconds() { + return requestedSeconds; + } + + public ScenarioStatus setRequestedSeconds(int requestedSeconds) { + this.requestedSeconds = requestedSeconds; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ScenarioStatus that = (ScenarioStatus) o; + + if (running != that.running) return false; + if (remainingSeconds != that.remainingSeconds) return false; + if (requestedSeconds != that.requestedSeconds) return false; + if (status != null ? !status.equals(that.status) : that.status != null) return false; + return runningScenario != null ? runningScenario.equals(that.runningScenario) : that.runningScenario == null; + } + + @Override + public int hashCode() { + int result = (running ? 1 : 0); + result = 31 * result + (status != null ? status.hashCode() : 0); + result = 31 * result + (runningScenario != null ? runningScenario.hashCode() : 0); + result = 31 * result + remainingSeconds; + result = 31 * result + requestedSeconds; + return result; + } + + @Override + public String toString() { + return "ScenarioStatus{" + + "running=" + running + + ", status='" + status + '\'' + + ", runningScenario=" + runningScenario + + ", remainingSeconds=" + remainingSeconds + + ", requestedSeconds=" + requestedSeconds + + '}'; + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/ErrorResponse.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/ErrorResponse.java new file mode 100644 index 0000000..9c8d640 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/ErrorResponse.java @@ -0,0 +1,40 @@ +package com.opsgenie.playground.scenarioGenerator.exception; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * @author Serhat Can + * @version 09/06/17 + */ +public class ErrorResponse { + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Integer code; + + private String message; + + public ErrorResponse(String message, int code) { + this.code = code; + this.message = message; + } + + public ErrorResponse(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/RestTemplateErrorHandler.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/RestTemplateErrorHandler.java new file mode 100644 index 0000000..f25f6c3 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/exception/RestTemplateErrorHandler.java @@ -0,0 +1,11 @@ +package com.opsgenie.playground.scenarioGenerator.exception; + +import org.springframework.web.client.DefaultResponseErrorHandler; + +/** + * @author Serhat Can + * @version 09/06/17 + */ +public class RestTemplateErrorHandler extends DefaultResponseErrorHandler { + +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/AgentService.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/AgentService.java new file mode 100644 index 0000000..369643e --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/AgentService.java @@ -0,0 +1,16 @@ +package com.opsgenie.playground.scenarioGenerator.service.agent; + +import com.opsgenie.playground.scenarioGenerator.entity.Agent; + +import java.util.List; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +public interface AgentService { + + List getAgents(); + + Agent getAgent(String agentName); +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/ApplicationPropertiesAgentService.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/ApplicationPropertiesAgentService.java new file mode 100644 index 0000000..8f9c21c --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/agent/ApplicationPropertiesAgentService.java @@ -0,0 +1,38 @@ +package com.opsgenie.playground.scenarioGenerator.service.agent; + +import com.opsgenie.playground.scenarioGenerator.config.properties.AgentsConfigurationProperties; +import com.opsgenie.playground.scenarioGenerator.entity.Agent; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +@Service +public class ApplicationPropertiesAgentService implements AgentService { + + private final Map agentMap; + private final List agentList; + + @Autowired + public ApplicationPropertiesAgentService(AgentsConfigurationProperties agentsConfigurationProperties) { + agentMap = agentsConfigurationProperties.getAgents(); + agentList = agentMap.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toList()); + } + + @Override + public List getAgents() { + return agentList; + } + + @Override + public Agent getAgent(String agentName) { + return agentMap.get(agentName); + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunner.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunner.java new file mode 100644 index 0000000..ce2c506 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunner.java @@ -0,0 +1,163 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario; + +import com.opsgenie.playground.scenarioGenerator.entity.ScenarioStatus; +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; + +import com.opsgenie.playground.scenarioGenerator.service.scenario.exception.ScenarioDoesNotExist; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static java.lang.Math.toIntExact; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +@Service +public class OnlyOneInAllScenarioRunner implements ScenarioRunner { + + private static final Logger log = LoggerFactory.getLogger(OnlyOneInAllScenarioRunner.class); + + private static final int SCENARIO_CHECK_INTERVAL = 1000; + + private Map runningScenariosMap = new HashMap<>(); + + private final ScenarioService scenarioService; + + @Autowired + public OnlyOneInAllScenarioRunner(ScenarioService scenarioService) { + this.scenarioService = scenarioService; + } + + @Override + public ScenarioStatus status(String agentName) { + if (runningScenariosMap.containsKey(agentName)) { + ScenarioWrapper running = runningScenariosMap.get(agentName); + return ScenarioStatus.createInProgressStatus(running.getScenario() , running.getRemainingSeconds(), running.getRequestedSeconds()); + } else { + return ScenarioStatus.createAvailableStatus(); + } + } + + @Scheduled(fixedRate = SCENARIO_CHECK_INTERVAL) + public void checkAndReleaseScenariosOnTime() { + + for(Map.Entry entry: runningScenariosMap.entrySet()) { + String agentName = entry.getKey(); + ScenarioWrapper scw = entry.getValue(); + if (scw.getRemainingSeconds() <= 0) { + scenarioService.disableScenario(agentName, scw.getScenario().getName()); + runningScenariosMap.remove(agentName); + log.info("Scenario '" + scw.getScenario().getName() + "' is finished. " + scw.toString()); + } + } + } + + @Override + public ScenarioStatus start(String agentName, String scenarioName, int requestedTime) throws ScenarioDoesNotExist, IOException { + final Scenario scenario = scenarioService.getScenario(agentName, scenarioName); + + if (runningScenariosMap.containsKey(agentName)) { + ScenarioWrapper running = runningScenariosMap.get(agentName); + return ScenarioStatus.createInProgressStatus(running.getScenario() , running.getRemainingSeconds(), running.getRequestedSeconds()); + } + + scenarioService.enableScenario(agentName, scenarioName); + + final int finalRequestedTime = validateAndGetRequestedTime(scenario, requestedTime); + final long currentTimeInSeconds = getCurrentTimeInSec(); + + ScenarioWrapper running = createNewScenarioWrapper(scenario, finalRequestedTime, currentTimeInSeconds); + + runningScenariosMap.put(agentName, running); + + return ScenarioStatus.createInProgressStatus(running.getScenario() , running.getRemainingSeconds(), running.getRequestedSeconds()); + } + + private ScenarioWrapper createNewScenarioWrapper(Scenario scenario, int requestedTime, long currentTimeInSec) { + ScenarioWrapper scenarioWrapper = new ScenarioWrapper(); + scenarioWrapper.setScenario(scenario); + scenarioWrapper.setRequestedSeconds(requestedTime); + scenarioWrapper.setFinishTime(currentTimeInSec + requestedTime); + scenarioWrapper.setStartTime(currentTimeInSec); + return scenarioWrapper; + } + + private int validateAndGetRequestedTime(Scenario scenario, int requestedTime) { + if(scenario.getMinTime() >= requestedTime) { + return scenario.getMinTime(); + } else if (requestedTime >= scenario.getMaxTime()) { + return scenario.getMaxTime(); + } else { + return requestedTime; + } + } + + private static long getCurrentTimeInSec() { + return System.currentTimeMillis() / 1000; + } + + static class ScenarioWrapper { + private Scenario scenario; + private int requestedSeconds; + private long startTime; + private long finishTime; + + public Scenario getScenario() { + return scenario; + } + + public void setScenario(Scenario scenario) { + this.scenario = scenario; + } + + public int getRequestedSeconds() { + return requestedSeconds; + } + + public void setRequestedSeconds(int requestedSeconds) { + this.requestedSeconds = requestedSeconds; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getFinishTime() { + return finishTime; + } + + public void setFinishTime(long finishTime) { + this.finishTime = finishTime; + } + + /** + * Important! + */ + public int getRemainingSeconds() { + return toIntExact(finishTime - getCurrentTimeInSec()); + } + + @Override + public String toString() { + return "ScenarioWrapper{" + + "scenario=" + scenario + + ", requestedSeconds=" + requestedSeconds + + ", startTime=" + startTime + + ", finishTime=" + finishTime + + '}'; + } + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioRunner.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioRunner.java new file mode 100644 index 0000000..52b8a92 --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioRunner.java @@ -0,0 +1,18 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario; + +import com.opsgenie.playground.scenarioGenerator.entity.ScenarioStatus; +import com.opsgenie.playground.scenarioGenerator.service.scenario.exception.ScenarioDoesNotExist; + +import java.io.IOException; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +public interface ScenarioRunner { + + ScenarioStatus status(String agentName); + + ScenarioStatus start(String agentName, String scenarioName, int requestedTime) throws ScenarioDoesNotExist, IOException; + +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioService.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioService.java new file mode 100644 index 0000000..fe234af --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioService.java @@ -0,0 +1,80 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario; + +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; +import com.opsgenie.playground.scenarioGenerator.service.agent.AgentService; +import com.opsgenie.playground.scenarioGenerator.service.scenario.exception.ScenarioDoesNotExist; +import com.opsgenie.playground.scenarioGenerator.util.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +@Service +public class ScenarioService { + + private static final Logger log = LoggerFactory.getLogger(ScenarioService.class); + + private final AgentService agentService; + + private final RestTemplate restTemplate; + + @Autowired + public ScenarioService(AgentService agentService, RestTemplate restTemplate) { + this.agentService = agentService; + this.restTemplate = restTemplate; + } + + public List getScenarios(String agentName) throws IOException { + String scenariosString = restTemplate.getForObject(getAgentUrlFromName(agentName) + "/scenarios", String.class); + return JSON.parseAsList(scenariosString, Scenario.class); + } + + private String getAgentUrlFromName(String agentName) { + return agentService.getAgent(agentName).getUrl(); + } + + /** + * TODO serhat: + * This service gets all scenarios and filters it. This should change later and agents should + * have an endpoint that provides specific scenario details + */ + public Scenario getScenario(String agentName, String scenarioName) throws IOException, ScenarioDoesNotExist { + final List scenarios = getScenarios(agentName); + final Optional scenarioOptional = scenarios.stream().filter(sc -> sc.getName().equalsIgnoreCase(scenarioName)).findFirst(); + if (!scenarioOptional.isPresent()) { + throw new ScenarioDoesNotExist("Scenario '" + scenarioName + "' does not exist."); + } + + return scenarioOptional.get(); + } + + public void enableScenario(String agentName, String scenarioName) { + log.info("Enabling scenario '" + scenarioName + "' for '" + agentName + "' agent."); + restTemplate.postForLocation(getAgentUrlFromName(agentName) + "/scenarios/" + scenarioName + "/enable", String.class); + } + + public void disableScenario(String agentName, String scenarioName) { + log.info("Disabling scenario '" + scenarioName + "' for '" + agentName + "' agent."); + restTemplate.postForLocation(getAgentUrlFromName(agentName) + "/scenarios/" + scenarioName + "/disable", String.class); + } + + @EventListener(ContextRefreshedEvent.class) + void reset() { + agentService.getAgents().forEach(agent -> { + log.info("Requesting to reset agent [" + agent.getName() + "] scenarios."); + restTemplate.postForLocation(agent.getUrl() + "/reset", String.class); + }); + } +} \ No newline at end of file diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioAlreadyInProgressException.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioAlreadyInProgressException.java new file mode 100644 index 0000000..775b87c --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioAlreadyInProgressException.java @@ -0,0 +1,21 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario.exception; + +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +public class ScenarioAlreadyInProgressException extends Exception { + + private Scenario runningScenario; + + public ScenarioAlreadyInProgressException(String message, Scenario scenario) { + super(message); + this.runningScenario = scenario; + } + + public Scenario getRunningScenario() { + return runningScenario; + } +} diff --git a/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioDoesNotExist.java b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioDoesNotExist.java new file mode 100644 index 0000000..973cd8a --- /dev/null +++ b/scenario-generator/src/main/java/com/opsgenie/playground/scenarioGenerator/service/scenario/exception/ScenarioDoesNotExist.java @@ -0,0 +1,12 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario.exception; + +/** + * @author Serhat Can + * @version 02/06/17 + */ +public class ScenarioDoesNotExist extends Exception { + + public ScenarioDoesNotExist(String message) { + super(message); + } +} diff --git a/scenario-generator/src/main/resources/application-dev.properties b/scenario-generator/src/main/resources/application-dev.properties new file mode 100644 index 0000000..b98b0b1 --- /dev/null +++ b/scenario-generator/src/main/resources/application-dev.properties @@ -0,0 +1,13 @@ +# Copy this to the same path as application.properties and update accordingly + +# server properties +server.port = 9009 + +# agents configuration - agent names can be like agent1 if you want +agents.easyTravel.name=easyTravel +agents.easyTravel.description=EasyTravel helps you travel the world +agents.easyTravel.url=http://localhost:9008 + +agents.badges.name=badges +agents.badges.description=Engage and show your skills to your coworkers +agents.badges.url=http://localhost:9010 \ No newline at end of file diff --git a/scenario-generator/src/main/resources/static/css/app.css b/scenario-generator/src/main/resources/static/css/app.css new file mode 100644 index 0000000..3a02ea5 --- /dev/null +++ b/scenario-generator/src/main/resources/static/css/app.css @@ -0,0 +1,171 @@ + +[ng\:cloak], [ng-cloak], .ng-cloak { + display: none !important; +} + +ul.error { + list-style: none; + color: #FF0000; +} + +#plugin { + padding-top: 10px; + padding-bottom: 10px; +} + +#plugin .name { + font-weight: 600; + font-size: large; +} + +#plugin .description { + font-size: small; +} + +.scenario-status { + color: red; +} + +.inline-alert.alert { + display: inline; + vertical-align: middle; + margin-left: 8px; + background-color: transparent; + border: none; + padding: 0; + margin-bottom: 10px; + font-size: 12px; +} + +.inline-alert.alert { + display: inline; + vertical-align: middle; + margin-left: 8px; + background-color: transparent; + border: none; + padding: 0; + margin-bottom: 10px; +} + +.range-input { + width: 80px; + display: inline-block; +} + +.every-part div { + padding-bottom: 10px; +} + +.every-part div:last-child { + padding-bottom: 0px; +} + +.with-counter { + padding-left: 90px; + position: relative; + text-align: left; +} + +.with-counter .inline-alert { + width: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: 0; + height: 32px; +} + +.with-counter .inline-alert i.reg { + width: 1px; + height: 32px; + display: inline-block; + vertical-align: middle; + +} + +.with-counter .inline-alert b { + display: inline-block; + vertical-align: middle; + width: 98%; + height: auto; + font-weight: 400; +} + +.with-counter input { + position: absolute; + left: 0; + top: 0; + width: 80px; +} + +b.label-text { + display: inline-block; + width: 100%; + margin-bottom: 2px; +} + +b.label-text span { + font-weight: 300; + font-style: italic; + color: #989898; + margin-left: 4px; +} + +b.label-text span:before { + content: '('; +} + +b.label-text span:after { + content: ')'; +} + +button.btn-primary { + background-color: #FF9104; + border: 1px solid #f18700; +} + +button.btn-primary:hover, +button.btn-primary:active { + background-color: #f18700; + border: 1px solid #f18700; +} + +.sc-state { + width: 100%; + max-width: 500px; + height: 100px; + border: 1px solid #dedede; + background-color: #f9f9f9; + border-radius: 4px; + margin-top: 5px; + -webkit-box-shadow: 0px 2px 6px 0px rgba(87, 87, 87, 0.18); + -moz-box-shadow: 0px 2px 6px 0px rgba(87, 87, 87, 0.18); + box-shadow: 0px 2px 6px 0px rgba(87, 87, 87, 0.18); +} + +.sc-state .scenario-status { + padding: 10px; + color: #409664; +} + +.sc-state .scenario-result { + padding: 10px; +} + +.sc-state .sep-div { + width: 100%; + height: 1px; + border-top: 1px solid #dedede; +} + +.sc-state.running-state { + border: 1px solid #b2ccac; + background-color: #f2f7f0; +} + +.sc-state.running-state .sep-div { + border-top: 1px solid #b2ccac; +} + +hr { + margin-bottom: 0; +} diff --git a/scenario-generator/src/main/resources/static/favicon.ico b/scenario-generator/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7ee500bb246090fc1f105ca3ff695b8688be808e GIT binary patch literal 1150 zcmah|&r4KM6uyJ4%T+7Oi54n|Ny?zKDl&^i3L=nJqJ~i5Qb{ZgH@Y#ioSZN)eN$ov z7O0D;v1n1)s`m3QxSq}D_r3e>`;~)lICI`P=ljk*_nh;L$?#WOYxtiv!zYY6ZH#H8 z&_$2+QyIh9aZlYGa+Ct4OsNE-clmPF_>7&Tc=gYKr{96mFTh#8(}&yZKz<)+&il3} zY&=iE{UxlEo_D~+H{jk!;K4SKosVL~2pi_$yVvr{#)Wg|o?*W83lUGY^0zMe+EWd* za}6(m@vlI~GH|m9^u7lk?f{Q@{v-NFc7YpSly{7GYg+Vk_`SNi3atOLxy8Nv0cd}1 z?Z%f@>s%+rzrc0oqwV)ub=<5L%^<;_+5;|dA1)SvF3#r82B4g2oVdXbXeYz?!f!ik_4yX(saRVu<|% zpMbC35BK*P&$`ck_i=YGy|Mc=L_5Y!>~AbbScmqeZ`024%G(6@zwbwU_wbDH`{j;Q e*atTwzgG_D^uZ}(N;SqzW~?}WHSKWXh + + + OpsGenie Playground - Scenario Generator + + + + + + + + +
+

Scenario Generator

+
+
+
+

Status: {{$ctrl.error.status}}

+

Message: {{$ctrl.error.message}}

+
+ +
+

{{agent.name}}

{{agent.description}} + +
+
{{agent.scenarioStatus.runningScenario.name}} scenario is in progress!
+
+
+ Remaining seconds: {{agent.scenarioStatus.remainingSeconds}} + + +
+
+ +
+
Available. Select and run a scenario!
+
+
+ +
+
+
+
+ +
+ + + + + + + + + + \ No newline at end of file diff --git a/scenario-generator/src/main/resources/static/js/app.js b/scenario-generator/src/main/resources/static/js/app.js new file mode 100644 index 0000000..5ce2434 --- /dev/null +++ b/scenario-generator/src/main/resources/static/js/app.js @@ -0,0 +1,167 @@ +angular.module('scenarioGenerator', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']); + +angular.module('scenarioGenerator').factory('scenarioService', function ($http) { + + var scenarioService = {}; + + scenarioService.getAgents = function () { + return $http.get('/agents'); + }; + + scenarioService.getAgentStatus = function (agentName) { + return $http.get('/agents/' + agentName + '/scenarioStatus'); + }; + + scenarioService.getScenarios = function (agentName) { + return $http.get('/agents/' + agentName + '/scenarios'); + }; + + scenarioService.startScenario = function (agentName, scenarioName, payload) { + return $http.post('/agents/' + agentName + '/scenarios/' + scenarioName + '/start', payload); + }; + + return scenarioService; +}); + +angular.module('scenarioGenerator').controller('scenarioCtrl', function ($scope, $http, $uibModal, $log, $document, $interval, scenarioService) { + + var $ctrl = this; + + // TODO: implement proper error handling and avoid duplicates + var handleErrorResponse = function (response) { + $log.error("Error response:", response); + $ctrl.error = response.data; + $ctrl.error.status = response.status; + }; + + var getAndSetScenarioStatusOfAnAgent = function (agent) { + scenarioService.getAgentStatus(agent.name) + .then(function success(response) { + agent.scenarioStatus = response.data; + agent.scenarioStatus.increasingSeconds = agent.scenarioStatus.requestedSeconds - agent.scenarioStatus.remainingSeconds; + + $log.debug("Scenario status: ", agent.scenarioStatus); + + //register timer for count down view on ui + if (agent.scenarioStatus.running) { + var stop = $interval(function () { + if (agent.scenarioStatus.remainingSeconds <= 0) { + $interval.cancel(stop); + agent.scenarioStatus.running = false; + } else { + agent.scenarioStatus.remainingSeconds--; + } + + }, 1000); + + // for progress bar + $interval(function () { + agent.scenarioStatus.increasingSeconds = agent.scenarioStatus.increasingSeconds + 0.05; + }, 50, agent.scenarioStatus.requestedSeconds * 20); + } + + }, handleErrorResponse); + }; + + var init = function () { + scenarioService.getAgents() + .then(function success(response) { + $ctrl.agents = response.data; + + $ctrl.agents.forEach(function (agent) { + // TODO: get this with one request later + getAndSetScenarioStatusOfAnAgent(agent); + }) + + }, handleErrorResponse); + }; + + init(); + + $ctrl.openStartScenarioModal = function (selectedAgent, parentSelector) { + var modalInstance = $uibModal.open({ + animation: true, + ariaLabelledBy: 'modal-title', + ariaDescribedBy: 'modal-body', + templateUrl: 'modal.html', + controller: 'StartScenarioModalCtrl', + controllerAs: '$mdl', + resolve: { + agent: function () { + return selectedAgent; + } + } + }); + + modalInstance.result.then(function (modalsAgent) { + + $ctrl.agents.forEach(function (agent) { + if(agent.name === modalsAgent.name) { + getAndSetScenarioStatusOfAnAgent(agent); + } + }); + }, function () { + $log.debug("Removing selected scenario if exists"); + $ctrl.agents.forEach(function (sc) { sc.selectedScenario = null; }) + }); + }; + +}); + +angular.module('scenarioGenerator').controller('StartScenarioModalCtrl', function ($uibModalInstance, $log, agent, scenarioService) { + var $mdl = this; + + $mdl.agent = agent; + + var handleErrorResponse = function (response) { + $log.error("Error response on modal:", response); + $mdl.error = response.data; + $mdl.error.status = response.status; + }; + + var setFormValidationMessage = function (valid, state, message) { + $mdl.scenarioStartForm = { + valid: valid, + state: state, + message: message + }; + }; + + var init = function () { + setFormValidationMessage(false, 'success', 'Looks good!'); + + scenarioService.getScenarios($mdl.agent.name) + .then(function success(response) { + $mdl.agent.scenarios = []; + response.data.forEach(function (scenario) { + scenario.requestedSeconds = scenario.minTime; + $mdl.agent.scenarios.push(scenario); + }); + }, handleErrorResponse) + }; + + init(); + + $mdl.validateRequestedSeconds = function (scenario) { + if (scenario.requestedSeconds >= scenario.minTime || scenario.requestedSeconds <= scenario.maxTime) { + setFormValidationMessage(true, 'success', 'Looks good!'); + } else { + setFormValidationMessage(false, 'danger', + 'Please choose a number between ' + scenario.minTime + ' and ' + scenario.maxTime); + } + }; + + $mdl.ok = function (agent, selectedScenario) { + if (!$mdl.scenarioStartForm.valid) return; + + scenarioService.startScenario(agent.name, selectedScenario.name, selectedScenario.requestedSeconds) + .then(function success(response) { + $log.debug("Scenario starting...", response.data); + $uibModalInstance.close(agent); + }, handleErrorResponse); + }; + + $mdl.cancel = function () { + $uibModalInstance.dismiss('cancel'); + }; +}); \ No newline at end of file diff --git a/scenario-generator/src/main/resources/static/modal.html b/scenario-generator/src/main/resources/static/modal.html new file mode 100644 index 0000000..b845752 --- /dev/null +++ b/scenario-generator/src/main/resources/static/modal.html @@ -0,0 +1,49 @@ + \ No newline at end of file diff --git a/scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunnerTest.java b/scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunnerTest.java new file mode 100644 index 0000000..e0bd9d4 --- /dev/null +++ b/scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/OnlyOneInAllScenarioRunnerTest.java @@ -0,0 +1,126 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario; + +import com.opsgenie.playground.scenarioGenerator.entity.Agent; +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; +import com.opsgenie.playground.scenarioGenerator.entity.ScenarioStatus; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +/** + * @author Serhat Can + * @version 01/06/17 + */ +@RunWith(MockitoJUnitRunner.class) +public class OnlyOneInAllScenarioRunnerTest { + + private static final String EASY_TRAVEL = "easyTravel"; + private static final String BADGES = "badges"; + + @Mock + private ScenarioService scenarioService; + + @InjectMocks + private OnlyOneInAllScenarioRunner runner; + + private List agents = new ArrayList<>(); + + private Scenario etScenario1; + private Scenario etScenario2; + private Scenario etScenario3; + + public OnlyOneInAllScenarioRunnerTest() { + agents.add(new Agent(EASY_TRAVEL, "http://easytravel.com")); + agents.add(new Agent(BADGES, "https://badges.com")); + + etScenario1 = new Scenario("scenario1", "scenario1 desc", false, 5, 80); + etScenario2 = new Scenario("scenario2", "scenario2 desc", false, 10, 180); + etScenario3 = new Scenario("scenario3", "scenario3 desc", false, 0, 50); + } + + @Before + public void setup() throws Exception { + when(scenarioService.getScenario(EASY_TRAVEL, etScenario1.getName())).thenReturn(etScenario1); + when(scenarioService.getScenario(EASY_TRAVEL, etScenario2.getName())).thenReturn(etScenario2); + when(scenarioService.getScenario(EASY_TRAVEL, etScenario3.getName())).thenReturn(etScenario3); + + doNothing().when(scenarioService).enableScenario(EASY_TRAVEL, etScenario1.getName()); + } + + @Test + public void testStatus() throws Exception { + final ScenarioStatus availableStatus = runner.status(EASY_TRAVEL); + assertScenarioStatus(availableStatus, ScenarioStatus.AVAILABLE, 0, 0, null, false); + + final ScenarioStatus newScenario = runner.start(EASY_TRAVEL, etScenario1.getName(), 4); + assertScenarioStatus(newScenario, ScenarioStatus.IN_PROGRESS, 5, 5, etScenario1, true); + + final ScenarioStatus runningStatus = runner.status(EASY_TRAVEL); + assertScenarioStatus(runningStatus, ScenarioStatus.IN_PROGRESS, 5, 5, etScenario1, true); + + final ScenarioStatus busyScenario = runner.start(EASY_TRAVEL, etScenario2.getName(), 40); + assertScenarioStatus(busyScenario, ScenarioStatus.IN_PROGRESS, 5, 5, etScenario1, true); + } + + @Test + public void testStartSuccessfullyWithLowRequestedTimeThanExpected() throws Exception { + final ScenarioStatus ssWithLowerRequestedTime = runner.start(EASY_TRAVEL, etScenario1.getName(), 2); + assertScenarioStatus(ssWithLowerRequestedTime, ScenarioStatus.IN_PROGRESS, 5, 5, etScenario1, true); + } + + @Test + public void testStartSuccessfullyWithRegularRequestedTime() throws Exception { + final ScenarioStatus ssWithNormalRequestedTime = runner.start(EASY_TRAVEL, etScenario1.getName(), 10); + assertScenarioStatus(ssWithNormalRequestedTime, ScenarioStatus.IN_PROGRESS, 10, 10, etScenario1, true); + } + + @Test + public void testStartSuccessfullyWithHighRequestedTimeThanExpected() throws Exception { + final ScenarioStatus ssWithNormalRequestedTime = runner.start(EASY_TRAVEL, etScenario1.getName(), 90); + assertScenarioStatus(ssWithNormalRequestedTime, ScenarioStatus.IN_PROGRESS, 80, 80, etScenario1, true); + } + + @Test + public void testCheckAndReleaseScenariosOnTime() throws Exception { + final ScenarioStatus runningScenarioStatus = runner.start(EASY_TRAVEL, etScenario3.getName(), 0); + assertScenarioStatus(runningScenarioStatus, ScenarioStatus.IN_PROGRESS, 0, 0, etScenario3, true); + + final ScenarioStatus running = runner.status(EASY_TRAVEL); + assertScenarioStatus(running, ScenarioStatus.IN_PROGRESS, 0, 0, etScenario3, true); + + runner.checkAndReleaseScenariosOnTime(); + + final ScenarioStatus availableStatus = runner.status(EASY_TRAVEL); + assertScenarioStatus(availableStatus, ScenarioStatus.AVAILABLE, 0, 0, null, false); + + final ScenarioStatus runningScenarioStatus2 = runner.start(EASY_TRAVEL, etScenario3.getName(), 1); + assertScenarioStatus(runningScenarioStatus2, ScenarioStatus.IN_PROGRESS, 1, 1, etScenario3, true); + + runner.checkAndReleaseScenariosOnTime(); + + final ScenarioStatus availableStatus2 = runner.status(EASY_TRAVEL); + assertScenarioStatus(availableStatus2, ScenarioStatus.IN_PROGRESS, 1, 1, etScenario3, true); + + } + + private void assertScenarioStatus(ScenarioStatus scenarioStatus, String status, int requestedSeconds, int remainingSeconds, Scenario scenario, boolean running) { + assertThat(scenarioStatus.getStatus(), is(equalTo(status))); + assertThat(scenarioStatus.getRequestedSeconds(), is(equalTo(requestedSeconds))); + assertThat(scenarioStatus.getRemainingSeconds(), is(equalTo(remainingSeconds))); + assertThat(scenarioStatus.getRunningScenario(), is(equalTo(scenario))); + assertThat(scenarioStatus.isRunning(), is(equalTo(running))); + } +} \ No newline at end of file diff --git a/scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioServiceTest.java b/scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioServiceTest.java new file mode 100644 index 0000000..d562a8d --- /dev/null +++ b/scenario-generator/src/test/java/com/opsgenie/playground/scenarioGenerator/service/scenario/ScenarioServiceTest.java @@ -0,0 +1,105 @@ +package com.opsgenie.playground.scenarioGenerator.service.scenario; + +import com.opsgenie.playground.scenarioGenerator.entity.Agent; +import com.opsgenie.playground.scenarioGenerator.entity.Scenario; +import com.opsgenie.playground.scenarioGenerator.service.agent.AgentService; +import com.opsgenie.playground.scenarioGenerator.service.scenario.exception.ScenarioDoesNotExist; +import com.opsgenie.playground.scenarioGenerator.util.JSON; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @author Serhat Can + * @version 07/06/17 + */ +@RunWith(MockitoJUnitRunner.class) +public class ScenarioServiceTest { + + private static final String EASY_TRAVEL = "easyTravel"; + + private Agent agent; + + private Scenario scenario1; + + private Scenario scenario2; + + @Mock + private RestTemplate restTemplate; + + @Mock + private AgentService agentService; + + @InjectMocks + private ScenarioService scenarioService; + + public ScenarioServiceTest() { + this.agent = new Agent(EASY_TRAVEL, "http://a.com", "Travel fast and easy"); + this.scenario1 = new Scenario("sce1", "senaryo 1", true, 30, 50); + this.scenario2 = new Scenario("sce2", "senaryo 2"); + } + + @Before + public void setUp() throws Exception { + when(agentService.getAgent(EASY_TRAVEL)).thenReturn(agent); + + final List scenarios = Arrays.asList(scenario1, scenario2); + when(restTemplate.getForObject("http://a.com/scenarios", String.class)).thenReturn(JSON.toJson(scenarios)); + } + + @Test + public void testGetScenarios() throws Exception { + final List returnedScenarios = scenarioService.getScenarios(EASY_TRAVEL); + + verify(restTemplate, times(1)).getForObject("http://a.com/scenarios", String.class); + assertThat(Arrays.asList(scenario1, scenario2), is(equalTo(returnedScenarios))); + } + + @Test + public void testGetScenario() throws Exception { + final List scenarios = Arrays.asList(scenario1, scenario2); + when(restTemplate.getForObject("http://a.com/scenarios", String.class)).thenReturn(JSON.toJson(scenarios)); + + final Scenario sce1 = scenarioService.getScenario(EASY_TRAVEL, "sce1"); + assertThat(sce1, is(equalTo(scenario1))); + + final Scenario sce2 = scenarioService.getScenario(EASY_TRAVEL, "sce2"); + assertThat(sce2, is(equalTo(scenario2))); + } + + @Test(expected = ScenarioDoesNotExist.class) + public void testGetScenarioThrowsScenarioDoesNotExistException() throws Exception { + final List scenarios = Arrays.asList(scenario1); + when(restTemplate.getForObject("http://a.com/scenarios", String.class)).thenReturn(JSON.toJson(scenarios)); + + scenarioService.getScenario(EASY_TRAVEL, "sce2"); + } + + @Test + public void testEnableScenario() throws Exception { + scenarioService.enableScenario(EASY_TRAVEL, "scenario1"); + verify(restTemplate, times(1)).postForLocation("http://a.com/scenarios/scenario1/enable", String.class); + } + + @Test + public void testDisableScenario() throws Exception { + scenarioService.disableScenario(EASY_TRAVEL, "scenario1"); + verify(restTemplate, times(1)).postForLocation("http://a.com/scenarios/scenario1/disable", String.class); + } + +} \ No newline at end of file