diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..183db1999661ffd8a48b0e1ea22f64dc72dc7119 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target/ +/codemr/ +.project +.classpath +/.settings diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..bb7d754baa751630b25598e182a30f062e9b6ebd --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,40 @@ +include: + - project: osl/code/org.etsi.osl.main + ref: main + file: + - ci-templates/default.yml + - ci-templates/build.yml + rules: + - if: '$CI_COMMIT_REF_NAME == "main"' + + - project: osl/code/org.etsi.osl.main + ref: develop + file: + - ci-templates/default.yml + - ci-templates/build.yml + rules: + - if: '$CI_COMMIT_REF_NAME == "develop"' + + - project: osl/code/org.etsi.osl.main + ref: $CI_COMMIT_REF_NAME + file: + - ci-templates/default.yml + - ci-templates/build.yml + rules: + - if: '$CI_COMMIT_REF_PROTECTED && $CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "develop"' + + - project: osl/code/org.etsi.osl.main + ref: develop + file: + - ci-templates/default.yml + - ci-templates/build_unprotected.yml + rules: + - if: '$CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_REF_NAME != "develop" && !$CI_COMMIT_REF_PROTECTED' + +maven_build: + extends: .maven_build + +docker_build: + extends: .docker_build + needs: + - maven_build diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c30ca9f295d08174831b1e71ed27edff5ba2cdcb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM ibm-semeru-runtimes:open-17.0.7_7-jdk +MAINTAINER openslice.io +RUN mkdir /opt/shareclasses +RUN mkdir -p /opt/openslice/lib/ +COPY target/org.etsi.osl.oas-1.0.0.jar /opt/openslice/lib/ +CMD ["java", "-Xshareclasses:cacheDir=/opt/shareclasses", "-jar", "/opt/openslice/lib/org.etsi.osl.oas-1.0.0.jar"] +EXPOSE 13101 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/LICENSE @@ -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 [yyyy] [name of copyright owner] + + 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 index d9ba4c0101bae6391863e63da91da51adcd1d905..7e9cb79fa59c77ebf038b7504f1a2768af6a3b23 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,2 @@ # org.etsi.osl.oas - - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://labs.etsi.org/rep/osl/code/org.etsi.osl.oas.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://labs.etsi.org/rep/osl/code/org.etsi.osl.oas/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +Openslice Assurance Services diff --git a/ci_settings.xml b/ci_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..69ad06ed6c63795d191555afde6ea2d1da4e133d --- /dev/null +++ b/ci_settings.xml @@ -0,0 +1,16 @@ + + + + gitlab-maven + + + + Job-Token + ${CI_JOB_TOKEN} + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..30b259ad7396709d18988ceeb9d115e600ca2855 --- /dev/null +++ b/pom.xml @@ -0,0 +1,364 @@ + + 4.0.0 + + org.etsi.osl + org.etsi.osl.main + 1.0.0 + ../org.etsi.osl.main + + + + org.etsi.osl.oas + org.etsi.osl.oas + http://openslice.io + + + + + UTF-8 + UTF-8 + ${spring-boot-version} + ${flowable-version} + ${springdoc-version} + ${springdoc-security-version} + ${springdoc-openapiui-version} + 1.18.28 + + + + + gitlab-maven + https://labs.etsi.org/rep/api/v4/groups/260/-/packages/maven + + + + + gitlab-maven + ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven + + + gitlab-maven + ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven + + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot-version} + pom + import + + + + org.apache.camel.springboot + camel-spring-boot-dependencies + ${camel.version} + pom + import + + + com.google.guava + guava + 32.0 + + + org.keycloak.bom + keycloak-adapter-bom + ${keycloak.version} + pom + import + + + + + + + + + + + org.projectlombok + lombok + provided + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.flowable + flowable-spring-boot-starter + ${flowable.version} + + + + org.etsi.osl + org.etsi.osl.tmf.api + ${org.etsi.osl.tmf.api.version} + + + + + + org.springframework.boot + spring-boot-starter-activemq + + + org.apache.activemq + activemq-amqp + test + + + org.apache.qpid + proton-j + + + + + org.messaginghub + pooled-jms + + + + + + org.apache.camel.springboot + camel-spring-boot-starter + + + org.apache.activemq + activemq-pool + + + org.apache.camel + camel-activemq + + + org.apache.activemq + activemq-broker + + + + + org.apache.camel.springboot + camel-service-starter + + + + org.apache.camel.springboot + camel-http-starter + + + org.apache.camel + camel-jackson + + + org.apache.camel + camel-stream + + + + com.fasterxml.jackson.core + jackson-databind + + + + + + org.springframework.security + spring-security-web + + + org.springframework.security + spring-security-config + + + org.springframework.security.oauth + spring-security-oauth2 + ${oauth.version} + + + org.springframework.security + spring-security-jwt + ${jwt.version} + + + org.springframework.security + spring-security-test + test + + + + org.keycloak + keycloak-spring-boot-starter + + + org.keycloak + keycloak-spring-security-adapter + + + + org.keycloak + keycloak-admin-client + ${keycloak.version} + + + + + + com.h2database + h2 + test + + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + org.springdoc + springdoc-openapi-ui + ${springdoc.openapiui.version} + + + org.springdoc + springdoc-openapi-security + ${springdoc.security.version} + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-commons + test + + + org.junit.platform + junit-platform-runner + test + + + org.junit.platform + junit-platform-commons + 1.5.2 + test + + + + org.apache.activemq + activemq-broker + test + + + + + + javax.jms + javax.jms-api + 2.0.1 + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + org.projectlombok + lombok + ${lombok-version} + + + + + + + org.codehaus.mojo + license-maven-plugin + ${maven-license-plugin.version} + + false + ========================LICENSE_START================================= + =========================LICENSE_END================================== + *.json + + + + generate-license-headers + + update-file-header + + process-sources + + ${license.licenseName} + + + + + download-licenses + + download-licenses + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot-version} + + + + repackage + + + + + + + + + + + diff --git a/src/main/java/org/etsi/osl/oas/AlarmHandling.java b/src/main/java/org/etsi/osl/oas/AlarmHandling.java new file mode 100644 index 0000000000000000000000000000000000000000..12fd66cfd5d5e266b35d93cbdadcb3ab8f684e37 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/AlarmHandling.java @@ -0,0 +1,297 @@ +package org.etsi.osl.oas; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.etsi.osl.oas.model.Action; +import org.etsi.osl.oas.model.ActionCharacteristic; +import org.etsi.osl.oas.model.ActionExecutionStatus; +import org.etsi.osl.oas.model.Condition; +import org.etsi.osl.oas.model.RuleSpecification; +import org.etsi.osl.oas.reposervices.ActionSpecificationRepoService; +import org.etsi.osl.oas.reposervices.RuleSpecificationRepoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.am642.model.AffectedService; +import org.etsi.osl.tmf.am642.model.Alarm; +import org.etsi.osl.tmf.am642.model.AlarmCreateEvent; +import org.etsi.osl.tmf.am642.model.AlarmStateType; +import org.etsi.osl.tmf.am642.model.AlarmUpdate; +import org.etsi.osl.tmf.am642.model.Comment; +import org.etsi.osl.tmf.common.model.Any; +import org.etsi.osl.tmf.common.model.EValueType; +import org.etsi.osl.tmf.common.model.service.Characteristic; +import org.etsi.osl.tmf.common.model.service.Note; +import org.etsi.osl.tmf.sim638.model.ServiceUpdate; +import lombok.extern.apachecommons.CommonsLog; + +/** + * @author ctranoris + * + */ +@Configuration +@CommonsLog +public class AlarmHandling { + + + @Autowired + RuleSpecificationRepoService ruleSpecificationRepoService; + + @Autowired + ActionSpecificationRepoService actionSpecificationRepoService; + + @Autowired + AlarmsService alarmsService; + + @Autowired + CatalogService catalogService; + + @Value("${spring.application.name}") + private String compname; + + @Transactional + public void onAlarmCreateEvent(AlarmCreateEvent anAlarmCreateEvent) { + + + if ((anAlarmCreateEvent != null) + && (anAlarmCreateEvent.getEvent() != null) + && (anAlarmCreateEvent.getEvent().getAlarm() != null)) { + + + Alarm alarm = anAlarmCreateEvent.getEvent().getAlarm(); + log.info("onAlarmCreateEvent AlarmType=" + alarm.getAlarmType() + + ", ProbableCause=" + alarm.getProbableCause() + + ", PerceivedSeverity=" + alarm.getPerceivedSeverity() + + ", SourceSystemId=" + alarm.getSourceSystemId() + + ", SpecificProblem=" + alarm.getSpecificProblem() + + ", AlarmDetails=" + alarm.getAlarmDetails() ); + + + performActionOnAlarm( alarm ); + + } + + + } + + @Transactional + public void performActionOnAlarm(Alarm alarm) { + + //decide If we handle this. + var actions = decideForExecutionAction( alarm ); + + if ( ( actions == null ) || ( actions.size() == 0 )) { //we did not find an action to perform + alarmsService.patchAlarmAck(alarm, false, null); + return; + } + + + + // send ack to the alarm management + alarmsService.patchAlarmAck(alarm, true, actions); + + //execute the desired action + executeActions(actions,alarm); + + //if action is successful clear alarm + //clear probably will be done by process CheckActionsStatus + //by checking the EXEC_ACTION_LAST_ACK on related service, by serviceId + } + + + /** + * @param alarm + * + * decide Action to perform on alarm based on certain criteria or return null. + * The decision is based on registered RuleSpecification conditions of alarms + * + * + */ + @Transactional + public List decideForExecutionAction(Alarm alarm) { + + // examine our list of rulespecs if we can have a match for a specific service in inventory + // In future a rules engine should be more efficient. + // For now and for our PoC we will use query in DB and search the attributes + + var actionsToApply = new ArrayList(); + + for (AffectedService affectedService : alarm.getAffectedService()) { + var rulespecs = ruleSpecificationRepoService.findByScopeUuid( affectedService.getId() ); + for (RuleSpecification ruleSpecification : rulespecs) { + RuleSpecification spec = ruleSpecificationRepoService.findById( ruleSpecification.getUuid() ); + +// if (ruleSpecification.getCondition().size() == 0 ) { // if there are no conditions, this means any condition so add all of the actions! +// actionsToApply.addAll( ruleSpecification.getActions() ); +// } + + Boolean conditionInitialStatus = true; // if there are no conditions, this means any condition so add all of the actions! + + for (Condition condition : spec.getCondition()) { + + Boolean conditionStatus = false; + //match here the criteria + try { + + + Field field = Alarm.class.getDeclaredField(condition.getOpensliceEventAttributeName()); + field.setAccessible(true); + String alarmvalue = field.get(alarm).toString(); + String operator = condition.getOperator(); + switch ( operator) { + case "EQUALS": + conditionStatus = alarmvalue.equals( condition.getEventAttributeValue() ); + break; + case "NOTEQUAL": + conditionStatus = ! alarmvalue.equals( condition.getEventAttributeValue() ); + break; + case "GREATER_THAN": + conditionStatus = Double.valueOf(alarmvalue) > Double.valueOf( condition.getEventAttributeValue()); + break; + case "LESS_THAN": + conditionStatus = Double.valueOf(alarmvalue) < Double.valueOf( condition.getEventAttributeValue()); + break; + + default: + break; + } + + + + } catch (NoSuchFieldException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (condition.getBooleanOperator().equals("OR") ) { + conditionInitialStatus = conditionInitialStatus || conditionStatus; + } else { + conditionInitialStatus = conditionInitialStatus && conditionStatus; + } + } + + + + + if (conditionInitialStatus) { + actionsToApply.addAll( ruleSpecification.getActions() ); + } + } + + } + + + + + + return actionsToApply; + } + + + + + + + + + /** + * execute each action, by passing it as a ServiceUpdate to the system + * @param actions + * @param alarm + */ + private void executeActions(List actions, Alarm alarm) { + + List actList= new ArrayList<>(); + + for (Action action : actions) { + + ServiceUpdate supd = new ServiceUpdate();; + Note n = new Note(); + n.setText("Service Action executeAction. Action: " + action.getName() + ", for acknowledged alarm " + alarm.getUuid() ); + n.setAuthor( compname ); + n.setDate( OffsetDateTime.now(ZoneOffset.UTC).toString() ); + supd.addNoteItem( n ); + String serviceid= null; + + Characteristic characteristic = new Characteristic(); + characteristic.setName("EXEC_ACTION"); + characteristic.setValueType( EValueType.MAP.getValue() ); + var map = new HashMap(); + map.put("ACTION_NAME", action.getName()); + map.put("ACTION_UUID", action.getUuid() ); + map.put("ALARM_UUID", alarm.getUuid() ); + for (ActionCharacteristic achar : action.getActionCharacteristics() ) { + if ( achar.getName().equalsIgnoreCase("serviceid")) { + serviceid = achar.getValue(); + } + map.put( achar.getName() , achar.getValue() ); + } + + + if ( alarm.getAlarmDetails() !=null ) { + map.put("ALARM_DETAILS", alarm.getAlarmDetails()); + + String[] tokens = alarm.getAlarmDetails().split(";"); + for (String token : tokens) { + String[] kv = token.split("="); + if ( kv.length == 2) { + map.put( kv[0], kv[1]); + } + } + } + + + Any value = new Any(); + String str; + try { + str = toJsonString( map ); + value.setValue( str ); + characteristic.setValue( value ); + supd.addServiceCharacteristicItem( characteristic ); + if ( serviceid != null ) { + catalogService.updateService( serviceid , supd, true); + } + + } catch (IOException e) { + e.printStackTrace(); + } + + ActionExecutionStatus aes = new ActionExecutionStatus(); + aes.setAction(action); + aes.setServiceId(serviceid); + actList.add( aes ); + + } + + + //add to list of Alarms to Check the Actions in order to clear it or unack it! + alarmsService.getPendingAlarmsToCheck().put( alarm.getId() , actList); + + } + + static String toJsonString(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(object); + } + + +} diff --git a/src/main/java/org/etsi/osl/oas/AlarmsService.java b/src/main/java/org/etsi/osl/oas/AlarmsService.java new file mode 100644 index 0000000000000000000000000000000000000000..54fc7c3031dabf73ef6e087b40e57b9e4dbc9b9a --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/AlarmsService.java @@ -0,0 +1,196 @@ +package org.etsi.osl.oas; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.etsi.osl.oas.model.Action; +import org.etsi.osl.oas.model.ActionExecutionStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.am642.model.Alarm; +import org.etsi.osl.tmf.am642.model.AlarmCreate; +import org.etsi.osl.tmf.am642.model.AlarmStateType; +import org.etsi.osl.tmf.am642.model.AlarmUpdate; +import org.etsi.osl.tmf.am642.model.Comment; +import org.etsi.osl.tmf.so641.model.ServiceOrder; + +@Service +public class AlarmsService { + + private static final transient Log logger = LogFactory.getLog(AlarmsService.class.getName()); + + @Autowired + CamelContext contxt; + + @Autowired + ProducerTemplate template; + + @Value("${ALARMS_ADD_ALARM}") + private String ALARMS_ADD_ALARM =""; + + @Value("${ALARMS_UPDATE_ALARM}") + private String ALARMS_UPDATE_ALARM =""; + + @Value("${ALARMS_GET_ALARM}") + private String ALARMS_GET_ALARM =""; + + + @Value("${spring.application.name}") + private String compname; + + + private Map > pendingAlarmsToCheck = new HashMap<>(); + + /** + * @param al + * @return a response in string + * @throws IOException + */ + public String createAlarm(AlarmCreate al) throws IOException { + + String body; + body = toJsonString(al); + logger.info("createAlarm body = " + body); + Object response = template.requestBody( ALARMS_ADD_ALARM, body); + return response.toString(); + } + + /** + * @param al + * @return a response in string + * @throws IOException + */ + public String updateAlarm(AlarmUpdate al, String alarmid, String commentText) throws IOException { + Comment comment = new Comment(); + comment.setTime(OffsetDateTime.now(ZoneOffset.UTC)); + comment.setSystemId(compname); + comment.setComment( commentText); + al.addCommentItem(comment); + + String body; + body = toJsonString(al); + logger.info("updateAlarm body = " + body); + Object response = template.requestBodyAndHeader( ALARMS_UPDATE_ALARM, body , "alarmid", alarmid); + return response.toString(); + } + + + + /** + * update the alarm status in alarm repo + * + * @param alarm + * @param canBehandled + * @param actions + */ + void patchAlarmAck(Alarm alarm, boolean canBehandled, List actions) { + AlarmUpdate aupd = new AlarmUpdate(); + String comment; + + if (canBehandled) { + aupd.setAckState("acknowledged"); + aupd.setAckSystemId( compname ); + + String[] names = actions.stream().map(p -> p.getName() ).toArray( size -> new String[actions.size()] ); + String acts = String.join(",", names); + comment = "Alarm handled automatically, by applying actions: " + acts ; + + }else { + + comment = "Alarm cannot be handled automatically"; + + } + aupd.setState(AlarmStateType.updated.name()); + + + try { + logger.info("Alarm id = " + alarm.getId() + "." + comment); + String response = this.updateAlarm(aupd, alarm.getId(), comment); + logger.info("Alarm id updated = " + response.toString() ); + } catch (IOException e) { + logger.error("patchAlarm Alarm id = " + alarm.getId() ); + e.printStackTrace(); + } + } + + + void patchAlarmClear( String alarmId) { + AlarmUpdate aupd = new AlarmUpdate(); + + aupd.setClearSystemId( compname ); + String comment = "Alarm cleared." ; + aupd.setState(AlarmStateType.cleared.name()); + + + try { + logger.info("Alarm id = " + alarmId + "." + comment); + String response = this.updateAlarm(aupd, alarmId, comment); + logger.info("Alarm id updated = " + response.toString() ); + } catch (IOException e) { + logger.error("patchAlarmClear Alarm id = " + alarmId ); + e.printStackTrace(); + } + } + + public Alarm getAlarm(String alarmid) { + + logger.info("will retrieve Alarm from id=" + alarmid ); + try { + + Map map = new HashMap<>(); + map.put("alarmid", alarmid ); + Object response = template. + requestBodyAndHeaders( ALARMS_GET_ALARM, "" , map); + + if ( !(response instanceof String)) { + logger.error("Alarm object is wrong."); + return null; + } + logger.debug("retrieve Alarm response is: " + response); + Alarm sor = toJsonObj( (String)response, Alarm.class); + + return sor; + + }catch (Exception e) { + logger.error("Cannot retrieve Service Order details from catalog. " + e.toString()); + } + return null; + + } + + public Map > getPendingAlarmsToCheck() { + return pendingAlarmsToCheck; + } + + public void setPendingAlarmsToCheck(Map > pendingAlarmsToCheck) { + this.pendingAlarmsToCheck = pendingAlarmsToCheck; + } + + + static String toJsonString(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(object); + } + + static T toJsonObj(String content, Class valueType) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.readValue( content, valueType); + } + + +} diff --git a/src/main/java/org/etsi/osl/oas/CatalogService.java b/src/main/java/org/etsi/osl/oas/CatalogService.java new file mode 100644 index 0000000000000000000000000000000000000000..eff04b3e1fc7f6dd021e28d92fb1714f41a50fdf --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/CatalogService.java @@ -0,0 +1,103 @@ +package org.etsi.osl.oas; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.etsi.osl.tmf.sim638.model.ServiceUpdate; + +@Service +public class CatalogService { + + private static final transient Log logger = LogFactory.getLog(CatalogService.class.getName()); + + @Autowired + CamelContext contxt; + + @Autowired + ProducerTemplate template; + + @Value("${CATALOG_UPD_SERVICE}") + private String CATALOG_UPD_SERVICE = ""; + + + @Value("${CATALOG_GET_SERVICE_BY_ID}") + private String CATALOG_GET_SERVICE_BY_ID = ""; + + public org.etsi.osl.tmf.sim638.model.Service updateService(String serviceId, ServiceUpdate s, boolean propagateToSO) { + logger.info("will update Service : " + serviceId ); + try { + Map map = new HashMap<>(); + map.put("serviceid", serviceId ); + map.put("propagateToSO", propagateToSO ); + + Object response = template.requestBodyAndHeaders( CATALOG_UPD_SERVICE, toJsonString(s), map); + + if ( !(response instanceof String)) { + logger.error("Service Instance object is wrong."); + } + + org.etsi.osl.tmf.sim638.model.Service serviceInstance = toJsonObj( (String)response, org.etsi.osl.tmf.sim638.model.Service.class); + //logger.debug("createService response is: " + response); + return serviceInstance; + + + }catch (Exception e) { + logger.error("Cannot update Service: " + serviceId + ": " + e.toString()); + } + return null; + + } + + + + + /** + * Ger service instance via bus + * @param serviceID + * @return + */ + public org.etsi.osl.tmf.sim638.model.Service retrieveService(String serviceID) { + logger.info("will retrieve Service instance from catalog serviceID=" + serviceID ); + try { + Object response = template. + requestBody( CATALOG_GET_SERVICE_BY_ID, serviceID); + + if ( !(response instanceof String)) { + logger.error("Service object is wrong."); + return null; + } + org.etsi.osl.tmf.sim638.model.Service serviceInstance = toJsonObj( (String)response, org.etsi.osl.tmf.sim638.model.Service.class); + //logger.debug("retrieveService response is: " + response); + return serviceInstance; + + }catch (Exception e) { + logger.error("Cannot retrieve Service details from catalog. " + e.toString()); + } + return null; + } + + + static T toJsonObj(String content, Class valueType) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.readValue( content, valueType); + } + static String toJsonString(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(object); + } + +} diff --git a/src/main/java/org/etsi/osl/oas/CheckActionsStatus.java b/src/main/java/org/etsi/osl/oas/CheckActionsStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..592ad7e6dcf78d78887abfef3a464cd89dee207a --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/CheckActionsStatus.java @@ -0,0 +1,145 @@ +package org.etsi.osl.oas; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.etsi.osl.oas.model.ActionExecutionStatus; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.am642.model.Alarm; +import org.etsi.osl.tmf.am642.model.AlarmStateType; +import org.etsi.osl.tmf.am642.model.AlarmUpdate; +import org.etsi.osl.tmf.common.model.service.Characteristic; +import org.etsi.osl.tmf.sim638.model.Service; + + +@Component(value = "CheckActionsStatus") // bean name +public class CheckActionsStatus implements JavaDelegate { + + @Autowired + AlarmsService alarmsService; + + @Autowired + CatalogService catalogService; + + + @Autowired + ObjectMapper mapper; + + + private static final transient Log logger = LogFactory.getLog( CheckActionsStatus.class.getName() ); + @Override + public void execute(DelegateExecution execution) { + logger.info("===================== CheckActionsStatus ===================="); + +// Alarm a = alarmsService.getAlarm("0"); +// a.getAlarmRaisedTime().toEpochSecond(); + //retrieve services for each pending alarms to check + List myList = new CopyOnWriteArrayList<>(alarmsService.getPendingAlarmsToCheck().keySet()); + for (String alarmId : myList ) { + + List aes = alarmsService.getPendingAlarmsToCheck().get(alarmId); + for (ActionExecutionStatus actionExecutionStatus : aes) { + String serviceId = actionExecutionStatus.getServiceId(); + Service aService = catalogService.retrieveService(serviceId); + + Characteristic execAction = aService.getServiceCharacteristicByName("EXEC_ACTION"); + Characteristic execActionAck = aService.getServiceCharacteristicByName("EXEC_ACTION_LAST_ACK"); + if ( (execAction != null) && (execActionAck != null ) && (execAction.getValue() != null) && (execActionAck.getValue() != null ) ) { + + if ( execActionAck.getValue().getValue().contains("ERROR") ) { + AlarmUpdate aupd = new AlarmUpdate(); + try { + alarmsService.updateAlarm(aupd, alarmId, "ERROR cannot clear alarm automatically"); + alarmsService.getPendingAlarmsToCheck().remove(alarmId); + break; + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } else { + String characteristicValue = execAction.getValue().getValue(); + try { + Map valsexecAction = mapper.readValue( characteristicValue, new TypeReference< Map>() {}); + Map valsexecexecActionAck = mapper.readValue( characteristicValue, new TypeReference< Map>() {}); + + if ( valsexecAction.get("ALARM_UUID").equals(alarmId) && + valsexecexecActionAck.get("ALARM_UUID").equals(alarmId)) { + + actionExecutionStatus.setActionfulfilled( true ); + + + } + + + + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + } + + + } + + + + } + + boolean alarmCanbeCleared = true; + for (ActionExecutionStatus actionExecutionStatus : aes) { //check all actions of fulfilled + alarmCanbeCleared = alarmCanbeCleared && actionExecutionStatus.isActionfulfilled(); + } + + + if (alarmCanbeCleared) { + //seems everything ok, clear alarm + alarmsService.patchAlarmClear(alarmId); + alarmsService.getPendingAlarmsToCheck().remove(alarmId); + } + } + + + //if alarm is older than 15minutes. Unack it and remove from hashmap + List amyList = new CopyOnWriteArrayList(alarmsService.getPendingAlarmsToCheck().keySet()); + + for (String alarmId : amyList ) { + Alarm alarm = alarmsService.getAlarm(alarmId); + OffsetDateTime anow = OffsetDateTime.now(ZoneOffset.UTC); + + if ( alarm!= null ) { + long diff = anow.toEpochSecond()- alarm.getAlarmRaisedTime().toEpochSecond(); + if ( diff > 30*60 ) { + AlarmUpdate aupd = new AlarmUpdate(); + aupd.setState(AlarmStateType.updated.name()); + try { + alarmsService.updateAlarm(aupd, alarmId, "Action expired, cannot clear alarm automatically after 30min"); + } catch (IOException e) { + e.printStackTrace(); + } + alarmsService.getPendingAlarmsToCheck().remove(alarmId); + + } + + } + } + + } +} diff --git a/src/main/java/org/etsi/osl/oas/EventRouteBuilder.java b/src/main/java/org/etsi/osl/oas/EventRouteBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..7e680b958803065bf001df8a7498b714c96134c0 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/EventRouteBuilder.java @@ -0,0 +1,40 @@ +package org.etsi.osl.oas; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.model.dataformat.JsonLibrary; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import org.etsi.osl.tmf.am642.model.AlarmCreateEvent; + +/** + * @author ctranoris + * + */ +@Configuration +@Component +public class EventRouteBuilder extends RouteBuilder { + + @Value("${EVENT_ALARM_CREATE}") + private String EVENT_ALARM_CREATE = ""; + + private static final transient Log logger = LogFactory.getLog(EventRouteBuilder.class.getName()); + + public void configure() { + + + /** + * Create route, from event + */ + + from( EVENT_ALARM_CREATE ) + .unmarshal().json( JsonLibrary.Jackson, AlarmCreateEvent.class, true) + .bean( AlarmHandling.class, "onAlarmCreateEvent") + .to("stream:out"); + + + } +} diff --git a/src/main/java/org/etsi/osl/oas/LocalMysqlDialect.java b/src/main/java/org/etsi/osl/oas/LocalMysqlDialect.java new file mode 100644 index 0000000000000000000000000000000000000000..2f0d8bd93d772c68685ea5ac2338cbaaf75e4d6e --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/LocalMysqlDialect.java @@ -0,0 +1,29 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas; + +import org.hibernate.dialect.MySQLDialect; ; + +public class LocalMysqlDialect extends MySQLDialect { + @Override + public String getTableTypeString() { + return " DEFAULT CHARSET=utf8"; + } +} diff --git a/src/main/java/org/etsi/osl/oas/MvcConfig.java b/src/main/java/org/etsi/osl/oas/MvcConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..8f6c12d9120600e0a117995333aa4d7b456857b6 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/MvcConfig.java @@ -0,0 +1,101 @@ +package org.etsi.osl.oas; + + + +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.resource.EncodedResourceResolver; +import org.springframework.web.servlet.resource.PathResourceResolver; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +//@EnableWebMvc - removed 30/7/2021 +public class MvcConfig implements WebMvcConfigurer { + @Autowired + Environment env; + + public MvcConfig() { + super(); + } + + @Override + public void addViewControllers(final ViewControllerRegistry registry) { + registry.addViewController("/index.html"); + + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + + registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + + } + + @Override + public void extendMessageConverters(List> converters) { + // DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd + // HH:mm:ss"); + DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME; + LocalDateTimeSerializer localDateTimeSerializer = new LocalDateTimeSerializer(formatter); + LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(formatter); + + JavaTimeModule module = new JavaTimeModule(); + module.addSerializer(LocalDateTime.class, localDateTimeSerializer); + module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(module); + + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(OffsetDateTime.class, new JsonSerializer() { + @Override + public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException, JsonProcessingException { + jsonGenerator.writeString(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(offsetDateTime)); + + } + }); + + + mapper.registerModule(simpleModule); + + // add converter at the very front + // if there are same type mappers in converters, setting in first mapper + // is used. + converters.add(0, new MappingJackson2HttpMessageConverter(mapper)); + + } + + @Override + public void configureMessageConverters(List> converters) { + converters.add(new StringHttpMessageConverter()); + converters.add(new ByteArrayHttpMessageConverter()); + } +} diff --git a/src/main/java/org/etsi/osl/oas/OasSpingBoot.java b/src/main/java/org/etsi/osl/oas/OasSpingBoot.java new file mode 100644 index 0000000000000000000000000000000000000000..b68f967222eaf168417afbba2e87f811a4b8aae3 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/OasSpingBoot.java @@ -0,0 +1,116 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.osom + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas; + +import java.util.Collections; +import java.util.List; + +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.ExitCodeGenerator; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; + +/** + * @author ctranoris + * + */ + +@SpringBootApplication +public class OasSpingBoot implements CommandLineRunner { + + private static ApplicationContext applicationContext; + + @Override + public void run(String... arg0) throws Exception { + if (arg0.length > 0 && arg0[0].equals("exitcode")) { + throw new ExitException(); + } + + } + + public static void main(String[] args) throws Exception { + + applicationContext = new SpringApplication(OasSpingBoot.class).run(args); + +// for (String beanName : applicationContext.getBeanDefinitionNames()) { +// System.out.println(beanName); +// } + } + + class ExitException extends RuntimeException implements ExitCodeGenerator { + private static final long serialVersionUID = 1L; + + @Override + public int getExitCode() { + return 10; + } + } + + @Bean + public CommandLineRunner init(final RepositoryService repositoryService, + final RuntimeService runtimeService, + final TaskService taskService) { + + return new CommandLineRunner() { + @Override + public void run(String... strings) throws Exception { +// System.out.println("Number of process definitions : " + repositoryService.createProcessDefinitionQuery().count()); +// System.out.println("Number of active process definitions : " + repositoryService.createProcessDefinitionQuery().active().count() ); +// System.out.println("Number of active/suspended process definitions : " + repositoryService.createProcessDefinitionQuery().active().suspended().count()); +// System.out.println("Number of suspended process definitions : " + repositoryService.createProcessDefinitionQuery().suspended().count() ); +// System.out.println("Number of tasks : " + taskService.createTaskQuery().count()); +// System.out.println("Number of process instances : " + runtimeService.createProcessInstanceQuery().count() ); +// System.out.println("Number of suspended process instances : " + runtimeService.createProcessInstanceQuery().suspended().count() ); +// System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count()); +// +// +// List pr = repositoryService.createProcessDefinitionQuery().list(); +// for (ProcessDefinition processDefinition : Collections.unmodifiableList(pr) ) { +// System.out.println("Number of tasks after process start: " + processDefinition.getDeploymentId() ); +// System.out.println("Number of tasks after process start: " + processDefinition.toString() ); +// System.out.println("Number of tasks after process start: " + processDefinition.isSuspended() ); +//// try { +//// repositoryService.deleteDeployment( processDefinition.getDeploymentId(), true ); +//// }finally { +//// +//// } +// +// } +// +// List dq = repositoryService.createDeploymentQuery().list(); +// for (Deployment deployment : dq) { +// System.out.println("deployment: " + deployment.getName()); +// System.out.println("deployment: " + deployment.toString() ); +// System.out.println("deployment: " + deployment.isNew() ); +//// repositoryService.deleteDeployment( deployment.getId() , true ); +// +// } + } + }; + } + +} diff --git a/src/main/java/org/etsi/osl/oas/api/ActionSpecificationApi.java b/src/main/java/org/etsi/osl/oas/api/ActionSpecificationApi.java new file mode 100644 index 0000000000000000000000000000000000000000..6c859d795985b9271d4d0de6fff7ea54bae264cb --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/api/ActionSpecificationApi.java @@ -0,0 +1,149 @@ +package org.etsi.osl.oas.api; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.oas.model.ActionSpecification; +import org.etsi.osl.oas.model.ActionSpecificationCreate; +import org.etsi.osl.oas.model.ActionSpecificationUpdate; +import org.etsi.osl.oas.model.Error; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +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.RequestParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + + +@Tag( name = "actionSpecification") +public interface ActionSpecificationApi { + + + Logger log = LoggerFactory.getLogger(ActionSpecificationApi.class); + + default Optional getObjectMapper() { + return Optional.empty(); + } + + default Optional getRequest() { + return Optional.empty(); + } + + default Optional getAcceptHeader() { + return getRequest().map(r -> r.getHeader("Accept")); + } + + @Operation(summary = "Creates an ActionSpecification", operationId = "createActionSpecification", description = "This operation creates an ActionSpecification entity.", tags = { + "actionSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Created"), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/actionSpecification", produces = { "application/json;charset=utf-8" }, consumes = { + "application/json;charset=utf-8" }, method = RequestMethod.POST) + default ResponseEntity createActionSpecification( + @Parameter(description = "The ActionSpecification to be created", required = true) @Valid @RequestBody ActionSpecificationCreate body) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "Deletes an ActionSpecification", operationId = "deleteActionSpecification", description = "This operation deletes a ActionSpecification entity.", tags = { + "actionSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Deleted"), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/actionSpecification/{id}", produces = { + "application/json;charset=utf-8" }, method = RequestMethod.DELETE) + default ResponseEntity deleteActionSpecification( + @Parameter(description = "Identifier of the ActionSpecification", required = true) @PathVariable("id") String id) { + if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) { + } else { + log.warn( + "ObjectMapper or HttpServletRequest not configured in default ActionSpecificationApi interface so no example is generated"); + } + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "List or find ActionSpecification objects", operationId = "listActionSpecification", description = "This operation list or find ActionSpecification entities", tags = { + "actionSpecification", }) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success" ) , + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/actionSpecification", produces = { + "application/json;charset=utf-8" }, method = RequestMethod.GET) + default ResponseEntity> listActionSpecification( + @Parameter(description = "Comma-separated properties to be provided in response") @Valid @RequestParam(value = "fields", required = false) String fields, + @Parameter(description = "Requested index for start of resources to be provided in response") @Valid @RequestParam(value = "offset", required = false) Integer offset, + @Parameter(description = "Requested number of resources to be provided in response") @Valid @RequestParam(value = "limit", required = false) Integer limit, + @Parameter(hidden = true) @Valid @RequestParam Map allParams) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "Updates partially an ActionSpecification", operationId = "patchActionSpecification", description = "This operation updates partially an ActionSpecification entity.", tags = { + "actionSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Updated" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/actionSpecification/{id}", produces = { + "application/json;charset=utf-8" }, consumes = { + "application/json;charset=utf-8" }, method = RequestMethod.PATCH) + default ResponseEntity patchActionSpecification( + @Parameter(description = "The ActionSpecification to be updated", required = true) @Valid @RequestBody ActionSpecificationUpdate body, + @Parameter(description = "Identifier of the ActionSpecification", required = true) @PathVariable("id") String id) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "Retrieves an ActionSpecification by ID", operationId = "retrieveActionSpecification", description = "This operation retrieves an ActionSpecification entity. Attribute selection is enabled for all first level attributes.", tags = { + "actionSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Success" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/actionSpecification/{id}", produces = { + "application/json;charset=utf-8" }, method = RequestMethod.GET) + default ResponseEntity retrieveActionSpecification( + @Parameter(description = "Identifier of the ActionSpecification", required = true) @PathVariable("id") String id, + @Parameter(description = "Comma-separated properties to provide in response") @Valid @RequestParam(value = "fields", required = false) String fields, Map allParams) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + +} + diff --git a/src/main/java/org/etsi/osl/oas/api/ActionSpecificationApiController.java b/src/main/java/org/etsi/osl/oas/api/ActionSpecificationApiController.java new file mode 100644 index 0000000000000000000000000000000000000000..b8627e23904efc8d400e52f150c738ff161156ff --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/api/ActionSpecificationApiController.java @@ -0,0 +1,147 @@ +package org.etsi.osl.oas.api; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.oas.model.ActionSpecification; +import org.etsi.osl.oas.model.ActionSpecificationCreate; +import org.etsi.osl.oas.model.ActionSpecificationUpdate; +import org.etsi.osl.oas.reposervices.ActionSpecificationRepoService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + + +@Controller +@RequestMapping("/assuranceServicesManagement/v1/") +public class ActionSpecificationApiController implements ActionSpecificationApi { + + private static final Logger log = LoggerFactory.getLogger(ActionSpecificationApiController.class); + + private final ObjectMapper objectMapper; + + private final HttpServletRequest request; + + + @Autowired + ActionSpecificationRepoService actionSpecificationRepoService; + + @Value("${spring.application.name}") + private String compname; + + @org.springframework.beans.factory.annotation.Autowired + public ActionSpecificationApiController(ObjectMapper objectMapper, HttpServletRequest request) { + this.objectMapper = objectMapper; + this.request = request; + } + + @Override + public Optional getObjectMapper() { + return Optional.ofNullable(objectMapper); + } + + @Override + public Optional getRequest() { + return Optional.ofNullable(request); + } + + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity createActionSpecification(@Valid ActionSpecificationCreate body) { + try { + + ActionSpecification c = actionSpecificationRepoService.addActionSpecification( body ); + + return new ResponseEntity(c, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity deleteActionSpecification(String id) { + try { + + return new ResponseEntity( actionSpecificationRepoService.deleteById( id ), HttpStatus.OK); + } catch ( Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity retrieveActionSpecification(String id, @Valid String fields, + Map allParams) { + + try { + + return new ResponseEntity( actionSpecificationRepoService.findById( id ), HttpStatus.OK); + } catch ( Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity patchActionSpecification(@Valid ActionSpecificationUpdate body, + String id) { + ActionSpecification c = actionSpecificationRepoService.updateActionSpecification( id, body ); + + return new ResponseEntity(c, HttpStatus.OK); + } + + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity> listActionSpecification(@Valid String fields, + @Valid Integer offset, @Valid Integer limit, @Valid Map allParams) { + + try { + if (allParams != null) { + allParams.remove("fields"); + allParams.remove("offset"); + allParams.remove("limit"); + } else { + allParams = new HashMap<>(); + } + if ((fields == null) && (allParams.size() == 0)) { + + String myfields = null; + return new ResponseEntity>( + actionSpecificationRepoService.findAll( myfields, allParams), HttpStatus.OK); + + + } else { + + + return new ResponseEntity>( + actionSpecificationRepoService.findAll(fields, allParams), HttpStatus.OK); + } + + } catch (Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + +} diff --git a/src/main/java/org/etsi/osl/oas/api/RuleSpecificationApi.java b/src/main/java/org/etsi/osl/oas/api/RuleSpecificationApi.java new file mode 100644 index 0000000000000000000000000000000000000000..3484d76231e1e641fb06871cf912f671797c66ea --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/api/RuleSpecificationApi.java @@ -0,0 +1,149 @@ +package org.etsi.osl.oas.api; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.oas.model.Error; +import org.etsi.osl.oas.model.RuleSpecification; +import org.etsi.osl.oas.model.RuleSpecificationCreate; +import org.etsi.osl.oas.model.RuleSpecificationUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +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.RequestParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + + +@Tag(name = "ruleSpecification") +public interface RuleSpecificationApi { + + + Logger log = LoggerFactory.getLogger(RuleSpecificationApi.class); + + default Optional getObjectMapper() { + return Optional.empty(); + } + + default Optional getRequest() { + return Optional.empty(); + } + + default Optional getAcceptHeader() { + return getRequest().map(r -> r.getHeader("Accept")); + } + + @Operation(summary = "Creates a RuleSpecification", operationId = "createRuleSpecification", description = "This operation creates a RuleSpecification entity.", tags = { + "ruleSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Created" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/ruleSpecification", produces = { "application/json;charset=utf-8" }, consumes = { + "application/json;charset=utf-8" }, method = RequestMethod.POST) + default ResponseEntity createRuleSpecification( + @Parameter(description = "The RuleSpecification to be created", required = true) @Valid @RequestBody RuleSpecificationCreate body) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "Deletes a RuleSpecification", operationId = "deleteRuleSpecification", description = "This operation deletes a RuleSpecification entity.", tags = { + "ruleSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Deleted" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/ruleSpecification/{id}", produces = { + "application/json;charset=utf-8" }, method = RequestMethod.DELETE) + default ResponseEntity deleteRuleSpecification( + @Parameter(description = "Identifier of the RuleSpecification", required = true) @PathVariable("id") String id) { + if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) { + } else { + log.warn( + "ObjectMapper or HttpServletRequest not configured in default RuleSpecificationApi interface so no example is generated"); + } + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "List or find RuleSpecification objects", operationId = "listRuleSpecification", description = "This operation list or find RuleSpecification entities", tags = { + "ruleSpecification", }) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/ruleSpecification", produces = { + "application/json;charset=utf-8" }, method = RequestMethod.GET) + default ResponseEntity> listRuleSpecification( + @Parameter(description = "Comma-separated properties to be provided in response") @Valid @RequestParam(value = "fields", required = false) String fields, + @Parameter(description = "Requested index for start of resources to be provided in response") @Valid @RequestParam(value = "offset", required = false) Integer offset, + @Parameter(description = "Requested number of resources to be provided in response") @Valid @RequestParam(value = "limit", required = false) Integer limit, + @Parameter(hidden = true) @Valid @RequestParam Map allParams) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "Updates partially a RuleSpecification", operationId = "patchRuleSpecification", description = "This operation updates partially a RuleSpecification entity.", tags = { + "ruleSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Updated" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/ruleSpecification/{id}", produces = { + "application/json;charset=utf-8" }, consumes = { + "application/json;charset=utf-8" }, method = RequestMethod.PATCH) + default ResponseEntity patchRuleSpecification( + @Parameter(description = "The RuleSpecification to be updated", required = true) @Valid @RequestBody RuleSpecificationUpdate body, + @Parameter(description = "Identifier of the RuleSpecification", required = true) @PathVariable("id") String id) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Operation(summary = "Retrieves a RuleSpecification by ID", operationId = "retrieveRuleSpecification", description = "This operation retrieves a RuleSpecification entity. Attribute selection is enabled for all first level attributes.", tags = { + "ruleSpecification", }) + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Success" ), + @ApiResponse(responseCode = "400", description = "Bad Request" ), + @ApiResponse(responseCode = "401", description = "Unauthorized" ), + @ApiResponse(responseCode = "403", description = "Forbidden" ), + @ApiResponse(responseCode = "404", description = "Not Found" ), + @ApiResponse(responseCode = "405", description = "Method Not allowed" ), + @ApiResponse(responseCode = "409", description = "Conflict" ), + @ApiResponse(responseCode = "500", description = "Internal Server Error" ) }) + @RequestMapping(value = "/ruleSpecification/{id}", produces = { + "application/json;charset=utf-8" }, method = RequestMethod.GET) + default ResponseEntity retrieveRuleSpecification( + @Parameter(description = "Identifier of the RuleSpecification", required = true) @PathVariable("id") String id, + @Parameter(description = "Comma-separated properties to provide in response") @Valid @RequestParam(value = "fields", required = false) String fields) { + + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + +} + diff --git a/src/main/java/org/etsi/osl/oas/api/RuleSpecificationApiController.java b/src/main/java/org/etsi/osl/oas/api/RuleSpecificationApiController.java new file mode 100644 index 0000000000000000000000000000000000000000..e2894d42ac30f599da2ecde5896e971f5b1cd75a --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/api/RuleSpecificationApiController.java @@ -0,0 +1,139 @@ +package org.etsi.osl.oas.api; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.oas.model.RuleSpecification; +import org.etsi.osl.oas.model.RuleSpecificationCreate; +import org.etsi.osl.oas.model.RuleSpecificationUpdate; +import org.etsi.osl.oas.reposervices.RuleSpecificationRepoService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; + + +@Controller +@RequestMapping("/assuranceServicesManagement/v1/") +public class RuleSpecificationApiController implements RuleSpecificationApi { + + private static final Logger log = LoggerFactory.getLogger(RuleSpecificationApiController.class); + + private final ObjectMapper objectMapper; + + private final HttpServletRequest request; + + @Autowired + RuleSpecificationRepoService ruleSpecificationRepoService; + + @Value("${spring.application.name}") + private String compname; + + @org.springframework.beans.factory.annotation.Autowired + public RuleSpecificationApiController(ObjectMapper objectMapper, HttpServletRequest request) { + this.objectMapper = objectMapper; + this.request = request; + } + + @Override + public Optional getObjectMapper() { + return Optional.ofNullable(objectMapper); + } + + @Override + public Optional getRequest() { + return Optional.ofNullable(request); + } + + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity createRuleSpecification(@Valid RuleSpecificationCreate body) { + try { + + RuleSpecification c = ruleSpecificationRepoService.addRuleSpecification( body ); + + return new ResponseEntity(c, HttpStatus.OK); + } catch (Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity deleteRuleSpecification(String id) { + try { + + return new ResponseEntity( ruleSpecificationRepoService.deleteById( id ), HttpStatus.OK); + } catch ( Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity retrieveRuleSpecification(String id, @Valid String fields) { + try { + + return new ResponseEntity( ruleSpecificationRepoService.findById( id ), HttpStatus.OK); + } catch ( Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity patchRuleSpecification(@Valid RuleSpecificationUpdate body, String id) { + RuleSpecification c = ruleSpecificationRepoService.updateRuleSpecification( id, body ); + + return new ResponseEntity(c, HttpStatus.OK); + } + + + @Override + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')" ) + public ResponseEntity> listRuleSpecification(@Valid String fields, + @Valid Integer offset, @Valid Integer limit, @Valid Map allParams) { + try { + if (allParams != null) { + allParams.remove("fields"); + allParams.remove("offset"); + allParams.remove("limit"); + } else { + allParams = new HashMap<>(); + } + if ((fields == null) && (allParams.size() == 0)) { + + String myfields = null; + return new ResponseEntity>( + ruleSpecificationRepoService.findAll( myfields, allParams), HttpStatus.OK); + + + } else { + + + return new ResponseEntity>( + ruleSpecificationRepoService.findAll(fields, allParams), HttpStatus.OK); + } + + } catch (Exception e) { + log.error("Couldn't serialize response for content type application/json", e); + return new ResponseEntity>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/ActiveMQComponentConfig.java b/src/main/java/org/etsi/osl/oas/configuration/ActiveMQComponentConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1e87c60e1b5929d25474ebe0172b8a9bd5428d0f --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/ActiveMQComponentConfig.java @@ -0,0 +1,41 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.bugzilla + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + +import jakarta.jms.ConnectionFactory; + +import org.apache.camel.component.activemq.ActiveMQComponent; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author ctranoris + * + */ +@Configuration +public class ActiveMQComponentConfig { + + @Bean(name = "activemq") + public ActiveMQComponent createComponent(ConnectionFactory factory) { + ActiveMQComponent activeMQComponent = new ActiveMQComponent(); + activeMQComponent.setConnectionFactory(factory); + return activeMQComponent; + } +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/CustomClaimVerifier.java b/src/main/java/org/etsi/osl/oas/configuration/CustomClaimVerifier.java new file mode 100644 index 0000000000000000000000000000000000000000..16c553ac7dcb590517735cb565e01e347c97c58e --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/CustomClaimVerifier.java @@ -0,0 +1,35 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.portal.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + +import java.util.Map; + +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.token.store.JwtClaimsSetVerifier; + +public class CustomClaimVerifier implements JwtClaimsSetVerifier { + @Override + public void verify(Map claims) throws InvalidTokenException { + final String username = (String) claims.get("user_name"); + if ((username == null) || (username.length() == 0)) { + throw new InvalidTokenException("user_name claim is empty"); + } + } +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/HomeController.java b/src/main/java/org/etsi/osl/oas/configuration/HomeController.java new file mode 100644 index 0000000000000000000000000000000000000000..ee2cc5b60f430e75b9252b621fa0ce18ffc903bb --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/HomeController.java @@ -0,0 +1,35 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * Home redirection to swagger api documentation + */ +@Controller +public class HomeController { + @RequestMapping(value = "/") + public String index() { + System.out.println("swagger-ui.html"); + return "redirect:swagger-ui.html"; + } +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/MethodSecurityConfig.java b/src/main/java/org/etsi/osl/oas/configuration/MethodSecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..78a4354c171d3b1e36b1b10448ecca2a67dcba71 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/MethodSecurityConfig.java @@ -0,0 +1,39 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.portal.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; +import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler; + +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) +public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { + +// +// @Override +// protected MethodSecurityExpressionHandler createExpressionHandler() { +// return new OAuth2MethodSecurityExpressionHandler(); +// } + + +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/RestAuthenticationEntryPoint.java b/src/main/java/org/etsi/osl/oas/configuration/RestAuthenticationEntryPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..851e343b364ee522bfd9d584a190153d6dc60095 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/RestAuthenticationEntryPoint.java @@ -0,0 +1,56 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + +import java.io.IOException; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public final class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { + + /** + * From https://www.baeldung.com/securing-a-restful-web-service-with-spring-security + * + * In a standard web application, the authentication process may + * automatically trigger when an un-authenticated client tries to access a + * secured resource. This process usually redirects to a login page so that + * the user can enter credentials. However, for a REST Web Service,this + * behaviour doesn’t make much sense. We should be able to authenticate only + * by a request to the correct URI and if the user is not authenticated all + * requests should simply fail with a 401 UNAUTHORIZED status code. Spring + * Security handles this automatic triggering of the authentication process + * with the concept of an Entry Point – this is a required part of the + * configuration, and can be injected via the authenticationEntryPoint + * method. + */ + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException { + + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); + } +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/SwaggerDocumentationConfig.java b/src/main/java/org/etsi/osl/oas/configuration/SwaggerDocumentationConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..57ea90ca99648a53974bc1e9d600726503fa9e69 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/SwaggerDocumentationConfig.java @@ -0,0 +1,94 @@ +/*- +7 * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + +import java.util.Arrays; + +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springdoc.core.models.GroupedOpenApi; +import org.springdoc.core.utils.SpringDocUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.OAuthFlow; +import io.swagger.v3.oas.annotations.security.OAuthFlows; +import io.swagger.v3.oas.annotations.security.OAuthScope; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.models.SpecVersion; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityRequirement; + +/** + * + * @author ctranoris + * + */ + +@Configuration +@SecurityScheme(name = "security_auth", type = SecuritySchemeType.OAUTH2, bearerFormat = "JWT", +scheme = "bearer", +flows = @OAuthFlows(authorizationCode = @OAuthFlow( + authorizationUrl = "${springdoc.oAuthFlow.authorizationUrl}" + , tokenUrl = "${springdoc.oAuthFlow.tokenUrl}", scopes = { + @OAuthScope(name = "read", description = "read scope"), + @OAuthScope(name = "write", description = "write scope") }))) +public class SwaggerDocumentationConfig { + + + + OpenApiCustomizer apiInfoOasV1() { + + + + return openApi -> openApi + .specVersion( SpecVersion.V30 ).addSecurityItem(new SecurityRequirement().addList("security_auth")) + .info(new Info().title("Assurance Services Management") + .description("## Assurance Services Management " + + "### Release : 0.1 - Feb 2021 - Assurance Services Management " + + "### Operations on Assurance Services: Rules, Action") + + .version("0.1.0") + .license(new License().name("Apache 2.0").url("http://openslice.io"))); + + } + + + + + + + @Bean + public GroupedOpenApi customOasV1(){ + SpringDocUtils.getConfig().replaceWithClass(java.time.LocalDate.class, java.sql.Date.class); + SpringDocUtils.getConfig().replaceWithClass(java.time.OffsetDateTime.class, java.util.Date.class); + return GroupedOpenApi.builder() + .group("openslice-api-Assurance Services Management-v0.1.0") + .addOpenApiCustomizer( this.apiInfoOasV1() ) + .packagesToScan("org.etsi.osl.oas.api") + .build(); + + + } + + +} diff --git a/src/main/java/org/etsi/osl/oas/configuration/WebSecurityConfigKeycloak.java b/src/main/java/org/etsi/osl/oas/configuration/WebSecurityConfigKeycloak.java new file mode 100644 index 0000000000000000000000000000000000000000..b18e88315c00011e6154bf717717783eaa8f1fc6 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/configuration/WebSecurityConfigKeycloak.java @@ -0,0 +1,253 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.portal.api + * %% + * Copyright (C) 2019 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.configuration; + + +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.PathNotFoundException; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.convert.converter.Converter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationManagerResolver; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtDecoders; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.stereotype.Component; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import jakarta.servlet.http.HttpServletRequest; +import lombok.Data; +import lombok.RequiredArgsConstructor; + + +@Configuration +@EnableWebSecurity +@Profile("!testing") +public class WebSecurityConfigKeycloak { + + @Bean + SecurityFilterChain filterChain(HttpSecurity http, ServerProperties serverProperties, + @Value("${origins:[]}") String[] origins, @Value("${permit-all:[]}") String[] permitAll, + AuthenticationManagerResolver authenticationManagerResolver) throws Exception { + + http.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver)); + + // Enable and configure CORS + http.cors(cors -> cors.configurationSource(corsConfigurationSource(origins))); + + // State-less session (state in access-token only) + http.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + + // Disable CSRF because of state-less session-management + http.csrf(csrf -> csrf.disable()); + + // Return 401 (unauthorized) instead of 302 (redirect to login) when + // authorization is missing or invalid + http.exceptionHandling(eh -> eh.authenticationEntryPoint((request, response, authException) -> { + response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\""); + response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); + })); + + // If SSL enabled, disable http (https only) + if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) { + http.requiresChannel(channel -> channel.anyRequest().requiresSecure()); + } + + // @formatter:off + http.authorizeHttpRequests(requests -> requests + //.requestMatchers(permitAll).permitAll() + .anyRequest().permitAll()); + // @formatter:on + + return http.build(); + } + + private UrlBasedCorsConfigurationSource corsConfigurationSource(String[] origins) { + final var configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList(origins)); + configuration.setAllowedMethods(List.of("*")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setExposedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + + final var source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + @Data + @Configuration + @ConfigurationProperties(prefix = "spring-addons") + static class SpringAddonsProperties { + private IssuerProperties[] issuers = {}; + + @Data + static class IssuerProperties { + private URL uri; + + @NestedConfigurationProperty + private ClaimMappingProperties[] claims; + + private String usernameJsonPath = JwtClaimNames.SUB; + + @Data + static class ClaimMappingProperties { + private String jsonPath; + private CaseProcessing caseProcessing = CaseProcessing.UNCHANGED; + private String prefix = ""; + + static enum CaseProcessing { + UNCHANGED, TO_LOWER, TO_UPPER + } + } + } + + public IssuerProperties get(URL issuerUri) throws MisconfigurationException { + final var issuerProperties = Stream.of(issuers).filter(iss -> issuerUri.equals(iss.getUri())).toList(); + if (issuerProperties.size() == 0) { + throw new MisconfigurationException( + "Missing authorities mapping properties for %s".formatted(issuerUri.toString())); + } + if (issuerProperties.size() > 1) { + throw new MisconfigurationException( + "Too many authorities mapping properties for %s".formatted(issuerUri.toString())); + } + return issuerProperties.get(0); + } + + static class MisconfigurationException extends RuntimeException { + private static final long serialVersionUID = 5887967904749547431L; + + public MisconfigurationException(String msg) { + super(msg); + } + } + } + + @RequiredArgsConstructor + static class JwtGrantedAuthoritiesConverter implements Converter> { + private final SpringAddonsProperties.IssuerProperties properties; + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Collection convert(Jwt jwt) { + + + return Stream.of(properties.claims).flatMap(claimProperties -> { + Object claim; + try { + claim = JsonPath.read(jwt.getClaims(), claimProperties.jsonPath); + } catch (PathNotFoundException e) { + claim = null; + } + if (claim == null) { + return Stream.empty(); + } + if (claim instanceof String claimStr) { + return Stream.of(claimStr.split(",")); + } + if (claim instanceof String[] claimArr) { + return Stream.of(claimArr); + } + if (Collection.class.isAssignableFrom(claim.getClass())) { + final var iter = ((Collection) claim).iterator(); + if (!iter.hasNext()) { + return Stream.empty(); + } + final var firstItem = iter.next(); + if (firstItem instanceof String) { + return (Stream) ((Collection) claim).stream(); + } + if (Collection.class.isAssignableFrom(firstItem.getClass())) { + return (Stream) ((Collection) claim).stream() + .flatMap(colItem -> ((Collection) colItem).stream()).map(String.class::cast); + } + } + return Stream.empty(); + }) /* Insert some transformation here if you want to add a prefix like "ROLE_" or force upper-case authorities */ + + .map(s -> "ROLE_" + s) + .map(SimpleGrantedAuthority::new) + .map(GrantedAuthority.class::cast).toList(); + } + } + + @Component + @RequiredArgsConstructor + static class SpringAddonsJwtAuthenticationConverter implements Converter { + private final SpringAddonsProperties springAddonsProperties; + + @Override + public AbstractAuthenticationToken convert(Jwt jwt) { + final var issuerProperties = springAddonsProperties.get(jwt.getIssuer()); + final var authorities = new JwtGrantedAuthoritiesConverter(issuerProperties).convert(jwt); + final String username = JsonPath.read(jwt.getClaims(), issuerProperties.getUsernameJsonPath()); + return new JwtAuthenticationToken(jwt, authorities, username); + } + } + + @Bean + AuthenticationManagerResolver authenticationManagerResolver( + SpringAddonsProperties addonsProperties, SpringAddonsJwtAuthenticationConverter authenticationConverter) { + final Map authenticationProviders = Stream.of(addonsProperties.getIssuers()) + .map(SpringAddonsProperties.IssuerProperties::getUri).map(URL::toString) + .collect(Collectors.toMap(issuer -> issuer, + issuer -> authenticationProvider(issuer, authenticationConverter)::authenticate)); + return new JwtIssuerAuthenticationManagerResolver( + (AuthenticationManagerResolver) authenticationProviders::get); + } + + JwtAuthenticationProvider authenticationProvider(String issuer, + SpringAddonsJwtAuthenticationConverter authenticationConverter) { + JwtDecoder decoder = JwtDecoders.fromIssuerLocation(issuer); + var provider = new JwtAuthenticationProvider(decoder); + provider.setJwtAuthenticationConverter(authenticationConverter); + return provider; + } +} + diff --git a/src/main/java/org/etsi/osl/oas/model/Action.java b/src/main/java/org/etsi/osl/oas/model/Action.java new file mode 100644 index 0000000000000000000000000000000000000000..0f9725be28a50835b4c0769e70e362e49c6360cf --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/Action.java @@ -0,0 +1,48 @@ +package org.etsi.osl.oas.model; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.validation.Valid; +import lombok.Data; + +/** + * @author ctranoris + * + */ +@Schema(description = "Action element") +@Validated +@Entity(name = "OASAction") +@Data +public class Action { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + @JsonProperty("name") + protected String name = null; + + @JsonProperty("actionCharacteristics") + @Valid + @OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) + private Set actionCharacteristics = new HashSet<>(); + + @JsonProperty("actionSpecificationRef") + @Valid + @OneToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) + private ActionSpecificationRef actionSpecificationRef; +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionCharacteristic.java b/src/main/java/org/etsi/osl/oas/model/ActionCharacteristic.java new file mode 100644 index 0000000000000000000000000000000000000000..c3157b9dfd1fb397ff54dddb1e59bc5e781a30f5 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionCharacteristic.java @@ -0,0 +1,39 @@ +package org.etsi.osl.oas.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.persistence.Column; +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Data; + +/** + * @author ctranoris + * + * + */ +@Schema(description = "An ActionCharacteristic is an entity that describes the values of the characteristics of an action in a rule.") + +@Validated +@Entity(name = "OASActionCharacteristic") +@Data +public class ActionCharacteristic { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + + @JsonProperty("name") + protected String name = null; + + @JsonProperty("value") + @Column(name = "\"value\"") + protected String value = null; +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionExecutionStatus.java b/src/main/java/org/etsi/osl/oas/model/ActionExecutionStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..0681b6911b452d42320688adb03e1814c7c1cb18 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionExecutionStatus.java @@ -0,0 +1,16 @@ +package org.etsi.osl.oas.model; + +import lombok.Data; + +/** + * ActionExecutionStatus element. Keeps the status of the registered exec actions for specific alarm. Used to monitor if the alarm is cleared or not + * @author ctranoris + * + */ +@Data +public class ActionExecutionStatus { + + Action action ; + String serviceId ; + boolean actionfulfilled = false; +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionParam.java b/src/main/java/org/etsi/osl/oas/model/ActionParam.java new file mode 100644 index 0000000000000000000000000000000000000000..154da1753e27caa0bc807ae7f1d9e07f995d70de --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionParam.java @@ -0,0 +1,36 @@ +package org.etsi.osl.oas.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Data; + +/** + * @author ctranoris + * + */ +@Schema(description = "An ActionParam is an entity that describes parameteres of an action.") +@Validated +@Entity(name = "OASActionParam") +@Data +public class ActionParam { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + + @JsonProperty("paramName") + protected String paramName = null; + + + @JsonProperty("paramValue") + protected String paramValue = null; +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionSpecification.java b/src/main/java/org/etsi/osl/oas/model/ActionSpecification.java new file mode 100644 index 0000000000000000000000000000000000000000..cbff3feaae7d1a22b94cdd29b3d555d85c551356 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionSpecification.java @@ -0,0 +1,58 @@ +package org.etsi.osl.oas.model; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.OneToMany; +import jakarta.validation.Valid; +import lombok.Data; + +/** + * @author ctranoris + * + * An ActionSpecification is an entity that describes an action to perform on certain entities + * + */ +@Schema(description = "An ActionSpecification is an entity that describes an action to perform on certain entities.") +@Validated +@Entity(name = "OASActionSpec") +@Data +public class ActionSpecification { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + @JsonProperty("name") + protected String name = null; + + + @Lob + @Column(name = "LDESCRIPTION", columnDefinition = "LONGTEXT") + @JsonProperty("description") + protected String description = null; + + + @JsonProperty("params") + @Valid + @OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) + private Set params = new HashSet<>(); + + + + + +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionSpecificationCreate.java b/src/main/java/org/etsi/osl/oas/model/ActionSpecificationCreate.java new file mode 100644 index 0000000000000000000000000000000000000000..1d0d9834b1d3f8bc88bca2ba76aca6c3499f6e78 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionSpecificationCreate.java @@ -0,0 +1,17 @@ +package org.etsi.osl.oas.model; + +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * @author ctranoris + * + * An ActionSpecification is an entity that describes an action to perform on certain entities + * + */ +@Schema(description = "An ActionSpecification is an entity that describes an action to perform on certain entities.") +@Validated +public class ActionSpecificationCreate extends ActionSpecificationUpdate { + +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionSpecificationRef.java b/src/main/java/org/etsi/osl/oas/model/ActionSpecificationRef.java new file mode 100644 index 0000000000000000000000000000000000000000..3fc421b0f22f3272453d629ac5b74fde4c443d58 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionSpecificationRef.java @@ -0,0 +1,39 @@ +package org.etsi.osl.oas.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Data; + +/** + * @author ctranoris + * + *Action reference + */ +@Schema(description = "Action reference") +@Validated +@Entity(name = "OASActionSpecRef") +@JsonIgnoreProperties({ "uuid" }) +@Data +public class ActionSpecificationRef { + + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + @JsonProperty("actionId") + private String actionId = null; + + + + +} diff --git a/src/main/java/org/etsi/osl/oas/model/ActionSpecificationUpdate.java b/src/main/java/org/etsi/osl/oas/model/ActionSpecificationUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..7f08d248873b69e1081965bdecf8d58536d9aa88 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/ActionSpecificationUpdate.java @@ -0,0 +1,83 @@ +package org.etsi.osl.oas.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; + +/** + * @author ctranoris + * + * An ActionSpecification is an entity that describes an action to perform on certain entities + * + */ +@Schema(description = "An ActionSpecification is an entity that describes an action to perform on certain entities.") +@Validated +public class ActionSpecificationUpdate { + + + + @JsonProperty("name") + protected String name = null; + + + @JsonProperty("description") + protected String description = null; + + + @JsonProperty("params") + @Valid + private List params = new ArrayList<>(); + + + /** + * @return the params + */ + public List getParams() { + return params; + } + + /** + * @param params the params to set + */ + public void setParams(List params) { + this.params = params; + } + + /** + * The name of the service test + * + * @return name + **/ + @Schema(description = "The name of the entity") + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + /** + * Description of the service test + * + * @return description + **/ + @Schema(description = "Description of the entity") + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + +} diff --git a/src/main/java/org/etsi/osl/oas/model/Condition.java b/src/main/java/org/etsi/osl/oas/model/Condition.java new file mode 100644 index 0000000000000000000000000000000000000000..f7c57200c3989e8bc693051bf80b951193a4b08d --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/Condition.java @@ -0,0 +1,49 @@ +package org.etsi.osl.oas.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Data; + +/** + * @author ctranoris + * + * A Condition is an entity that describes the condition that must be tru in order to apply an action + * + */ +@Schema(description = "A Condition is an entity that describes the condition that must be tru in order to apply an action.") +@Validated +@Entity(name = "OASCondition") +@Data +public class Condition { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + @JsonProperty("booleanOperator") + String booleanOperator; + + @JsonProperty("eventAttributeName") + String opensliceEventAttributeName; + + @JsonProperty("eventAttributeValue") + String eventAttributeValue; + + + /** + * EQUAL + * NOTEQUAL + * GREATER_THAN + * LESS_THAN + */ + @JsonProperty("operator") + String operator; +} diff --git a/src/main/java/org/etsi/osl/oas/model/Error.java b/src/main/java/org/etsi/osl/oas/model/Error.java new file mode 100644 index 0000000000000000000000000000000000000000..03f505f7b5fb8c0d51d307e5b6ca740c05dd5aca --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/Error.java @@ -0,0 +1,171 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 - 2021 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.model; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +/** + * Used when an API throws an Error, typically with a HTTP error response-code (3xx, 4xx, 5xx) + */ +@Schema(description = "Used when an API throws an Error, typically with a HTTP error response-code (3xx, 4xx, 5xx)") +@Validated +@jakarta.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.SpringCodegen", date = "2021-02-08T11:50:07.226173200+02:00[Europe/Athens]") +public class Error { + @JsonProperty("code") + private String code = null; + + @JsonProperty("reason") + private String reason = null; + + @JsonProperty("message") + private String message = null; + + @JsonProperty("status") + private String status = null; + + + public Error code(String code) { + this.code = code; + return this; + } + + /** + * Application relevant detail, defined in the API or a common list. + * @return code + **/ + @Schema(required = true, description = "Application relevant detail, defined in the API or a common list.") + @NotNull + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Error reason(String reason) { + this.reason = reason; + return this; + } + + /** + * Explanation of the reason for the error which can be shown to a client user. + * @return reason + **/ + @Schema(required = true, description = "Explanation of the reason for the error which can be shown to a client user.") + @NotNull + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public Error message(String message) { + this.message = message; + return this; + } + + /** + * More details and corrective actions related to the error which can be shown to a client user. + * @return message + **/ + @Schema(description = "More details and corrective actions related to the error which can be shown to a client user.") + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Error status(String status) { + this.status = status; + return this; + } + + /** + * HTTP Error code extension + * @return status + **/ + @Schema(description = "HTTP Error code extension") + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Error error = (Error) o; + return Objects.equals(this.code, error.code) && + Objects.equals(this.reason, error.reason) && + Objects.equals(this.message, error.message) && + Objects.equals(this.status, error.status) ; + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Error {\n"); + + sb.append(" code: ").append(toIndentedString(code)).append("\n"); + sb.append(" reason: ").append(toIndentedString(reason)).append("\n"); + sb.append(" message: ").append(toIndentedString(message)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/src/main/java/org/etsi/osl/oas/model/RuleSpecification.java b/src/main/java/org/etsi/osl/oas/model/RuleSpecification.java new file mode 100644 index 0000000000000000000000000000000000000000..5684c7053dce0f47ff526508f877e9548742386c --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/RuleSpecification.java @@ -0,0 +1,144 @@ +package org.etsi.osl.oas.model; + +import java.util.HashSet; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.hibernate.annotations.GenericGenerator; +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.OneToMany; +import jakarta.validation.Valid; +import lombok.Data; + +/** + * @author ctranoris + * + * A RuleSpecification is an entity that describes a rule to apply an action on certain conditions in the context of a service + * + */ +@Schema(description = "A RuleSpecification is an entity that describes a rule to apply an action on certain conditions in the context of a service.") +@Validated +@Entity(name = "OASRuleSpec") +@Data +public class RuleSpecification { + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + protected String uuid = null; + + @JsonProperty("name") + protected String name = null; + + @Lob + @Column(name = "LDESCRIPTION", columnDefinition = "LONGTEXT") + @JsonProperty("description") + protected String description = null; + + @JsonProperty("eventType") + String opensliceEventType; + + @JsonProperty("actions") + @Valid + @OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) + private Set actions = new HashSet<>(); + + @JsonProperty("scope") + Scope scope; + + @JsonProperty("condition") + @Valid + @OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }) + private Set condition = new HashSet<>(); + + public RuleSpecification name(String name) { + this.name = name; + return this; + } + + + + /** + * Name of the entity + * + * @return name + **/ + @Schema(description = "Name of the entity") + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * @return the uuid + */ + public String getUuid() { + return uuid; + } + + /** + * @param uuid the uuid to set + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + + /** + * Description of this entity + * + * @return description + **/ + @Schema(description = "Description of this entity") + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Entity {\n"); + + sb.append(" uuid: ").append(toIndentedString(uuid)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + public void copyFromObj(RuleSpecification be) { + this.uuid = be.uuid; + this.name = be.name; + + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/src/main/java/org/etsi/osl/oas/model/RuleSpecificationCreate.java b/src/main/java/org/etsi/osl/oas/model/RuleSpecificationCreate.java new file mode 100644 index 0000000000000000000000000000000000000000..bed0d7ebd462a6157b052ed03cb100652559d4fe --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/RuleSpecificationCreate.java @@ -0,0 +1,24 @@ +package org.etsi.osl.oas.model; + +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author ctranoris + * + * A RuleSpecification is an entity that describes a rule to apply an action on certain conditions in the context of a service + * + */ +@Schema(description = "A RuleSpecification is an entity that describes a rule to apply an action on certain conditions in the context of a service.") +@Validated +@Data +@EqualsAndHashCode(callSuper=true) +public class RuleSpecificationCreate extends RuleSpecificationUpdate { + + + + +} diff --git a/src/main/java/org/etsi/osl/oas/model/RuleSpecificationUpdate.java b/src/main/java/org/etsi/osl/oas/model/RuleSpecificationUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..397027685e09a1aa3f23190f8e09be7861001823 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/RuleSpecificationUpdate.java @@ -0,0 +1,47 @@ +package org.etsi.osl.oas.model; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import lombok.Data; + +/** + * @author ctranoris + * + * A RuleSpecification is an entity that describes a rule to apply an action on certain conditions in the context of a service + * + */ +@Schema(description = "A RuleSpecification is an entity that describes a rule to apply an action on certain conditions in the context of a service.") +@Validated +@Data +public class RuleSpecificationUpdate { + + + + @JsonProperty("name") + protected String name = null; + + @JsonProperty("description") + protected String description = null; + + @JsonProperty("eventType") + String opensliceEventType; + + + @JsonProperty("actions") + @Valid + private List actions = new ArrayList<>(); + + @JsonProperty("scope") + Scope scope; + + @JsonProperty("condition") + @Valid + private List condition = new ArrayList<>(); +} diff --git a/src/main/java/org/etsi/osl/oas/model/Scope.java b/src/main/java/org/etsi/osl/oas/model/Scope.java new file mode 100644 index 0000000000000000000000000000000000000000..a9c3b6a123a0254074c14f730ab8f2dea8fb6a3e --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/model/Scope.java @@ -0,0 +1,26 @@ +package org.etsi.osl.oas.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.springframework.validation.annotation.Validated; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.Embeddable; +import lombok.Data; + +/** + * @author ctranoris + * + * The scope that the Rule is related + * + */ +@Schema(description = "The scope that the Rule is related.") +@Validated +@Data +@Embeddable +public class Scope { + + @JsonProperty("entityUUID") + String entityUUID; + +} diff --git a/src/main/java/org/etsi/osl/oas/repo/ActionSpecificationRepository.java b/src/main/java/org/etsi/osl/oas/repo/ActionSpecificationRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..62a609ae2f3d29d76ac723bdca10cd25b7adbdb3 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/repo/ActionSpecificationRepository.java @@ -0,0 +1,34 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 - 2021 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.repo; + +import java.util.Optional; +import org.etsi.osl.oas.model.ActionSpecification; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ActionSpecificationRepository extends CrudRepository { + + + Optional findByUuid(String id); + + +} diff --git a/src/main/java/org/etsi/osl/oas/repo/RuleSpecificationRepository.java b/src/main/java/org/etsi/osl/oas/repo/RuleSpecificationRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..114001fbc45de266804e7eb8cfe72e3a6949a180 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/repo/RuleSpecificationRepository.java @@ -0,0 +1,38 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 - 2021 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas.repo; + +import java.util.List; +import java.util.Optional; +import org.etsi.osl.oas.model.RuleSpecification; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RuleSpecificationRepository extends CrudRepository { + + + Optional findByUuid(String id); + + + List findByScopeEntityUUID(String uuid); + + +} diff --git a/src/main/java/org/etsi/osl/oas/reposervices/ActionSpecificationRepoService.java b/src/main/java/org/etsi/osl/oas/reposervices/ActionSpecificationRepoService.java new file mode 100644 index 0000000000000000000000000000000000000000..10269b3299c199b0f1d84a81b2e6759a355e6c6c --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/reposervices/ActionSpecificationRepoService.java @@ -0,0 +1,142 @@ +package org.etsi.osl.oas.reposervices; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.etsi.osl.oas.model.ActionParam; +import org.etsi.osl.oas.model.ActionSpecification; +import org.etsi.osl.oas.model.ActionSpecificationCreate; +import org.etsi.osl.oas.model.ActionSpecificationUpdate; +import org.etsi.osl.oas.repo.ActionSpecificationRepository; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import jakarta.persistence.EntityManagerFactory; +import jakarta.validation.Valid; + + +@Service +public class ActionSpecificationRepoService { + + + @Autowired + ActionSpecificationRepository actionSpecificationRepository; + + + private SessionFactory sessionFactory; + + @Autowired + public ActionSpecificationRepoService(EntityManagerFactory factory) { + if (factory.unwrap(SessionFactory.class) == null) { + throw new NullPointerException("factory is not a hibernate factory"); + } + this.sessionFactory = factory.unwrap(SessionFactory.class); + } + + + + @Transactional + public ActionSpecification addActionSpecification(@Valid ActionSpecificationCreate actionSpecificationCreate) { + + ActionSpecification as = new ActionSpecification(); + as = updateActionSpecificationFromAPICall( as, actionSpecificationCreate); + as = this.actionSpecificationRepository.save(as); + return as; + } + + + + public List findAll() { + return (List) this.actionSpecificationRepository.findAll(); + } + + + public Void deleteById(String id) { + Optional optionalCat = this.actionSpecificationRepository.findByUuid(id); + this.actionSpecificationRepository.delete(optionalCat.get()); + return null; + } + + + @Transactional + public ActionSpecification updateActionSpecification(String id, @Valid ActionSpecificationUpdate body) { + + ActionSpecification s = this.findByUuid(id); + if (s == null) { + return null; + } + ActionSpecification alm = s; + alm = this.updateActionSpecificationFromAPICall(alm, body); + + alm = this.actionSpecificationRepository.save(alm); + return alm; + } + + + public ActionSpecification findByUuid(String id) { + Optional optionalCat = this.actionSpecificationRepository.findByUuid(id); + return optionalCat.orElse(null); + } + + public List findAll(String myfields, @Valid Map allParams) { + return findAll(); + } + + public ActionSpecification findById(String id) { + Optional optionalCat = this.actionSpecificationRepository.findByUuid(id); + return optionalCat.orElse(null); + } + + + private ActionSpecification updateActionSpecificationFromAPICall(ActionSpecification as, + @Valid ActionSpecificationUpdate actionSpecificationUpdate) { + + if ( actionSpecificationUpdate.getName() != null) { + as.setName( actionSpecificationUpdate.getName() ); + } + + if ( actionSpecificationUpdate.getDescription() != null) { + as.setDescription( actionSpecificationUpdate.getDescription() ); + } + + + if ( actionSpecificationUpdate.getParams() != null) { + Map idAddedUpdated = new HashMap<>(); + + for (ActionParam ar : actionSpecificationUpdate.getParams() ) { + // find by id and reload it here. + + boolean idexists = false; + for (ActionParam orinalCom : as.getParams()) { + if (ar.getUuid()!=null && orinalCom.getUuid().equals(ar.getUuid())) { + idexists = true; + idAddedUpdated.put(orinalCom.getUuid(), true); + break; + } + } + + if (!idexists) { + as.getParams().add(ar); + idAddedUpdated.put(ar.getUuid(), true); + } + } + + List toRemove = new ArrayList<>(); + for (ActionParam ss : as.getParams()) { + if (idAddedUpdated.get(ss.getUuid()) == null) { + toRemove.add(ss); + } + } + + for (ActionParam ar : toRemove) { + as.getParams().remove(ar); + } + + } + + return as; + } +} diff --git a/src/main/java/org/etsi/osl/oas/reposervices/RuleSpecificationRepoService.java b/src/main/java/org/etsi/osl/oas/reposervices/RuleSpecificationRepoService.java new file mode 100644 index 0000000000000000000000000000000000000000..7e041ac6876cc5a3e74ef0fd62a89f729b279162 --- /dev/null +++ b/src/main/java/org/etsi/osl/oas/reposervices/RuleSpecificationRepoService.java @@ -0,0 +1,201 @@ +package org.etsi.osl.oas.reposervices; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.etsi.osl.oas.model.Action; +import org.etsi.osl.oas.model.Condition; +import org.etsi.osl.oas.model.RuleSpecification; +import org.etsi.osl.oas.model.RuleSpecificationCreate; +import org.etsi.osl.oas.model.RuleSpecificationUpdate; +import org.etsi.osl.oas.repo.RuleSpecificationRepository; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import jakarta.persistence.EntityManagerFactory; +import jakarta.validation.Valid; + + +@Service +public class RuleSpecificationRepoService { + + + @Autowired + RuleSpecificationRepository ruleSpecificationRepository; + + + private SessionFactory sessionFactory; + + @Autowired + public RuleSpecificationRepoService(EntityManagerFactory factory) { + if (factory.unwrap(SessionFactory.class) == null) { + throw new NullPointerException("factory is not a hibernate factory"); + } + this.sessionFactory = factory.unwrap(SessionFactory.class); + } + + + + @Transactional + public RuleSpecification addRuleSpecification(@Valid RuleSpecificationCreate ruleSpecificationCreate) { + + RuleSpecification as = new RuleSpecification(); + as = updateRuleSpecificationFromAPICall( as, ruleSpecificationCreate); + as = this.ruleSpecificationRepository.save(as); + return as; + } + + + + public List findAll() { + return (List) this.ruleSpecificationRepository.findAll(); + } + + + public Void deleteById(String id) { + Optional optionalCat = this.ruleSpecificationRepository.findByUuid(id); + this.ruleSpecificationRepository.delete(optionalCat.get()); + return null; + } + + + @Transactional + public RuleSpecification updateRuleSpecification(String id, @Valid RuleSpecificationUpdate body) { + + RuleSpecification s = this.findByUuid(id); + if (s == null) { + return null; + } + RuleSpecification alm = s; + alm = this.updateRuleSpecificationFromAPICall(alm, body); + + alm = this.ruleSpecificationRepository.save(alm); + return alm; + } + + + @Transactional + public RuleSpecification findByUuid(String id) { + Optional optionalCat = this.ruleSpecificationRepository.findByUuid(id); + return optionalCat.orElse(null); + } + + public List findAll(String myfields, @Valid Map allParams) { + return findAll(); + } + + + @Transactional + public RuleSpecification findById(String id) { + Optional optionalCat = this.ruleSpecificationRepository.findByUuid(id); + optionalCat.get().getActions(); + optionalCat.get().getCondition() ; + return optionalCat.orElse(null); + } + + @Transactional + public List findByScopeUuid(String uuid) { + return this.ruleSpecificationRepository.findByScopeEntityUUID( uuid ); + } + + + private RuleSpecification updateRuleSpecificationFromAPICall(RuleSpecification as, + @Valid RuleSpecificationUpdate ruleSpecificationUpdate) { + + if ( ruleSpecificationUpdate.getName() != null) { + as.setName( ruleSpecificationUpdate.getName() ); + } + + if ( ruleSpecificationUpdate.getDescription() != null) { + as.setDescription( ruleSpecificationUpdate.getDescription() ); + } + + if ( ruleSpecificationUpdate.getOpensliceEventType() != null) { + as.setOpensliceEventType( ruleSpecificationUpdate.getOpensliceEventType() ); + + } + + if ( ruleSpecificationUpdate.getScope() != null) { + as.setScope( ruleSpecificationUpdate.getScope() ); + + } + + if ( ruleSpecificationUpdate.getCondition() != null) { + Map idAddedUpdated = new HashMap<>(); + + for (Condition ar : ruleSpecificationUpdate.getCondition()) { + // find by id and reload it here. + + boolean idexists = false; + for (Condition orinalCom : as.getCondition()) { + if (ar.getUuid()!=null && orinalCom.getUuid().equals(ar.getUuid())) { + idexists = true; + idAddedUpdated.put(orinalCom.getUuid(), true); + break; + } + } + + if (!idexists) { + as.getCondition().add(ar); + idAddedUpdated.put(ar.getUuid(), true); + } + } + + List toRemove = new ArrayList<>(); + for (Condition ss : as.getCondition()) { + if (idAddedUpdated.get(ss.getUuid()) == null) { + toRemove.add(ss); + } + } + + for (Condition ar : toRemove) { + as.getCondition().remove(ar); + } + + } + + if ( ruleSpecificationUpdate.getActions() != null) { + Map idAddedUpdated = new HashMap<>(); + + for (Action ar : ruleSpecificationUpdate.getActions()) { + // find by id and reload it here. + + boolean idexists = false; + for (Action orinalCom : as.getActions()) { + if ( ar.getUuid() != null) { + if (orinalCom.getUuid().equals(ar.getUuid())) { + idexists = true; + idAddedUpdated.put(orinalCom.getUuid(), true); + break; + } + } + } + + if (!idexists) { + as.getActions().add(ar); + idAddedUpdated.put(ar.getUuid(), true); + } + } + + List toRemove = new ArrayList<>(); + for (Action ss : as.getActions()) { + if (idAddedUpdated.get(ss.getUuid()) == null) { + toRemove.add(ss); + } + } + + for (Action ar : toRemove) { + as.getActions().remove(ar); + } + } + + return as; + } + + + + +} diff --git a/src/main/resources/application-testing.yml b/src/main/resources/application-testing.yml new file mode 100644 index 0000000000000000000000000000000000000000..fec39294a5533a3c303d5e1853390c43dcaf1aca --- /dev/null +++ b/src/main/resources/application-testing.yml @@ -0,0 +1,118 @@ +spring: + application: + name: openslice-assurance-services-management-api + # datasource: + #url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 + #password: sa + #username: sa + #jpa: + #database-platform: org.hibernate.dialect.H2Dialect + #hibernate: + #ddl-auto: create-drop + #hbm2ddl.auto: create-drop + #show-sql: false + #generate-ddl: true + # Embedded ActiveMQ Configuration Example + activemq: + broker-url: vm://embedded?broker.persistent=false,useShutdownHook=false + in-memory: true + non-blocking-redelivery: true + pool: + block-if-full: true + block-if-full-timeout: -1 + create-connection-on-startup: true + enabled: false + expiry-timeout: 0 + idle-timeout: 30000 + max-connections: 1 + maximum-active-session-per-connection: 500 + reconnect-on-exception: true + time-between-expiration-check: -1 + use-anonymous-producers: true + # Spring JMS Settings + jms: + listener: + acknowledge-mode: auto + auto-startup: true + concurrency: 5 + max-concurrency: 10 + pub-sub-domain: false + template: + default-destination: + delivery-mode: non_persistent + priority: 100 + qos-enabled: true + receive-timeout: 1000 + time-to-live: 36000 + +logging: + level: + root: INFO + org.springframework: INFO + org.hibernate.SQL: INFO + org.hibernate.type.descriptor.sql.BasicBinder: INFO + org.etsi.osl.oas: DEBUG + pattern: + console: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" + file: "%d %p %c{1.} [%t] %m%n" + +swagger: + authserver: http://localhost/auth/realms/openslice + clientid: "osapiWebClientId" + clientsecret: "secret" + +oauthsign: + key: "XXX" + + + + +CATALOG_GET_SERVICEORDERS: "jms:queue:CATALOG.GET.SERVICEORDERS" +CATALOG_GET_SERVICEORDER_BY_ID: "jms:queue:CATALOG.GET.SERVICEORDER_BY_ID" +CATALOG_UPD_SERVICEORDER_BY_ID: "jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID" +CATALOG_GET_INITIAL_SERVICEORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS" +CATALOG_GET_SERVICEORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS" +CATALOG_ADD_SERVICE: "jms:queue:CATALOG.ADD.SERVICE" +CATALOG_UPD_SERVICE: "jms:queue:CATALOG.UPD.SERVICE" +CATALOG_GET_SERVICE_BY_ID: "jms:queue:CATALOG.GET.SERVICE" +CATALOG_SERVICE_QUEUE_ITEMS_GET: "jms:queue:CATALOG.SERVICEQUEUEITEMS.GET" +CATALOG_SERVICE_QUEUE_ITEM_UPD: "jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE" +CATALOG_SERVICE_QUEUE_ITEM_DELETE: "jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE" +CATALOG_GET_SERVICESPEC_BY_ID: "jms:queue:CATALOG.GET.SERVICESPEC_BY_ID" +CATALOG_SERVICES_TO_TERMINATE: "jms:queue:CATALOG.GET.SERVICETOTERMINATE" +CATALOG_SERVICES_OF_PARTNERS: "jms:queue:CATALOG.GET.SERVICESOFPARTNERS" + +CATALOG_GET_PARTNER_ORGANIZATON_BY_ID: "jms:queue:CATALOG.GET.PARTNER_ORGANIZATION_BY_ID" +CATALOG_UPDATE_PARTNER_ORGANIZATION: "jms:queue:CATALOG.UPD.PARTNER_ORGANIZATION" +CATALOG_GET_EXTERNAL_SERVICE_PARTNERS: "jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS" +CATALOG_UPD_EXTERNAL_SERVICESPEC: "jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC" + +NFV_CATALOG_DEPLOY_NSD_REQ: "jms:queue:NFVCATALOG.DEPLOY.NSD_REQ" +NFV_CATALOG_GET_DEPLOYMENT_BY_ID : "jms:queue:NFVCATALOG.GET.DEPLOYMENT_BY_ID" +NFV_CATALOG_GET_NSD_BY_ID: "jms:queue:NFVCATALOG.GET.NSD_BY_ID" +NFV_CATALOG_UPD_DEPLOYMENT_BY_ID: "jms:queue:NFVCATALOG.UPD.DEPLOYMENT_BY_ID" +NFV_CATALOG_NS_DAY2_ACTION: "jms:queue:ns.action.run" + + + +#ALARMS +ALARMS_ADD_ALARM: "jms:queue:ALARMS.ADD.ALARM" +ALARMS_UPDATE_ALARM: "jms:queue:ALARMS.UPDATE.ALARM" +ALARMS_GET_ALARM: "jms:queue:ALARMS.GET.ALARM" + +#EVENT TOPICS IN Message Bus +EVENT_SERVICE_CREATE: "jms:topic:EVENT.SERVICE.CREATE" +EVENT_SERVICE_STATE_CHANGED: "jms:topic:EVENT.SERVICE.STATECHANGED" +EVENT_SERVICE_DELETE: "jms:topic:EVENT.SERVICE.DELETE" +EVENT_SERVICE_ATTRIBUTE_VALUE_CHANGED: "jms:topic:EVENT.SERVICE.ATTRCHANGED" +EVENT_SERVICE_ORDER_CREATE: "jms:topic:EVENT.SERVICEORDER.CREATE" +EVENT_SERVICE_ORDER_STATE_CHANGED: "jms:topic:EVENT.SERVICEORDER.STATECHANGED" +EVENT_SERVICE_ORDER_DELETE: "jms:topic:EVENT.SERVICEORDER.DELETE" +EVENT_SERVICE_ORDER_ATTRIBUTE_VALUE_CHANGED: "jms:topic:EVENT.SERVICEORDER.ATTRCHANGED" +EVENT_CUSTOMER_CREATE: "jms:topic:EVENT.CUSTOMER.CREATE" +EVENT_CUSTOMER_CHANGED: "jms:topic:EVENT.CUSTOMER.CHANGE" +EVENT_INDIVIDUAL_CREATE: "jms:topic:EVENT.INDIVIDUAL.CREATE" +EVENT_INDIVIDUAL_CHANGED: "jms:topic:EVENT.INDIVIDUAL.CHANGE" +EVENT_ORGANIZATION_CREATE: "jms:topic:EVENT.ORGANIZATION.CREATE" +EVENT_ORGANIZATION_CHANGED: "jms:topic:EVENT.ORGANIZATION.CHANGE" +EVENT_ALARM_CREATE: "jms:topic:EVENT.ALARM.CREATE" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..2311c1441d89f4aab52d46d477bb22a96823ceb4 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,164 @@ +origins: http://localhost,http://localhost:13082,http://127.0.0.1:13082,https://localhost, + + +server: + port: 13101 + servlet: + context-path : /oas-api + + +spring: + config: + activate: + on-profile: "default" + application: + name: openslice-assurance-services-management-api + datasource: + url: jdbc:mysql://localhost:13306/oasdb?createDatabaseIfNotExist=true&useUnicode=true&nullCatalogMeansCurrent=true&characterEncoding=utf8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC + password: letmein + username: root + databaseSchemaUpdate: true + jpa: + database-platform: org.etsi.osl.oas.LocalMysqlDialect + hibernate: + ddl-auto: update + show-sql: false + generate-ddl: true + properties.hibernate.current_session_context_class: org.springframework.orm.hibernate5.SpringSessionContext + properties: + hibernate: + connection: + characterEncoding: utf-8 + CharSet: utf-8 + useUnicode: true + activemq: + brokerUrl: tcp://localhost:61616?jms.watchTopicAdvisories=false + user: artemis + password: artemis + pool: + enabled: true + max-connections: 100 + packages: + trust-all: true + security: + oauth2: + resourceserver: + jwt: + issuer-uri: http://keycloak:8080/auth/realms/openslice + + +logging: + level: + root: INFO + org.etsi.osl.oas: DEBUG + org.springframework: INFO + org.apache.camel: INFO + reactor.netty.tcp.TcpClient: INFO + reactor.netty.http.client.HttpClient: INFO + pattern: + console: "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" + file: "%d %p %c{1.} [%t] %m%n" + +springdoc: + version: '@springdoc.version@' + writer-with-default-pretty-printer: true + swagger-ui: + display-request-duration: true + groups-order: ASC + operationsSorter: method + disable-swagger-default-url: true + use-root-path: true + oauth: + client-id: osapiWebClientId + clientsecret: "secret" + use-pkce-with-authorization-code-grant: false + oAuthFlow: + authorizationUrl: http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/auth + tokenUrl: http://keycloak:8080/auth/realms/openslice/protocol/openid-connect/token + show-actuator: true + +spring-addons: + issuers: + - uri: http://keycloak:8080/auth/realms/openslice + username-json-path: $.preferred_username + claims: + - jsonPath: $.realm_access.roles + - jsonPath: $.resource_access.*.roles + + + +# =================================================================== +# Common Flowable Spring Boot Properties +# SEE: https://flowable.com/open-source/docs/bpmn/ch05a-Spring-Boot/ +# This sample file is provided as a guideline. Do NOT copy it in its +# entirety to your own application. ^^^ +# =================================================================== + +flowable: + database-schema-update: true + +# Core (Process) https://github.com/flowable/flowable-engine/tree/master/modules/flowable-spring-boot/flowable-spring-boot-starters/flowable-spring-boot-autoconfigure/src/main/java/org/flowable/spring/boot/FlowableProperties.java +#flowable.check-process-definitions: true # Whether process definitions need to be auto deployed. +#flowable.database-schema= # In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly. +#flowable.db-history-used=true # Whether db history should be used. +#flowable.deployment-name=SpringBootAutoDeployment # The name of the auto deployment. +#flowable.history-level=audit # The history level that needs to be used. +#flowable.process-definition-location-prefix=classpath*:/processes/ # The folder in which processes need to be searched for auto deployment. +#flowable.process-definition-location-suffixes=**.bpmn20.xml,**.bpmn # The suffixes (extensions) of the files that needs to be deployed from the 'processDefinitionLocationPrefix' location. + + + +CATALOG_GET_SERVICEORDERS: "jms:queue:CATALOG.GET.SERVICEORDERS" +CATALOG_GET_SERVICEORDER_BY_ID: "jms:queue:CATALOG.GET.SERVICEORDER_BY_ID" +CATALOG_UPD_SERVICEORDER_BY_ID: "jms:queue:CATALOG.UPD.SERVICEORDER_BY_ID" +CATALOG_ADD_SERVICEORDER: "jms:queue:CATALOG.ADD.SERVICEORDER" +CATALOG_GET_INITIAL_SERVICEORDERS_IDS: "jms:queue:CATALOG.GET.INITIAL_SERVICEORDERS" +CATALOG_GET_SERVICEORDER_IDS_BY_STATE: "jms:queue:CATALOG.GET.ACKNOWLEDGED_SERVICEORDERS" +CATALOG_ADD_SERVICE: "jms:queue:CATALOG.ADD.SERVICE" +CATALOG_UPD_SERVICE: "jms:queue:CATALOG.UPD.SERVICE" +CATALOG_GET_SERVICE_BY_ID: "jms:queue:CATALOG.GET.SERVICE" +CATALOG_GET_SERVICE_BY_ORDERID: "jms:queue:CATALOG.GET.SERVICE_BY_ORDERID" +CATALOG_SERVICE_QUEUE_ITEMS_GET: "jms:queue:CATALOG.SERVICEQUEUEITEMS.GET" +CATALOG_SERVICE_QUEUE_ITEM_UPD: "jms:queue:CATALOG.SERVICEQUEUEITEM.UPDATE" +CATALOG_SERVICE_QUEUE_ITEM_DELETE: "jms:queue:CATALOG.SERVICEQUEUEITEM.DELETE" +CATALOG_GET_SERVICESPEC_BY_ID: "jms:queue:CATALOG.GET.SERVICESPEC_BY_ID" +CATALOG_SERVICES_TO_TERMINATE: "jms:queue:CATALOG.GET.SERVICETOTERMINATE" +CATALOG_SERVICES_OF_PARTNERS: "jms:queue:CATALOG.GET.SERVICESOFPARTNERS" + + +CATALOG_GET_PARTNER_ORGANIZATON_BY_ID: "jms:queue:CATALOG.GET.PARTNER_ORGANIZATION_BY_ID" +CATALOG_UPDATE_PARTNER_ORGANIZATION: "jms:queue:CATALOG.UPD.PARTNER_ORGANIZATION" +CATALOG_GET_EXTERNAL_SERVICE_PARTNERS: "jms:queue:CATALOG.GET.EXTERNALSERVICEPARTNERS" +CATALOG_UPD_EXTERNAL_SERVICESPEC: "jms:queue:CATALOG.UPD.EXTERNAL_SERVICESPEC" + +NFV_CATALOG_DEPLOY_NSD_REQ: "jms:queue:NFVCATALOG.DEPLOY.NSD_REQ" +NFV_CATALOG_GET_DEPLOYMENT_BY_ID : "jms:queue:NFVCATALOG.GET.DEPLOYMENT_BY_ID" +NFV_CATALOG_GET_NSD_BY_ID: "jms:queue:NFVCATALOG.GET.NSD_BY_ID" +NFV_CATALOG_UPD_DEPLOYMENT_BY_ID: "jms:queue:NFVCATALOG.UPD.DEPLOYMENT_BY_ID" +NFV_CATALOG_NS_DAY2_ACTION: "jms:queue:ns.action.run" + + + +#ALARMS +ALARMS_ADD_ALARM: "jms:queue:ALARMS.ADD.ALARM" +ALARMS_UPDATE_ALARM: "jms:queue:ALARMS.UPDATE.ALARM" +ALARMS_GET_ALARM: "jms:queue:ALARMS.GET.ALARM" + +#EVENT TOPICS IN Message Bus +EVENT_SERVICE_CREATE: "jms:topic:EVENT.SERVICE.CREATE" +EVENT_SERVICE_STATE_CHANGED: "jms:topic:EVENT.SERVICE.STATECHANGED" +EVENT_SERVICE_DELETE: "jms:topic:EVENT.SERVICE.DELETE" +EVENT_SERVICE_ATTRIBUTE_VALUE_CHANGED: "jms:topic:EVENT.SERVICE.ATTRCHANGED" +EVENT_SERVICE_ORDER_CREATE: "jms:topic:EVENT.SERVICEORDER.CREATE" +EVENT_SERVICE_ORDER_STATE_CHANGED: "jms:topic:EVENT.SERVICEORDER.STATECHANGED" +EVENT_SERVICE_ORDER_DELETE: "jms:topic:EVENT.SERVICEORDER.DELETE" +EVENT_SERVICE_ORDER_ATTRIBUTE_VALUE_CHANGED: "jms:topic:EVENT.SERVICEORDER.ATTRCHANGED" +EVENT_CUSTOMER_CREATE: "jms:topic:EVENT.CUSTOMER.CREATE" +EVENT_CUSTOMER_CHANGED: "jms:topic:EVENT.CUSTOMER.CHANGE" +EVENT_INDIVIDUAL_CREATE: "jms:topic:EVENT.INDIVIDUAL.CREATE" +EVENT_INDIVIDUAL_CHANGED: "jms:topic:EVENT.INDIVIDUAL.CHANGE" +EVENT_ORGANIZATION_CREATE: "jms:topic:EVENT.ORGANIZATION.CREATE" +EVENT_ORGANIZATION_CHANGED: "jms:topic:EVENT.ORGANIZATION.CHANGE" +EVENT_ALARM_CREATE: "jms:topic:EVENT.ALARM.CREATE" + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..6a53ea9eb2a42dc6edf5c205a975cb80b15b7dae --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + app.log + + + logs/archived/app.%d{yyyy-MM-dd}.%i.log + + 10MB + + 20GB + + 60 + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + + + + diff --git a/src/main/resources/processes/CheckServiceActionStatusProcess.bpmn b/src/main/resources/processes/CheckServiceActionStatusProcess.bpmn new file mode 100644 index 0000000000000000000000000000000000000000..86b66f304a4d382d51a831f283247a3b6f098f0d --- /dev/null +++ b/src/main/resources/processes/CheckServiceActionStatusProcess.bpmn @@ -0,0 +1,35 @@ + + + + + + 0/30 * * * * ? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/etsi/osl/oas/RulesIntegrationTest.java b/src/test/java/org/etsi/osl/oas/RulesIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fcb31d3b791bf6efbd2bb03365b0e9e551b5a0c4 --- /dev/null +++ b/src/test/java/org/etsi/osl/oas/RulesIntegrationTest.java @@ -0,0 +1,428 @@ +/*- + * ========================LICENSE_START================================= + * org.etsi.osl.tmf.api + * %% + * Copyright (C) 2019 - 2021 openslice.io + * %% + * 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. + * =========================LICENSE_END================================== + */ +package org.etsi.osl.oas; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.etsi.osl.oas.model.Action; +import org.etsi.osl.oas.model.ActionCharacteristic; +import org.etsi.osl.oas.model.ActionParam; +import org.etsi.osl.oas.model.ActionSpecification; +import org.etsi.osl.oas.model.ActionSpecificationCreate; +import org.etsi.osl.oas.model.ActionSpecificationRef; +import org.etsi.osl.oas.model.Condition; +import org.etsi.osl.oas.model.RuleSpecification; +import org.etsi.osl.oas.model.RuleSpecificationCreate; +import org.etsi.osl.oas.model.RuleSpecificationUpdate; +import org.etsi.osl.oas.model.Scope; +import org.etsi.osl.oas.reposervices.ActionSpecificationRepoService; +import org.etsi.osl.oas.reposervices.RuleSpecificationRepoService; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.etsi.osl.tmf.am642.model.AffectedService; +import org.etsi.osl.tmf.am642.model.Alarm; +import lombok.extern.apachecommons.CommonsLog; + +@RunWith(SpringRunner.class) +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = OasSpingBoot.class) +@AutoConfigureMockMvc +@ActiveProfiles("testing") +@CommonsLog +public class RulesIntegrationTest { + + @Autowired + private MockMvc mvc; + + @Autowired + RuleSpecificationRepoService ruleSpecificationRepoService; + + @Autowired + ActionSpecificationRepoService actionSpecificationRepoService; + + @Autowired + private WebApplicationContext context; + + @Autowired + private AlarmHandling alarmHandling; + + @Before + public void setup() { + mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build(); + } + + @WithMockUser(username = "osadmin", roles = { "ADMIN", "USER" }) + @Test + public void testRuleCreateAndUpdate() throws Exception { + + ActionSpecificationCreate actionCreate = new ActionSpecificationCreate(); + actionCreate.setName("scaleEqually"); + + ActionParam param = new ActionParam(); + param.setParamName("Service"); + param.setParamValue("UUID"); + actionCreate.getParams().add(param ); + + String responseAction = mvc + .perform(MockMvcRequestBuilders.post("/assuranceServicesManagement/v1/actionSpecification") + .with(SecurityMockMvcRequestPostProcessors.csrf()).contentType(MediaType.APPLICATION_JSON) + .content(toJson(actionCreate))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("name", is("scaleEqually"))).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString(); + + assertThat(actionSpecificationRepoService.findAll().size()).isEqualTo(1); + + ActionSpecification anActionSpecification = toJsonObj(responseAction, ActionSpecification.class); + assertThat(anActionSpecification.getParams().size()).isEqualTo(1); + + ActionSpecificationRef aref = new ActionSpecificationRef(); + aref.setActionId(anActionSpecification.getUuid()); + + + + Action action = new Action(); + action.setName( anActionSpecification.getName() ); + action.setActionSpecificationRef(aref); + ActionCharacteristic characteristic = new ActionCharacteristic(); + characteristic.setName("ServiceID"); + characteristic.setValue("AUUID"); + action.getActionCharacteristics().add( characteristic ); + + RuleSpecificationCreate rule = new RuleSpecificationCreate(); + rule.setName("aRule"); + rule.getActions().add( action ); + rule.setDescription("Descr"); + rule.setOpensliceEventType("AlarmCreateEvent"); + Scope scope = new Scope(); + scope.setEntityUUID("UUIDREFTEST"); + rule.setScope(scope); + + Condition c1 = new Condition(); + c1.setBooleanOperator("AND"); + c1.setOpensliceEventAttributeName("probableCause"); + c1.setOperator("EQUALS"); + c1.setEventAttributeValue("thresholdCrossed"); + + Condition c2 = new Condition(); + c2.setBooleanOperator("AND"); + c2.setOpensliceEventAttributeName("severity"); + c2.setOperator("EQUALS"); + c2.setEventAttributeValue("critical"); + + rule.getCondition().add(c1); + rule.getCondition().add(c2); + + String responseRule = mvc + .perform(MockMvcRequestBuilders.post("/assuranceServicesManagement/v1/ruleSpecification") + .with(SecurityMockMvcRequestPostProcessors.csrf()).contentType(MediaType.APPLICATION_JSON) + .content(toJson(rule))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("name", is("aRule"))).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString(); + + assertThat(ruleSpecificationRepoService.findAll().size()).isEqualTo(1); + + RuleSpecification ruleSpec = toJsonObj(responseRule, RuleSpecification.class); + + assertThat(ruleSpec.getName()).isEqualTo("aRule"); + assertThat(ruleSpec.getOpensliceEventType()).isEqualTo("AlarmCreateEvent"); + assertThat(ruleSpec.getActions().stream().findFirst().get().getActionSpecificationRef().getActionId() ).isEqualTo(anActionSpecification.getUuid()); + assertThat(ruleSpec.getScope().getEntityUUID()).isEqualTo("UUIDREFTEST"); + assertThat(ruleSpec.getCondition().size()).isEqualTo(2); + + RuleSpecificationUpdate ruleUpd = new RuleSpecificationUpdate(); + ruleUpd.setName("aRule2"); + ruleUpd.getActions().add(action); + ruleUpd.setDescription("Descr2"); + ruleUpd.setOpensliceEventType("AlarmCreateEvent2"); + Scope scope2 = new Scope(); + scope2.setEntityUUID("UUIDREFTEST2"); + ruleUpd.setScope(scope2); + + responseRule = mvc + .perform(MockMvcRequestBuilders + .patch("/assuranceServicesManagement/v1/ruleSpecification/" + ruleSpec.getUuid()) + .with(SecurityMockMvcRequestPostProcessors.csrf()).contentType(MediaType.APPLICATION_JSON) + .content(toJson(ruleUpd))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("name", is("aRule2"))).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString(); + + assertThat(ruleSpecificationRepoService.findAll().size()).isEqualTo(1); + + RuleSpecification ruleSpec2 = toJsonObj(responseRule, RuleSpecification.class); + + assertThat(ruleSpec2.getName()).isEqualTo("aRule2"); + assertThat(ruleSpec2.getOpensliceEventType()).isEqualTo("AlarmCreateEvent2"); + assertThat(ruleSpec2.getActions().stream().findFirst().get().getActionSpecificationRef().getActionId()).isEqualTo(anActionSpecification.getUuid()); + assertThat(ruleSpec2.getScope().getEntityUUID()).isEqualTo("UUIDREFTEST2"); + assertThat(ruleSpec2.getCondition().size()).isEqualTo(2); + + assertThat(ruleSpecificationRepoService.findByScopeUuid("UUIDREFTEST2").size()).isEqualTo(1); + + scope.setEntityUUID("UUIDREFTEST2"); + rule.setScope(scope); + mvc + .perform(MockMvcRequestBuilders.post("/assuranceServicesManagement/v1/ruleSpecification") + .with(SecurityMockMvcRequestPostProcessors.csrf()).contentType(MediaType.APPLICATION_JSON) + .content(toJson(rule))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("name", is("aRule"))).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString(); + + assertThat(ruleSpecificationRepoService.findByScopeUuid("UUIDREFTEST2").size()).isEqualTo(2); + + + + + } + + + @WithMockUser(username = "osadmin", roles = { "ADMIN", "USER" }) + @Test + public void testRuleCreateFromFiles() throws Exception { + + File faction = new File( "src/test/resources/testAction.json" ); + InputStream in = new FileInputStream( faction ); + String resvxf = IOUtils.toString(in, "UTF-8"); + + ActionSpecificationCreate aspec = toJsonObj( resvxf, ActionSpecificationCreate.class); + + String responseAction = mvc + .perform(MockMvcRequestBuilders.post("/assuranceServicesManagement/v1/actionSpecification") + .with(SecurityMockMvcRequestPostProcessors.csrf()).contentType(MediaType.APPLICATION_JSON) + .content(toJson( aspec ))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("name", is("scaleServiceEqually"))).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString(); + + assertThat(actionSpecificationRepoService.findAll().size()).isEqualTo(1); + + ActionSpecification anActionSpecification = toJsonObj(responseAction, ActionSpecification.class); + + File scatalog = new File( "src/test/resources/testRule.json" ); + in = new FileInputStream( scatalog ); + resvxf = IOUtils.toString(in, "UTF-8"); + + RuleSpecificationCreate scc = toJsonObj( resvxf, RuleSpecificationCreate.class); + + scc.getActions().get(0).getActionSpecificationRef().setActionId(anActionSpecification.getUuid() ); + + String responseRule = mvc + .perform(MockMvcRequestBuilders.post("/assuranceServicesManagement/v1/ruleSpecification") + .with(SecurityMockMvcRequestPostProcessors.csrf()).contentType(MediaType.APPLICATION_JSON) + .content(toJson(scc))) + .andExpect(status().isOk()).andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("name", is("Threshold Alarm on frontend"))).andExpect(status().isOk()).andReturn().getResponse() + .getContentAsString(); + + assertThat(ruleSpecificationRepoService.findAll().size()).isEqualTo(1); + + RuleSpecification ruleSpec3 = toJsonObj(responseRule, RuleSpecification.class); + + assertThat(ruleSpec3.getName()).isEqualTo("Threshold Alarm on frontend"); + assertThat(ruleSpec3.getOpensliceEventType()).isEqualTo("AlarmCreateEvent"); + assertThat(ruleSpec3.getActions().stream().findFirst().get().getActionSpecificationRef().getActionId()).isEqualTo(anActionSpecification.getUuid()); + assertThat(ruleSpec3.getScope().getEntityUUID()).isEqualTo("eb2bd384-3ed1-4605-a69c-bd5b887f396c"); + assertThat(ruleSpec3.getCondition().size()).isEqualTo(4); + + } + + @Test + public void testAlarmHandling() { + // create some Actions Specs + ActionSpecificationCreate actionSpecCreate = new ActionSpecificationCreate(); + actionSpecCreate.setName("sendEmail"); + var act1 = actionSpecificationRepoService.addActionSpecification(actionSpecCreate); + actionSpecCreate = new ActionSpecificationCreate(); + actionSpecCreate.setName("scaleEqualy"); + var act2 = actionSpecificationRepoService.addActionSpecification(actionSpecCreate); + actionSpecCreate = new ActionSpecificationCreate(); + actionSpecCreate.setName("callHuman"); + var act3 = actionSpecificationRepoService.addActionSpecification(actionSpecCreate); + assertThat(actionSpecificationRepoService.findAll().size()).isEqualTo(3); + // create some RuleSpecs and add to repo + + RuleSpecificationCreate rule01wConditions = new RuleSpecificationCreate(); + rule01wConditions.setName("aRule01"); + var aref = new ActionSpecificationRef(); + aref.setActionId(act1.getUuid()); + Action action = new Action(); + action.setName( act1.getName() ); + action.setActionSpecificationRef(aref); + ActionCharacteristic characteristic = new ActionCharacteristic(); + characteristic.setName("ServiceID"); + characteristic.setValue("AUUID"); + action.getActionCharacteristics().add( characteristic ); + rule01wConditions.getActions().add(action); + + + var aref2 = new ActionSpecificationRef(); + aref2.setActionId(act2.getUuid()); + action = new Action(); + action.setName( act2.getName() ); + action.setActionSpecificationRef(aref2); + characteristic = new ActionCharacteristic(); + characteristic.setName("ServiceID"); + characteristic.setValue("AUUID"); + action.getActionCharacteristics().add( characteristic ); + rule01wConditions.getActions().add(action); + rule01wConditions.setDescription("Descr"); + rule01wConditions.setOpensliceEventType("AlarmCreateEvent"); + Scope scope = new Scope(); + scope.setEntityUUID("service-uuid"); + rule01wConditions.setScope(scope); + + Condition c1 = new Condition(); + c1.setBooleanOperator("AND"); + c1.setOpensliceEventAttributeName("probableCause"); + c1.setOperator("EQUALS"); + c1.setEventAttributeValue("thresholdCrossed"); + Condition c2 = new Condition(); + c2.setBooleanOperator("AND"); + c2.setOpensliceEventAttributeName("perceivedSeverity"); + c2.setOperator("EQUALS"); + c2.setEventAttributeValue("critical"); + + rule01wConditions.getCondition().add(c1); + rule01wConditions.getCondition().add(c2); + + ruleSpecificationRepoService.addRuleSpecification(rule01wConditions); + + // create an Alarm and check the related actions are two + var alarm = new Alarm(); + alarm.setProbableCause("thresholdCrossed"); + alarm.setPerceivedSeverity("critical"); + + AffectedService affectedService = new AffectedService(); + affectedService.setUuid("service-uuid"); + affectedService.setId("service-uuid"); + alarm.getAffectedService().add(affectedService); + var actions = alarmHandling.decideForExecutionAction(alarm); + assertThat(actions.size()).isEqualTo(2); + + // create an irrelevant Alarm and check the related actions are zero + alarm = new Alarm(); + alarm.setProbableCause("notknown"); + alarm.setPerceivedSeverity("critical"); + alarm.getAffectedService().add(affectedService); + actions = alarmHandling.decideForExecutionAction(alarm); + assertThat(actions.size()).isEqualTo( 0 ); + + // create an irrelevant Alarm and check the related actions are zero + alarm = new Alarm(); + alarm.setProbableCause("thresholdCrossed"); + alarm.setPerceivedSeverity("warning"); + alarm.getAffectedService().add(affectedService); + actions = alarmHandling.decideForExecutionAction(alarm); + assertThat(actions.size()).isEqualTo( 0 ); + + + // create an irrelevant Alarm and check the related actions are zero + alarm = new Alarm(); + alarm.setProbableCause("thresholdCrossed"); + alarm.setPerceivedSeverity("warning"); + actions = alarmHandling.decideForExecutionAction(alarm); + assertThat(actions.size()).isEqualTo( 0 ); + + //create a rule that will be true with no conditions + RuleSpecificationCreate rule02NoConditions = new RuleSpecificationCreate(); + rule02NoConditions.setName("aRule02"); + var aref3 = new ActionSpecificationRef(); + aref3.setActionId(act3.getUuid()); + + action = new Action(); + action.setName( act3.getName() ); + action.setActionSpecificationRef(aref3); + characteristic = new ActionCharacteristic(); + characteristic.setName("ServiceID"); + characteristic.setValue("AUUID"); + action.getActionCharacteristics().add( characteristic ); + rule02NoConditions.getActions().add(action); + rule02NoConditions.setDescription("Descr"); + rule02NoConditions.setOpensliceEventType("AlarmCreateEvent"); + scope = new Scope(); + scope.setEntityUUID("service-uuid"); + rule02NoConditions.setScope(scope); + + ruleSpecificationRepoService.addRuleSpecification(rule02NoConditions); + + // create an Alarm and check the related actions are three + alarm = new Alarm(); + alarm.setProbableCause("thresholdCrossed"); + alarm.setPerceivedSeverity("critical"); + affectedService = new AffectedService(); + affectedService.setUuid("service-uuid"); + affectedService.setId("service-uuid"); + alarm.getAffectedService().add(affectedService); + actions = alarmHandling.decideForExecutionAction(alarm); + assertThat(actions.size()).isEqualTo(3); //all three actions! + + + Condition c3 = new Condition(); + c3.setBooleanOperator("AND"); + c3.setOpensliceEventAttributeName("threshold"); + c3.setOperator("GREATER_THAN"); + c3.setEventAttributeValue("50"); + + } + + static byte[] toJson(Object object) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsBytes(object); + } + + static T toJsonObj(String content, Class valueType) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.readValue(content, valueType); + } + +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000000000000000000000000000000000000..e535939c74ad282638c06453b9938ac004463b39 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,32 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/src/test/resources/testAction.json b/src/test/resources/testAction.json new file mode 100644 index 0000000000000000000000000000000000000000..d0be4c50b9a63d8a288046c10348b7b0e95596e1 --- /dev/null +++ b/src/test/resources/testAction.json @@ -0,0 +1,18 @@ +{ + "name": "scaleServiceEqually", + "description": null, + "params": [ + { + "paramName": "serviceID", + "paramValue": null + }, + { + "paramName": "Member_vnf_index", + "paramValue": null + }, + { + "paramName": "Scaling_group_descriptor", + "paramValue": null + } + ] +} \ No newline at end of file diff --git a/src/test/resources/testRule.json b/src/test/resources/testRule.json new file mode 100644 index 0000000000000000000000000000000000000000..a6a973f0447d31df5e8c03af83e4bb28d51e6322 --- /dev/null +++ b/src/test/resources/testRule.json @@ -0,0 +1,56 @@ +{ + "actions": [ + { + "name": "scaleServiceEqually", + "actionSpecificationRef": { + "actionId": "118a3d29-0067-4e69-92a2-95c9e3f69310" + }, + "actionCharacteristics": [ + { + "name": "serviceID", + "value": "111" + }, + { + "name": "Member_vnf_index", + "value": "1" + }, + { + "name": "Scaling_group_descriptor", + "value": "Wserver_autoscale" + } + ] + } + ], + "condition": [ + { + "booleanOperator": "AND", + "eventAttributeName": "sourceSystemId", + "eventAttributeValue": "mano-client-service", + "operator": "EQUALS" + }, + { + "booleanOperator": "AND", + "eventAttributeName": "perceivedSeverity", + "eventAttributeValue": "critical", + "operator": "EQUALS" + }, + { + "booleanOperator": "AND", + "eventAttributeName": "alarmType", + "eventAttributeValue": "qualityOfServiceAlarm", + "operator": "EQUALS" + }, + { + "booleanOperator": "AND", + "eventAttributeName": "probableCause", + "eventAttributeValue": "thresholdCrossed", + "operator": "EQUALS" + } + ], + "description": "", + "eventType": "AlarmCreateEvent", + "name": "Threshold Alarm on frontend", + "scope": { + "entityUUID": "eb2bd384-3ed1-4605-a69c-bd5b887f396c" + } +} \ No newline at end of file