diff --git a/pom.xml b/pom.xml
index 51dad97394ee08a2a949747e2339d997ee7eb08e..97de096f85c5bf498a9ca959d9972ee5c467c623 100644
--- a/pom.xml
+++ b/pom.xml
@@ -295,6 +295,12 @@
junit-platform-runner
test
+
+ org.mockito
+ mockito-inline
+ 4.0.0
+ test
+
com.h2database
h2
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/ApiException.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ApiException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4fa93b1639d29fec025d4940d5c45acf9a9fcb2
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ApiException.java
@@ -0,0 +1,29 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+public class ApiException extends Exception{
+ private int code;
+ public ApiException (int code, String msg) {
+ super(msg);
+ this.code = code;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/ApiOriginFilter.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ApiOriginFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1e9261fc3eb880715710693e8216f6cd261ede9
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ApiOriginFilter.java
@@ -0,0 +1,50 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+import java.io.IOException;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
+
+public class ApiOriginFilter implements jakarta.servlet.Filter {
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ HttpServletResponse res = (HttpServletResponse) response;
+ res.addHeader("Access-Control-Allow-Origin", "*");
+ res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
+ res.addHeader("Access-Control-Allow-Headers", "Content-Type");
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/ApiResponseMessage.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ApiResponseMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b8bfb850f1492cd8d6bfcb44971b219f60a925e
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ApiResponseMessage.java
@@ -0,0 +1,88 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+import jakarta.xml.bind.annotation.XmlTransient;
+
+@jakarta.xml.bind.annotation.XmlRootElement
+public class ApiResponseMessage {
+ public static final int ERROR = 1;
+ public static final int WARNING = 2;
+ public static final int INFO = 3;
+ public static final int OK = 4;
+ public static final int TOO_BUSY = 5;
+
+ int code;
+ String type;
+ String message;
+
+ public ApiResponseMessage(){}
+
+ public ApiResponseMessage(int code, String message){
+ this.code = code;
+ switch(code){
+ case ERROR:
+ setType("error");
+ break;
+ case WARNING:
+ setType("warning");
+ break;
+ case INFO:
+ setType("info");
+ break;
+ case OK:
+ setType("ok");
+ break;
+ case TOO_BUSY:
+ setType("too busy");
+ break;
+ default:
+ setType("unknown");
+ break;
+ }
+ this.message = message;
+ }
+
+ @XmlTransient
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/NotFoundException.java b/src/main/java/org/etsi/osl/tmf/ram702/api/NotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..92eaec8e60eced6e7f12519722005f4ec40b6896
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/NotFoundException.java
@@ -0,0 +1,29 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+public class NotFoundException extends ApiException {
+ private int code;
+ public NotFoundException (int code, String msg) {
+ super(code, msg);
+ this.code = code;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApi.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..da9b4e70d8f829fc0b599cc25c92913be02e014d
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApi.java
@@ -0,0 +1,317 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.etsi.osl.tmf.ri639.model.Resource;
+import org.etsi.osl.tmf.ri639.model.ResourceCreate;
+import org.etsi.osl.tmf.ri639.model.ResourceUpdate;
+
+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.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+
+import org.etsi.osl.tmf.ram702.utils.JSONResponseUtils;
+
+@Tag(name = "resource", description = "The resource Activation API")
+public interface ResourceActivationApi {
+
+ Logger log = LoggerFactory.getLogger(ResourceActivationApi.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 Resource",
+ operationId = "createResource",
+ description = "This operation creates a Resource entity.",
+ tags = {"resource"}
+ )
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "400", 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 = "/resource",
+ produces = {"application/json;charset=utf-8"},
+ consumes = {"application/json;charset=utf-8"},
+ method = RequestMethod.POST
+ )
+ default ResponseEntity createResource(
+ Principal principal,
+ @Parameter(description = "The Resource to be created", required = true)
+ @Valid @RequestBody ResourceCreate body
+ ) {
+ if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
+ if (getAcceptHeader().get().contains("application/json")) {
+ try {
+ return new ResponseEntity<>(
+ getObjectMapper().get().readValue(
+ JSONResponseUtils.RESOURCE_JSON_RESPONSE, Resource.class
+ ),
+ HttpStatus.NOT_IMPLEMENTED
+ );
+ } catch (IOException e) {
+ log.error(
+ "Couldn't serialize response for content type application/json", e
+ );
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+ } else {
+ log.warn(
+ "ObjectMapper or HttpServletRequest not configured in default ResourceActivationApi interface so no example is generated"
+ );
+ }
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Operation(
+ summary = "Deletes a Resource",
+ operationId = "deleteResource",
+ description = "This operation deletes a Resource entity.",
+ tags = {"resource"}
+ )
+ @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 = "/resource/{id}",
+ produces = {"application/json;charset=utf-8"},
+ method = RequestMethod.DELETE
+ )
+ default ResponseEntity deleteResource(
+ @Parameter(description = "Identifier of the Resource", required = true)
+ @PathVariable("id") String id
+ ) {
+ if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
+ // Implementation goes here
+ } else {
+ log.warn(
+ "ObjectMapper or HttpServletRequest not configured in default ResourceActivationApi interface so no example is generated"
+ );
+ }
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Operation(
+ summary = "List or find Resource objects",
+ operationId = "listResource",
+ description = "This operation lists or finds Resource entities.",
+ tags = {"resource"}
+ )
+ @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 = "/resource",
+ produces = {"application/json;charset=utf-8"},
+ method = RequestMethod.GET
+ )
+ default ResponseEntity> listResource(
+ Principal principal,
+ @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
+ ) {
+ if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
+ if (getAcceptHeader().get().contains("application/json")) {
+ try {
+ return new ResponseEntity<>(
+ getObjectMapper().get().readValue(
+ JSONResponseUtils.RESOURCE_LIST_JSON_RESPONSE,
+ new TypeReference>() {}
+ ),
+ HttpStatus.NOT_IMPLEMENTED
+ );
+ } catch (IOException e) {
+ log.error(
+ "Couldn't serialize response for content type application/json", e
+ );
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+ } else {
+ log.warn(
+ "ObjectMapper or HttpServletRequest not configured in default ResourceActivationApi interface so no example is generated"
+ );
+ }
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Operation(
+ summary = "Updates partially a Resource",
+ operationId = "patchResource",
+ description = "This operation updates partially a Resource entity.",
+ tags = {"resource"}
+ )
+ @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 = "/resource/{id}",
+ produces = {"application/json;charset=utf-8"},
+ consumes = {"application/json;charset=utf-8"},
+ method = RequestMethod.PATCH
+ )
+ default ResponseEntity patchResource(
+ Principal principal,
+ @Parameter(description = "The Resource to be updated", required = true)
+ @Valid @RequestBody ResourceUpdate body,
+ @Parameter(description = "Identifier of the Resource", required = true)
+ @PathVariable("id") String id
+ ) {
+ if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
+ if (getAcceptHeader().get().contains("application/json")) {
+ try {
+ return new ResponseEntity<>(
+ getObjectMapper().get().readValue(
+ JSONResponseUtils.RESOURCE_JSON_RESPONSE, Resource.class
+ ),
+ HttpStatus.NOT_IMPLEMENTED
+ );
+ } catch (IOException e) {
+ log.error(
+ "Couldn't serialize response for content type application/json", e
+ );
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+ } else {
+ log.warn(
+ "ObjectMapper or HttpServletRequest not configured in default ResourceActivationApi interface so no example is generated"
+ );
+ }
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Operation(
+ summary = "Retrieves a Resource by ID",
+ operationId = "retrieveResource",
+ description = "This operation retrieves a Resource entity. Attribute selection is enabled for all first-level attributes.",
+ tags = {"resource"}
+ )
+ @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 = "/resource/{id}",
+ produces = {"application/json;charset=utf-8"},
+ method = RequestMethod.GET
+ )
+ default ResponseEntity retrieveResource(
+ Principal principal,
+ @Parameter(description = "Identifier of the Resource", required = true)
+ @PathVariable("id") String id,
+ @Parameter(description = "Comma-separated properties to provide in response")
+ @Valid @RequestParam(value = "fields", required = false) String fields
+ ) {
+ if (getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
+ if (getAcceptHeader().get().contains("application/json")) {
+ try {
+ return new ResponseEntity<>(
+ getObjectMapper().get().readValue(
+ JSONResponseUtils.RESOURCE_JSON_RESPONSE, Resource.class
+ ),
+ HttpStatus.NOT_IMPLEMENTED
+ );
+ } catch (IOException e) {
+ log.error(
+ "Couldn't serialize response for content type application/json", e
+ );
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+ } else {
+ log.warn(
+ "ObjectMapper or HttpServletRequest not configured in default ResourceActivationApi interface so no example is generated"
+ );
+ }
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiController.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiController.java
new file mode 100644
index 0000000000000000000000000000000000000000..5421344741b3c03951a89905ebf849b95e144663
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiController.java
@@ -0,0 +1,241 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.etsi.osl.tmf.common.model.UserPartRoleType;
+import org.etsi.osl.tmf.ri639.model.Resource;
+import org.etsi.osl.tmf.ri639.model.ResourceCreate;
+import org.etsi.osl.tmf.ri639.model.ResourceUpdate;
+import org.etsi.osl.tmf.ram702.reposervices.ResourceActivationRepoService;
+
+import org.etsi.osl.tmf.util.AddUserAsOwnerToRelatedParties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.etsi.osl.model.nfv.UserRoleType;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+
+/**
+ * Controller class that implements the Resource Activation API.
+ * Handles HTTP requests for creating, retrieving, updating, and deleting resources.
+ */
+@Controller
+@RequestMapping("/resourceActivationManagement/v4/")
+public class ResourceActivationApiController implements ResourceActivationApi {
+
+ private final ObjectMapper objectMapper;
+ private final HttpServletRequest request;
+
+ @Autowired
+ private ResourceActivationRepoService resourceRepoService;
+
+
+ /**
+ * Constructs a new ResourceActivationApiController with the specified ObjectMapper and HttpServletRequest.
+ *
+ * @param objectMapper the ObjectMapper for JSON processing
+ * @param request the HttpServletRequest for accessing request data
+ */
+ @Autowired
+ public ResourceActivationApiController(
+ ObjectMapper objectMapper,
+ HttpServletRequest request
+ ) {
+ this.objectMapper = objectMapper;
+ this.request = request;
+ }
+
+
+ /**
+ * Creates a new resource.
+ *
+ * @param principal the security principal of the user making the request
+ * @param resource the resource to create
+ * @return a ResponseEntity containing the created resource and HTTP status
+ */
+ @PreAuthorize("hasAnyAuthority('ROLE_USER')")
+ @Override
+ public ResponseEntity createResource(
+ Principal principal,
+ @Valid ResourceCreate resource
+ ) {
+ try {
+ if (SecurityContextHolder.getContext().getAuthentication() != null) {
+ resource.setRelatedParty(
+ AddUserAsOwnerToRelatedParties.addUser(
+ principal.getName(),
+ principal.getName(),
+ UserPartRoleType.REQUESTER,
+ "",
+ resource.getRelatedParty()
+ )
+ );
+
+ Resource createdResource = resourceRepoService.addResource(resource);
+ return new ResponseEntity<>(createdResource, HttpStatus.CREATED);
+ } else {
+ return new ResponseEntity<>(HttpStatus.FORBIDDEN);
+ }
+ } catch (Exception e) {
+ log.error("Error creating resource", e);
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); // 500 Internal Server Error for other exceptions
+ }
+ }
+
+
+ /**
+ * Deletes a resource by its identifier.
+ *
+ * @param id the identifier of the resource to delete
+ * @return a ResponseEntity with the appropriate HTTP status
+ */
+ @PreAuthorize("hasAnyAuthority('ROLE_USER')")
+ @Override
+ public ResponseEntity deleteResource(String id) {
+ try {
+ resourceRepoService.deleteByUuid(id);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ } catch (ApiException e) {
+ log.error("Resource not found with id {}", id);
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND); // 404 if resource not found
+ } catch (Exception e) {
+ log.error("Error deleting resource with id {}", id, e);
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); // 500 for any other errors
+ }
+ }
+
+
+ /**
+ * Lists resources based on the provided parameters.
+ *
+ * @param principal the security principal of the user making the request
+ * @param fields the fields to include in the response
+ * @param offset the offset for pagination
+ * @param limit the maximum number of resources to return
+ * @param allParams additional parameters for filtering
+ * @return a ResponseEntity containing the list of resources and HTTP status
+ */
+ @SuppressWarnings("unchecked")
+ @PreAuthorize("hasAnyAuthority('ROLE_USER')")
+ @Override
+ public ResponseEntity> listResource(
+ Principal principal,
+ @Valid String fields,
+ @Valid Integer offset,
+ @Valid Integer limit,
+ Map allParams
+ ) {
+ try {
+ List resources = resourceRepoService.findAll(
+ principal.getName(), UserPartRoleType.REQUESTER
+ );
+ return new ResponseEntity<>(resources, HttpStatus.OK);
+
+ } catch (Exception e) {
+ log.error("Error listing resources", e);
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ /**
+ * Updates an existing resource by partially updating its fields.
+ *
+ * @param principal the security principal of the user making the request
+ * @param resource the resource data to update
+ * @param id the identifier of the resource to update
+ * @return a ResponseEntity containing the updated resource and HTTP status
+ */
+ @PreAuthorize("hasAnyAuthority('ROLE_USER')")
+ @Override
+ public ResponseEntity patchResource(
+ Principal principal,
+ @Valid ResourceUpdate resource,
+ String id
+ ) {
+ try {
+ // Call the updateResource method from the service class to update the resource
+ Resource updatedResource = resourceRepoService.updateResource(id, resource, true);
+
+ // Return the updated resource with 200 OK status
+ return new ResponseEntity<>(updatedResource, HttpStatus.OK);
+
+ } catch (ResourceNotFoundException e) {
+ log.error("Resource not found with id {}", id, e);
+ // Return 404 Not Found if the resource is not found
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+ } catch (Exception e) {
+ log.error("Error updating resource with id {}", id, e);
+ // Return 500 Internal Server Error for any other unexpected exceptions
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+
+ /**
+ * Retrieves a resource by its identifier.
+ *
+ * @param principal the security principal of the user making the request
+ * @param id the identifier of the resource to retrieve
+ * @param fields the fields to include in the response
+ * @return a ResponseEntity containing the resource and HTTP status
+ */
+ @PreAuthorize("hasAnyAuthority('ROLE_USER')")
+ @Override
+ public ResponseEntity retrieveResource(
+ Principal principal,
+ String id,
+ @Valid String fields
+ ) {
+ try {
+ // Call the service method to retrieve the resource
+ Resource resource = resourceRepoService.findByUuid(id);
+
+ // Return the resource with 200 OK status
+ return new ResponseEntity<>(resource, HttpStatus.OK);
+
+ } catch (ResourceNotFoundException e) {
+ log.error("Resource not found with id {}", id, e);
+ // Return 404 Not Found if the resource is not found
+ return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+
+ } catch (Exception e) {
+ log.error("Error retrieving resource with id {}", id, e);
+ // Return 500 Internal Server Error for any unexpected exceptions
+ return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+ }
+}
+
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiRouteBuilder.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiRouteBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..c556c8f7686ca938cd88aa8f203a5276666b7c3f
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiRouteBuilder.java
@@ -0,0 +1,102 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.camel.LoggingLevel;
+import org.apache.camel.ProducerTemplate;
+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.etsi.osl.tmf.ri639.model.ResourceCreate;
+import org.etsi.osl.tmf.ri639.model.ResourceUpdate;
+import org.etsi.osl.tmf.ram702.reposervices.ResourceActivationRepoService;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+@Configuration
+@Component
+public class ResourceActivationApiRouteBuilder extends RouteBuilder {
+
+ private static final transient Log logger = LogFactory.getLog(ResourceActivationApiRouteBuilder.class.getName());
+
+ @Value("${CATALOG_ADD_RESOURCE}")
+ private String CATALOG_ADD_RESOURCE = "";
+
+ @Value("${CATALOG_UPD_RESOURCE}")
+ private String CATALOG_UPD_RESOURCE = "";
+
+ @Value("${CATALOG_UPDADD_RESOURCE}")
+ private String CATALOG_UPDADD_RESOURCE = "";
+
+ @Value("${CATALOG_GET_RESOURCE_BY_ID}")
+ private String CATALOG_GET_RESOURCE_BY_ID = "";
+
+ @Autowired
+ private ProducerTemplate template;
+
+ @Autowired
+ ResourceActivationRepoService resourceRepoService;
+
+ @Override
+ public void configure() throws Exception {
+
+ from( CATALOG_ADD_RESOURCE )
+ .log(LoggingLevel.INFO, log, CATALOG_ADD_RESOURCE + " message received!")
+ .to("log:DEBUG?showBody=true&showHeaders=true")
+ .unmarshal().json( JsonLibrary.Jackson, ResourceCreate.class, true)
+ .bean( resourceRepoService, "addResource(${body})")
+ .marshal()
+ .json( JsonLibrary.Jackson)
+ .convertBodyTo( String.class );
+
+ from( CATALOG_UPD_RESOURCE )
+ .log(LoggingLevel.INFO, log, CATALOG_UPD_RESOURCE + " message received!")
+ .to("log:DEBUG?showBody=true&showHeaders=true")
+ .unmarshal().json( JsonLibrary.Jackson, ResourceUpdate.class, true)
+ .bean( resourceRepoService, "updateResource(${header.resourceId}, ${body}, ${header.triggerServiceActionQueue} )")
+ .marshal().json( JsonLibrary.Jackson)
+ .convertBodyTo( String.class );
+
+ from( CATALOG_UPDADD_RESOURCE )
+ .log(LoggingLevel.INFO, log, CATALOG_UPDADD_RESOURCE + " message received!")
+ .to("log:DEBUG?showBody=true&showHeaders=true")
+ .unmarshal().json( JsonLibrary.Jackson, ResourceCreate.class, true)
+ .bean( resourceRepoService, "addOrUpdateResourceByNameCategoryVersion(${header.aname},${header.acategory}, ${header.aversion}, ${body})")
+ .marshal().json( JsonLibrary.Jackson)
+ .convertBodyTo( String.class );
+ }
+
+ 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/tmf/ram702/api/ResourceActivationApiRouteBuilderEvents.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiRouteBuilderEvents.java
new file mode 100644
index 0000000000000000000000000000000000000000..c30136f29940635b1881df2e26e09b3df9403032
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceActivationApiRouteBuilderEvents.java
@@ -0,0 +1,112 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.api;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.etsi.osl.tmf.common.model.Notification;
+import org.etsi.osl.tmf.ri639.model.ResourceAttributeValueChangeNotification;
+import org.etsi.osl.tmf.ri639.model.ResourceCreateNotification;
+import org.etsi.osl.tmf.ri639.model.ResourceStateChangeNotification;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+@Configuration
+// @RefreshScope
+@Component
+public class ResourceActivationApiRouteBuilderEvents extends RouteBuilder {
+
+ private static final transient Log logger =
+ LogFactory.getLog(ResourceActivationApiRouteBuilderEvents.class.getName());
+
+
+
+ @Value("${EVENT_RESOURCE_CREATE}")
+ private String EVENT_RESOURCE_CREATE = "";
+
+ @Value("${EVENT_RESOURCE_STATE_CHANGED}")
+ private String EVENT_RESOURCE_STATE_CHANGED = "";
+
+ @Value("${EVENT_RESOURCE_DELETE}")
+ private String EVENT_RESOURCE_DELETE = "";
+
+ @Value("${EVENT_RESOURCE_ATTRIBUTE_VALUE_CHANGED}")
+ private String EVENT_RESOURCE_ATTRIBUTE_VALUE_CHANGED = "";
+
+
+ @Autowired
+ private ProducerTemplate template;
+
+
+ @Override
+ public void configure() throws Exception {
+
+ }
+
+
+ /**
+ * @param n
+ */
+ public void publishEvent(final Notification n, final String objId) {
+ n.setEventType(n.getClass().getName());
+ logger.info("will send Event topic for type " + n.getEventType());
+ try {
+ String msgtopic = "";
+
+ if (n instanceof ResourceCreateNotification) {
+ msgtopic = EVENT_RESOURCE_CREATE;
+ } else if (n instanceof ResourceAttributeValueChangeNotification) {
+ msgtopic = EVENT_RESOURCE_ATTRIBUTE_VALUE_CHANGED;
+ }else if (n instanceof ResourceStateChangeNotification ) {
+ msgtopic = EVENT_RESOURCE_STATE_CHANGED;
+ }
+
+ Map map = new HashMap<>();
+ map.put("eventid", n.getEventId());
+ map.put("objId", objId);
+
+ template.sendBodyAndHeaders(msgtopic, toJsonString(n), map);
+
+ } catch (Exception e) {
+ logger.error("Cannot send Event . " + e.getStackTrace());
+ }
+ }
+
+ 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/tmf/ram702/api/ResourceNotFoundException.java b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f08d3e6a5df8724d35f495f625d7605e61f1623
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/api/ResourceNotFoundException.java
@@ -0,0 +1,8 @@
+package org.etsi.osl.tmf.ram702.api;
+
+public class ResourceNotFoundException extends ApiException {
+
+ public ResourceNotFoundException(String message) {
+ super(404, message); // Setting the HTTP status code to 404 for "Not Found"
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/repo/ResourceActivationRepository.java b/src/main/java/org/etsi/osl/tmf/ram702/repo/ResourceActivationRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..1cc064d403f9f8646b9d477fe8580030ec04fed6
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/repo/ResourceActivationRepository.java
@@ -0,0 +1,57 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.repo;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.etsi.osl.tmf.ri639.model.Resource;
+
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.stereotype.Repository;
+
+
+
+@Repository("resourceActivationRepository")
+public interface ResourceActivationRepository extends CrudRepository, PagingAndSortingRepository {
+
+ Optional findByUuid(String id);
+
+ @Query(
+ "SELECT srv FROM RIResource srv " +
+ "JOIN FETCH srv.relatedParty rp " +
+ "WHERE rp.name = ?1"
+ )
+ Iterable findByRolename(String name);
+
+ @Query(
+ "SELECT srv FROM RIResource srv " +
+ "WHERE srv.resourceStatus = org.etsi.osl.tmf.ri639.model.ResourceStatusType.AVAILABLE " +
+ "AND srv.endOperatingDate < CURRENT_TIMESTAMP"
+ )
+ List findActiveToTerminate();
+
+ List findByNameAndResourceVersion(String aname, String aversion);
+
+ List findByNameAndCategoryAndResourceVersion(String aname, String acategory, String aversion);
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/repo/ResourceRepository.java b/src/main/java/org/etsi/osl/tmf/ram702/repo/ResourceRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..9faab4df85dded4c532ef039d8e2ca3ca33d1a96
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/repo/ResourceRepository.java
@@ -0,0 +1,63 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.repo;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.etsi.osl.tmf.ri639.model.Resource;
+
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.stereotype.Repository;
+
+
+
+@Repository("resourceRepository702")
+public interface ResourceRepository extends CrudRepository, PagingAndSortingRepository {
+
+
+ Optional findByUuid(String id);
+
+ @Query("SELECT srv FROM RIResource srv JOIN FETCH srv.relatedParty rp WHERE rp.name = ?1")
+ Iterable findByRolename(String name);
+
+
+ @Query("SELECT srv FROM RIResource srv WHERE srv.resourceStatus = org.etsi.osl.tmf.ri639.model.ResourceStatusType.AVAILABLE AND "
+ + "srv.endOperatingDate < CURRENT_TIMESTAMP")
+ List findActiveToTerminate();
+
+ @Query("SELECT srv FROM RIResource srv "
+ + "JOIN FETCH srv.resourceCharacteristic char "
+ + "JOIN FETCH char.value val "
+ + "WHERE (srv.resourceStatus = org.etsi.osl.tmf.ri639.model.ResourceStatusType.AVAILABLE OR "
+ + " srv.resourceStatus = org.etsi.osl.tmf.ri639.model.ResourceStatusType.RESERVED OR "
+ + " srv.resourceStatus = org.etsi.osl.tmf.ri639.model.ResourceStatusType.STANDBY) AND "
+ + "char.name = 'externalPartnerServiceId'"
+ )
+
+ List findActiveAndReservedResourcesOfPartners();
+
+
+ List findByNameAndResourceVersion(String aname, String aversion);
+ List findByNameAndCategoryAndResourceVersion(String aname, String acategory, String aversion);
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/reposervices/ResourceActivationRepoService.java b/src/main/java/org/etsi/osl/tmf/ram702/reposervices/ResourceActivationRepoService.java
new file mode 100644
index 0000000000000000000000000000000000000000..88d04f9f062a121530f9f1794ba6f78ee3fc9811
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/reposervices/ResourceActivationRepoService.java
@@ -0,0 +1,564 @@
+/*-
+ * ========================LICENSE_START=================================
+ * org.etsi.osl.tmf.api
+ * %%
+ * Copyright (C) 2024 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.tmf.ram702.reposervices;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.hibernate5.jakarta.Hibernate5JakartaModule;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.etsi.osl.tmf.ram702.api.ApiException;
+import org.etsi.osl.tmf.common.model.Any;
+import org.etsi.osl.tmf.common.model.UserPartRoleType;
+import org.etsi.osl.tmf.common.model.service.Note;
+import org.etsi.osl.tmf.prm669.model.RelatedParty;
+import org.etsi.osl.tmf.rcm634.model.ResourceSpecificationRef;
+import org.etsi.osl.tmf.rcm634.reposervices.ResourceSpecificationRepoService;
+import org.etsi.osl.tmf.ram702.api.ResourceActivationApiRouteBuilderEvents;
+import org.etsi.osl.tmf.ram702.api.ResourceNotFoundException;
+import org.etsi.osl.tmf.ri639.model.Characteristic;
+import org.etsi.osl.tmf.ri639.model.Feature;
+import org.etsi.osl.tmf.ri639.model.LogicalResource;
+import org.etsi.osl.tmf.ri639.model.PhysicalResource;
+import org.etsi.osl.tmf.ri639.model.Resource;
+import org.etsi.osl.tmf.ri639.model.ResourceAttributeValueChangeEvent;
+import org.etsi.osl.tmf.ri639.model.ResourceAttributeValueChangeNotification;
+import org.etsi.osl.tmf.ri639.model.ResourceCreate;
+import org.etsi.osl.tmf.ri639.model.ResourceCreateEvent;
+import org.etsi.osl.tmf.ri639.model.ResourceCreateNotification;
+import org.etsi.osl.tmf.ri639.model.ResourceRelationship;
+import org.etsi.osl.tmf.ri639.model.ResourceStateChangeEvent;
+import org.etsi.osl.tmf.ri639.model.ResourceStateChangeNotification;
+import org.etsi.osl.tmf.ri639.model.ResourceUpdate;
+import org.etsi.osl.tmf.ram702.repo.ResourceActivationRepository;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.transform.ResultTransformer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.validation.Valid;
+
+/**
+ * Service class for managing Resource Activation operations.
+ * Provides methods to add, update, delete, and retrieve resources.
+ */
+@org.springframework.stereotype.Service
+public class ResourceActivationRepoService {
+
+ private static final transient Log logger =
+ LogFactory.getLog(ResourceActivationRepoService.class.getName());
+
+ @Autowired
+ ObjectMapper objectMapper;
+
+ @Autowired
+ ResourceActivationRepository resourceRepo;
+
+ @Autowired
+ ResourceSpecificationRepoService resourceSpecRepoService;
+
+ private SessionFactory sessionFactory;
+
+ @Autowired
+ ResourceActivationApiRouteBuilderEvents resourceApiRouteBuilder;
+
+
+ /**
+ * Constructs the ResourceActivationRepoService with the given EntityManagerFactory.
+ *
+ * @param factory the EntityManagerFactory to use
+ */
+ @Autowired
+ public ResourceActivationRepoService(EntityManagerFactory factory) {
+ if (factory.unwrap(SessionFactory.class) == null) {
+ throw new NullPointerException("factory is not a hibernate factory");
+ }
+ this.sessionFactory = factory.unwrap(SessionFactory.class);
+ }
+
+
+ /**
+ * Retrieves all resources from the repository.
+ *
+ * @return a list of all resources
+ */
+ public List findAll() {
+
+ return (List) this.resourceRepo.findAll();
+ }
+
+ /**
+ * Finds resources by name and requester role.
+ *
+ * @param name the name of the resource
+ * @param requester the requester role
+ * @return a list of resources matching the name and requester role
+ */
+ public List findAll(String name, UserPartRoleType requester) {
+
+ return (List) this.resourceRepo.findByRolename(name);
+ }
+
+
+ /**
+ * Adds a new resource to the repository.
+ *
+ * @param resource the resource to add
+ * @return the added resource
+ */
+ public Resource addResource(@Valid ResourceCreate resource){
+ logger.info("Will add Resource: " + resource.getName());
+
+ Resource s;
+
+ if (resource.getAtType() != null
+ && resource.getAtType().toLowerCase().contains("physicalresource")) {
+ s = new PhysicalResource();
+ } else {
+ s = new LogicalResource();
+ }
+
+ if (resource.getAtType() != null) {
+ s.setType(resource.getAtType());
+ }
+ s.setName(resource.getName());
+ s.setCategory(resource.getCategory());
+ s.setDescription(resource.getDescription());
+
+ if ( resource.getStartOperatingDate() == null ) {
+ s.setStartOperatingDate(OffsetDateTime.now(ZoneOffset.UTC));
+ } else {
+ s.setStartOperatingDate(resource.getStartOperatingDate());
+ }
+ s.setEndOperatingDate(resource.getEndOperatingDate());
+ s.setUsageState(resource.getUsageState());
+ s.setResourceStatus(resource.getResourceStatus());
+ s.setResourceVersion(resource.getResourceVersion());
+ s.setOperationalState(resource.getOperationalState());
+ s.setAdministrativeState(resource.getAdministrativeState());
+
+ ResourceSpecificationRef thespecRef = new ResourceSpecificationRef();
+ thespecRef.setId(resource.getResourceSpecification().getId());
+ thespecRef.setName(resource.getResourceSpecification().getName());
+
+ s.setResourceSpecification(thespecRef);
+
+ if (resource.getPlace() != null) {
+ s.setPlace(resource.getPlace());
+ }
+ if (resource.getNote() != null) {
+ s.getNote().addAll(resource.getNote());
+ }
+ if (resource.getRelatedParty() != null) {
+ s.getRelatedParty().addAll(resource.getRelatedParty());
+ }
+ if (resource.getResourceCharacteristic() != null) {
+ s.getResourceCharacteristic().addAll(resource.getResourceCharacteristic());
+ }
+ if (resource.getResourceRelationship() != null) {
+ s.getResourceRelationship().addAll(resource.getResourceRelationship());
+ }
+ if (resource.getAttachment() != null) {
+ s.getAttachment().addAll(resource.getAttachment());
+ }
+ if (resource.getActivationFeature() != null) {
+ s.getActivationFeature().addAll(resource.getActivationFeature());
+ }
+
+ Note noteItem = new Note();
+ noteItem.setText("Resource status: " + s.getResourceStatus());
+ noteItem.setAuthor("API");
+ noteItem.setDate(OffsetDateTime.now(ZoneOffset.UTC));
+ s.addNoteItem(noteItem);
+
+ s = this.resourceRepo.save(s);
+
+ raiseResourceCreateNotification(s);
+ return s;
+ }
+
+
+ /**
+ * Finds a resource by its UUID.
+ *
+ * @param id the UUID of the resource
+ * @return the resource if found, or null if not found
+ */
+ @Transactional
+ public Resource findByUuid(String id) throws ResourceNotFoundException {
+ Optional optionalCat = this.resourceRepo.findByUuid(id);
+
+ if (optionalCat.isEmpty()) {
+ throw new ResourceNotFoundException("Resource not found with UUID: " + id);
+ }
+
+ return optionalCat.get();
+ }
+
+
+ /**
+ * Updates an existing resource with the given updates.
+ *
+ * @param id the UUID of the resource to update
+ * @param resourceUpd the resource update data
+ * @param triggerServiceActionQueue whether to trigger service action queue
+ * @return the updated resource
+ */
+ @Transactional
+ public Resource updateResource(String id, @Valid ResourceUpdate resourceUpd,
+ boolean triggerServiceActionQueue) throws ResourceNotFoundException {
+ Resource resource = this.getResourceEager(id);
+
+ if (resource == null) {
+ throw new ResourceNotFoundException("Resource not found with UUID: " + id);
+ }
+
+ logger.info("Will update Resource: " + resource.getName());
+
+ if (resourceUpd.getResourceRelationship() != null) {
+ resource.setResourceRelationship(
+ new LinkedHashSet(resourceUpd.getResourceRelationship()
+ ));
+ }
+
+ if (resourceUpd.getAtType() != null) {
+ resource.setType(resourceUpd.getAtType());
+ }
+ if (resourceUpd.getName() != null) {
+ resource.setName(resourceUpd.getName());
+ }
+ if (resourceUpd.getCategory() != null) {
+ resource.setCategory(resourceUpd.getCategory());
+ }
+ if (resourceUpd.getDescription() != null) {
+ resource.setDescription(resourceUpd.getDescription());
+ }
+ if (resourceUpd.getStartOperatingDate() != null) {
+ resource.setStartOperatingDate(resourceUpd.getStartOperatingDate());
+ }
+ if (resourceUpd.getEndOperatingDate() != null) {
+ resource.setEndOperatingDate(resourceUpd.getEndOperatingDate());
+ }
+ if (resourceUpd.getUsageState() != null) {
+ resource.setUsageState(resourceUpd.getUsageState());
+ }
+
+ boolean resourceStateChanged = false;
+ if (resourceUpd.getResourceStatus() != null) {
+ if (!resourceUpd.getResourceStatus().equals(resource.getResourceStatus())) {
+ resourceStateChanged = true;
+ }
+
+ resource.setResourceStatus(resourceUpd.getResourceStatus());
+ }
+ if (resourceUpd.getResourceVersion() != null) {
+ resource.setResourceVersion(resourceUpd.getResourceVersion());
+ }
+ if (resourceUpd.getOperationalState() != null) {
+ resource.setOperationalState(resourceUpd.getOperationalState());
+ }
+ if (resourceUpd.getAdministrativeState() != null) {
+ resource.setAdministrativeState(resourceUpd.getAdministrativeState());
+ }
+ if (resourceUpd.getResourceSpecification() != null) {
+ resource.setResourceSpecification(resourceUpd.getResourceSpecification());
+ }
+
+ if (resourceUpd.getPlace() != null) {
+ resource.setPlace(resourceUpd.getPlace());
+ }
+
+ if (resourceUpd.getNote() != null) {
+ for (Note n : resourceUpd.getNote()) {
+ if (n.getUuid() == null) {
+ resource.addNoteItem(n);
+ }
+ }
+ }
+
+ if (resourceUpd.getRelatedParty() != null) {
+ for (RelatedParty n : resourceUpd.getRelatedParty()) {
+ if (n.getUuid() == null) {
+ resource.addRelatedPartyItem(n);
+ }
+ }
+ }
+
+ boolean resourceCharacteristicChanged = false;
+ String charsChanged="";
+ if (resourceUpd.getResourceCharacteristic() != null) {
+ for (Characteristic n : resourceUpd.getResourceCharacteristic()) {
+
+ if (resource.getResourceCharacteristicByName(n.getName()) != null) {
+
+ Characteristic origChar = resource.getResourceCharacteristicByName(n.getName());
+ if ((origChar != null) && (origChar.getValue() != null) && (origChar.getValue().getValue() != null)) {
+ if (!origChar.getValue().getValue().equals(n.getValue().getValue())) {
+ resourceCharacteristicChanged = true;
+ charsChanged = charsChanged + n.getName() + ",";
+ }
+ }
+
+ resource.getResourceCharacteristicByName(n.getName())
+ .setValue(new Any(n.getValue().getValue(), n.getValue().getAlias()));
+ } else {
+ resource.addResourceCharacteristicItem(n);
+ resourceCharacteristicChanged = true;
+ charsChanged = charsChanged + n.getName() + ",";
+ }
+ }
+ }
+
+ if (resourceCharacteristicChanged) {
+ Note n = new Note();
+ n.setText("Resource characteristics changed : " + charsChanged);
+ n.setAuthor( "RAM702-API" );
+ n.setDate( OffsetDateTime.now(ZoneOffset.UTC).toString() );
+ resource.addNoteItem( n );
+
+ }
+
+ if (resourceStateChanged) {
+ Note n = new Note();
+ n.setText("Resource resourceStateChanged changed to " + resource.getResourceStatus().toString() );
+ n.setAuthor( "RAM702-API" );
+ n.setDate( OffsetDateTime.now(ZoneOffset.UTC).toString() );
+ resource.addNoteItem( n );
+
+ }
+
+ if (resourceUpd.getActivationFeature() != null) {
+ for (Feature n : resourceUpd.getActivationFeature()) {
+ if (n.getId() != null) {
+ // we need to update this ?
+ } else {
+ resource.getActivationFeature().add(n);
+ }
+
+ }
+ }
+
+ resource = this.resourceRepo.save(resource);
+
+ if (resourceCharacteristicChanged) {
+ raiseResourceAttributeValueChangeEventNotification(resource);
+ } else if (resourceStateChanged) {
+ raiseResourceStateChangeEventNotification(resource);
+ }
+ return resource;
+ }
+
+ /**
+ * Retrieves a resource by UUID and returns it as a JSON string.
+ *
+ * @param id the UUID of the resource
+ * @return the resource as a JSON string
+ * @throws JsonProcessingException if JSON processing fails
+ * @throws ResourceNotFoundException if Resource not found
+ */
+ public String getResourceEagerAsString(String id) throws JsonProcessingException, ResourceNotFoundException {
+ Resource s = this.getResourceEager(id);
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.registerModule(new Hibernate5JakartaModule());
+ String res = mapper.writeValueAsString(s);
+
+ return res;
+ }
+
+
+ /**
+ * Retrieves a resource by UUID with all associations eagerly loaded.
+ *
+ * @param id the UUID of the resource
+ * @return the resource with all associations initialized
+ * @throws ResourceNotFoundException if Resource not found
+ */
+ public Resource getResourceEager(String id) throws ResourceNotFoundException {
+ Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+ Resource s = null;
+ try {
+ s = (Resource) session.get(Resource.class, id);
+ if (s == null) {
+ return this.findByUuid(id);
+ }
+
+ Hibernate.initialize(s.getRelatedParty());
+ Hibernate.initialize(s.getNote());
+ Hibernate.initialize(s.getResourceCharacteristic());
+ Hibernate.initialize(s.getResourceSpecification());
+ Hibernate.initialize(s.getResourceRelationship());
+ Hibernate.initialize(s.getAttachment());
+ Hibernate.initialize(s.getActivationFeature());
+
+ tx.commit();
+ } finally {
+ session.close();
+ }
+
+ return s;
+ }
+
+
+ /**
+ * Raises a resource create notification event.
+ *
+ * @param so the resource that was created
+ */
+ @Transactional
+ private void raiseResourceCreateNotification(Resource so) {
+ ResourceCreateNotification n = new ResourceCreateNotification();
+ ResourceCreateEvent event = new ResourceCreateEvent();
+ event.getEvent().setResource(so);
+ n.setEvent(event);
+ resourceApiRouteBuilder.publishEvent(n, so.getId());
+
+ }
+
+
+ /**
+ * Raises a resource attribute value change notification event.
+ *
+ * @param so the resource that has changed
+ */
+ @Transactional
+ private void raiseResourceAttributeValueChangeEventNotification(Resource so) {
+ ResourceAttributeValueChangeNotification n = new ResourceAttributeValueChangeNotification();
+ ResourceAttributeValueChangeEvent event = new ResourceAttributeValueChangeEvent();
+ event.getEvent().setResource(so);
+ n.setEvent(event);
+ resourceApiRouteBuilder.publishEvent(n, so.getId());
+
+ }
+
+
+ /**
+ * Raises a resource state change notification event.
+ *
+ * @param so the resource whose state has changed
+ */
+ @Transactional
+ private void raiseResourceStateChangeEventNotification(Resource so) {
+ ResourceStateChangeNotification n = new ResourceStateChangeNotification();
+ ResourceStateChangeEvent event = new ResourceStateChangeEvent();
+ event.getEvent().setResource(so);
+ n.setEvent(event);
+ resourceApiRouteBuilder.publishEvent(n, so.getId());
+
+ }
+
+
+ /**
+ * Finds all active resources that need to be terminated.
+ *
+ * @return a list of resource UUIDs to terminate
+ */
+ @Transactional
+ public List findAllActiveResourcesToTerminate() {
+
+ List result = new ArrayList<>();
+ List resourcs = this.resourceRepo.findActiveToTerminate();
+ for (Resource r : resourcs) {
+ result.add(r.getId());
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Deletes a resource by its UUID.
+ *
+ * @param id the UUID of the resource to delete
+ * @return null
+ * @throws ApiException
+ */
+ public Void deleteByUuid(String id) throws ApiException {
+ Optional optionalCat = this.resourceRepo.findByUuid(id);
+
+ if (optionalCat.isPresent()) {
+ Resource s = optionalCat.get();
+ this.resourceRepo.delete(s);
+
+ logger.info("Deleted resource with UUID = " + id);
+ } else {
+ throw new ApiException(404, "Resource not found with UUID: " + id);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Adds a new resource or updates an existing one based on name, category, and version.
+ *
+ * @param aName the name of the resource
+ * @param aCategory the category of the resource
+ * @param aVersion the version of the resource
+ * @param resourceCreate the resource data to create or update
+ * @return the added or updated resource
+ */
+ @Transactional
+ public Resource addOrUpdateResourceByNameCategoryVersion(String aName, String aCategory,
+ String aVersion, ResourceCreate aResourceCreate) throws ApiException {
+
+ List resources =
+ this.resourceRepo.findByNameAndCategoryAndResourceVersion(aName, aCategory, aVersion);
+ Resource result = null;
+
+ if (resources.size() > 0) {
+ // perform update to the first one
+ String resID = resources.get(0).getUuid();
+ result = this.updateResource(resID, aResourceCreate, false);
+
+ } else {
+ result = this.addResource(aResourceCreate);
+ }
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ String originaServiceAsJson = mapper.writeValueAsString(result);
+ logger.debug(originaServiceAsJson);
+ } catch (JsonProcessingException e) {
+ logger.error("cannot umarshall service: " + result.getName());
+ e.printStackTrace();
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/etsi/osl/tmf/ram702/utils/JSONResponseUtils.java b/src/main/java/org/etsi/osl/tmf/ram702/utils/JSONResponseUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..9573c27fb18c8993aa2ccce4bbb248f8fe9e438a
--- /dev/null
+++ b/src/main/java/org/etsi/osl/tmf/ram702/utils/JSONResponseUtils.java
@@ -0,0 +1,361 @@
+package org.etsi.osl.tmf.ram702.utils;
+
+public class JSONResponseUtils {
+ public static final String RESOURCE_JSON_RESPONSE = "{\\r\\n" + //
+ " \\\"note\\\" : [ {\\r\\n" + //
+ " \\\"date\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"author\\\" : \\\"author\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"text\\\" : \\\"text\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"date\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"author\\\" : \\\"author\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"text\\\" : \\\"text\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"endOperatingDate\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"resourceVersion\\\" : \\\"resourceVersion\\\",\\r\\n" + //
+ " \\\"activationFeature\\\" : [ {\\r\\n" + //
+ " \\\"isBundle\\\" : true,\\r\\n" + //
+ " \\\"isEnabled\\\" : true,\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"featureCharacteristic\\\" : [ {\\r\\n" + //
+ " \\\"characteristicRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"valueType\\\" : \\\"valueType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"value\\\" : { }\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"characteristicRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"valueType\\\" : \\\"valueType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"value\\\" : { }\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"constraint\\\" : [ {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"version\\\" : \\\"version\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"version\\\" : \\\"version\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"featureRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"validFor\\\" : {\\r\\n" + //
+ " \\\"startDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\",\\r\\n" + //
+ " \\\"endDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"validFor\\\" : {\\r\\n" + //
+ " \\\"startDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\",\\r\\n" + //
+ " \\\"endDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\"\\r\\n" + //
+ " } ]\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"isBundle\\\" : true,\\r\\n" + //
+ " \\\"isEnabled\\\" : true,\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"featureCharacteristic\\\" : [ {\\r\\n" + //
+ " \\\"characteristicRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"valueType\\\" : \\\"valueType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"value\\\" : { }\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"characteristicRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"valueType\\\" : \\\"valueType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"value\\\" : { }\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"constraint\\\" : [ {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"version\\\" : \\\"version\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"version\\\" : \\\"version\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"featureRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"validFor\\\" : {\\r\\n" + //
+ " \\\"startDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\",\\r\\n" + //
+ " \\\"endDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"relationshipType\\\",\\r\\n" + //
+ " \\\"validFor\\\" : {\\r\\n" + //
+ " \\\"startDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\",\\r\\n" + //
+ " \\\"endDateTime\\\" : \\\"1985-04-12T23:20:50.52Z\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\"\\r\\n" + //
+ " } ]\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"description\\\" : \\\"description\\\",\\r\\n" + //
+ " \\\"resourceCharacteristic\\\" : [ null, null ],\\r\\n" + //
+ " \\\"relatedParty\\\" : [ {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"role\\\" : \\\"role\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"role\\\" : \\\"role\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"attachment\\\" : [ {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"attachmentType\\\" : \\\"video\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"description\\\" : \\\"Photograph of the Product\\\",\\r\\n" + //
+ " \\\"mimeType\\\" : \\\"mimeType\\\",\\r\\n" + //
+ " \\\"content\\\" : \\\"content\\\",\\r\\n" + //
+ " \\\"url\\\" : \\\"http://host/Content/4aafacbd-11ff-4dc8-b445-305f2215715f\\\",\\r\\n" + //
+ " \\\"size\\\" : {\\r\\n" + //
+ " \\\"amount\\\" : 0.8008282,\\r\\n" + //
+ " \\\"units\\\" : \\\"units\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"4aafacbd-11ff-4dc8-b445-305f2215715f\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://host/Attachment/4aafacbd-11ff-4dc8-b445-305f2215715f\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"attachmentType\\\" : \\\"video\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"description\\\" : \\\"Photograph of the Product\\\",\\r\\n" + //
+ " \\\"mimeType\\\" : \\\"mimeType\\\",\\r\\n" + //
+ " \\\"content\\\" : \\\"content\\\",\\r\\n" + //
+ " \\\"url\\\" : \\\"http://host/Content/4aafacbd-11ff-4dc8-b445-305f2215715f\\\",\\r\\n" + //
+ " \\\"size\\\" : {\\r\\n" + //
+ " \\\"amount\\\" : 0.8008282,\\r\\n" + //
+ " \\\"units\\\" : \\\"units\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"4aafacbd-11ff-4dc8-b445-305f2215715f\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://host/Attachment/4aafacbd-11ff-4dc8-b445-305f2215715f\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"administrativeState\\\" : \\\"locked\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"resourceRelationship\\\" : [ {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"bundled\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"resource\\\" : {\\r\\n" + //
+ " \\\"note\\\" : [ null, null ],\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"endOperatingDate\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"resourceVersion\\\" : \\\"resourceVersion\\\",\\r\\n" + //
+ " \\\"activationFeature\\\" : [ null, null ],\\r\\n" + //
+ " \\\"resourceSpecification\\\" : {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"version\\\" : \\\"version\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"description\\\" : \\\"description\\\",\\r\\n" + //
+ " \\\"resourceCharacteristic\\\" : [ null, null ],\\r\\n" + //
+ " \\\"relatedParty\\\" : [ null, null ],\\r\\n" + //
+ " \\\"resourceStatus\\\" : \\\"standby\\\",\\r\\n" + //
+ " \\\"usageState\\\" : \\\"idle\\\",\\r\\n" + //
+ " \\\"attachment\\\" : [ null, null ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"resourceRelationship\\\" : [ null, null ],\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"href\\\",\\r\\n" + //
+ " \\\"startOperatingDate\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"category\\\" : \\\"category\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " }, {\\r\\n" + //
+ " \\\"relationshipType\\\" : \\\"bundled\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"resource\\\" : {\\r\\n" + //
+ " \\\"note\\\" : [ null, null ],\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"endOperatingDate\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"resourceVersion\\\" : \\\"resourceVersion\\\",\\r\\n" + //
+ " \\\"activationFeature\\\" : [ null, null ],\\r\\n" + //
+ " \\\"resourceSpecification\\\" : {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"version\\\" : \\\"version\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"description\\\" : \\\"description\\\",\\r\\n" + //
+ " \\\"resourceCharacteristic\\\" : [ null, null ],\\r\\n" + //
+ " \\\"relatedParty\\\" : [ null, null ],\\r\\n" + //
+ " \\\"resourceStatus\\\" : \\\"standby\\\",\\r\\n" + //
+ " \\\"usageState\\\" : \\\"idle\\\",\\r\\n" + //
+ " \\\"attachment\\\" : [ null, null ],\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"resourceRelationship\\\" : [ null, null ],\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"href\\\",\\r\\n" + //
+ " \\\"startOperatingDate\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"category\\\" : \\\"category\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"http://example.com/aeiou\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " } ],\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"href\\\",\\r\\n" + //
+ " \\\"startOperatingDate\\\" : \\\"2000-01-23T04:56:07.000+00:00\\\",\\r\\n" + //
+ " \\\"operationalState\\\" : \\\"enable\\\",\\r\\n" + //
+ " \\\"place\\\" : {\\r\\n" + //
+ " \\\"@referredType\\\" : \\\"@referredType\\\",\\r\\n" + //
+ " \\\"role\\\" : \\\"role\\\",\\r\\n" + //
+ " \\\"@baseType\\\" : \\\"@baseType\\\",\\r\\n" + //
+ " \\\"@type\\\" : \\\"@type\\\",\\r\\n" + //
+ " \\\"name\\\" : \\\"name\\\",\\r\\n" + //
+ " \\\"id\\\" : \\\"id\\\",\\r\\n" + //
+ " \\\"href\\\" : \\\"href\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ " },\\r\\n" + //
+ " \\\"category\\\" : \\\"category\\\",\\r\\n" + //
+ " \\\"@schemaLocation\\\" : \\\"http://example.com/aeiou\\\"\\r\\n" + //
+ "}";
+
+ public static final String RESOURCE_LIST_JSON_RESPONSE = "[" + RESOURCE_JSON_RESPONSE + "]";
+}
diff --git a/src/test/java/org/etsi/osl/services/reposervices/ram702/ResourceActivationRepoServiceTest.java b/src/test/java/org/etsi/osl/services/reposervices/ram702/ResourceActivationRepoServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5703507057f4c725305d373f499c91c3f4785235
--- /dev/null
+++ b/src/test/java/org/etsi/osl/services/reposervices/ram702/ResourceActivationRepoServiceTest.java
@@ -0,0 +1,418 @@
+package org.etsi.osl.services.reposervices.ram702;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import org.etsi.osl.tmf.OpenAPISpringBoot;
+import org.etsi.osl.tmf.ram702.api.ApiException;
+import org.etsi.osl.tmf.ram702.api.ResourceNotFoundException;
+import org.etsi.osl.tmf.ram702.repo.ResourceActivationRepository;
+import org.etsi.osl.tmf.ram702.reposervices.ResourceActivationRepoService;
+import org.etsi.osl.tmf.ri639.model.Resource;
+import org.etsi.osl.tmf.ri639.model.ResourceCreate;
+import org.etsi.osl.tmf.ri639.model.ResourceStatusType;
+import org.etsi.osl.tmf.ri639.model.ResourceUpdate;
+import org.etsi.osl.tmf.ri639.model.ResourceUsageStateType;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.mock.mockito.SpyBean;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Unit tests for {@link ResourceActivationRepoService}.
+ *
+ * This class uses Mockito and Spring's testing framework to mock dependencies
+ * and verify the behavior of the ResourceActivationRepoService.
+ */
+@RunWith(SpringRunner.class)
+@ActiveProfiles("testing")
+@SpringBootTest(classes = OpenAPISpringBoot.class)
+public class ResourceActivationRepoServiceTest {
+ /**
+ * The service being tested, with a spy to allow partial mocking of certain methods.
+ */
+ @SpyBean
+ @Autowired
+ private ResourceActivationRepoService resourceActivationRepoService;
+
+ /**
+ * Mock for the {@link ResourceActivationRepository} to simulate repository operations.
+ */
+ @MockBean
+ private ResourceActivationRepository resourceActivationRepo;
+
+ private static ResourceCreate resourceCreate;
+
+ private static Resource resource;
+
+ /**
+ * Loads test data from JSON files before all tests.
+ *
+ * @throws Exception if there is an error loading the test data.
+ */
+ @BeforeClass
+ public static void setupBeforeClass() {
+ // Load resourceCreate and resourceUpdare from the
+ // JSON files into the respective classes
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+
+ // Configure the mapper to ignore unknown fields that are present in
+ // the JSON but not in the class. This is needed to be able to
+ // update a resource using a Resource or a ResourceUpdate.
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ resourceCreate = mapper.readValue(
+ new File(
+ "src/test/resources/reposervices/ram702/resourceCreate.json"
+ ),
+ ResourceCreate.class
+ );
+
+ resource = mapper.readValue(
+ new File(
+ "src/test/resources/reposervices/ram702/resource.json"
+ ),
+ Resource.class
+ );
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ // Assert that the files were properly loaded
+ assertNotNull(resourceCreate);
+ assertNotNull(resource);
+ }
+
+ /**
+ * Sets up common mock behavior for the repository before each test.
+ * @throws ResourceNotFoundException
+ */
+ @Before
+ public void setupBefore() throws ResourceNotFoundException {
+ when(resourceActivationRepo.findByUuid(anyString())).thenReturn(Optional.of(resource));
+ when(resourceActivationRepo.save(any(Resource.class))).thenReturn(resource);
+ doReturn(resource).when(resourceActivationRepoService).getResourceEager(anyString());
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#findByUuid(String)} when a resource is found.
+ *
+ * @throws ResourceNotFoundException if the resource is not found (not expected).
+ */
+ @Test
+ public void testFindByUuidWhenResourceIsFound() throws ResourceNotFoundException {
+ // When
+ Resource result = resourceActivationRepoService.findByUuid(anyString());
+
+ // Then
+ assertNotNull(result);
+ verify(resourceActivationRepo, times(1)).findByUuid(anyString());
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#findByUuid(String)} when no resource is found.
+ * This ensures that a {@link ResourceNotFoundException} is thrown when the resource does not exist.
+ *
+ * @throws ResourceNotFoundException expected exception if the resource is not found.
+ */
+ @Test
+ public void testFindByUuidWhenResourceIsNotFound() throws ResourceNotFoundException {
+ // Given
+ when(resourceActivationRepo.findByUuid(anyString())).thenReturn(Optional.empty());
+
+ // When
+ ResourceNotFoundException exception = assertThrows(ResourceNotFoundException.class, () -> {
+ resourceActivationRepoService.findByUuid("123");
+ });
+
+ // Then
+ assertEquals("Resource not found with UUID: 123", exception.getMessage());
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#findAll()} to verify it retrieves all resources.
+ */
+ @Test
+ public void testFindAllResources() {
+ // Given
+ List resources = new ArrayList<>();
+ Resource resource1 = new Resource();
+ resource1.setName("resource1");
+ Resource resource2 = new Resource();
+ resource2.setName("resource2");
+ resources.add(resource1);
+ resources.add(resource2);
+
+ // Mock repository to return the list of resources
+ when(resourceActivationRepo.findAll()).thenReturn(resources);
+
+ // When
+ List result = resourceActivationRepoService.findAll();
+
+ // Then
+ assertNotNull(result);
+ assertEquals(2, result.size());
+ assertEquals("resource1", result.get(0).getName());
+ assertEquals("resource2", result.get(1).getName());
+ verify(resourceActivationRepo, times(1)).findAll();
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#addResource(ResourceCreate)} to verify resource creation.
+ */
+ @Test
+ public void testAddResource() {
+ // When
+ Resource result = resourceActivationRepoService.addResource(resourceCreate);
+
+ // Then
+ assertNotNull(result);
+ assertEquals("test_resource", result.getName());
+ verify(resourceActivationRepo, times(1)).save(any(Resource.class));
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#updateResource(String, ResourceUpdate, boolean)}
+ * to verify resource update when the resource is found.
+ *
+ * @throws ResourceNotFoundException if the resource is not found (not expected).
+ */
+ @Test
+ public void testUpdateResourceWhenResourceIsFound() throws ResourceNotFoundException{
+ ResourceUpdate update = new ResourceUpdate();
+ update.setName("updated_name");
+ update.setCategory("updated_category");
+ update.setDescription("Updated description");
+ update.setStartOperatingDate(OffsetDateTime.now());
+ update.setEndOperatingDate(OffsetDateTime.now().plusDays(1));
+ update.setUsageState(ResourceUsageStateType.ACTIVE);
+ update.setResourceStatus(ResourceStatusType.AVAILABLE);
+ update.setResourceVersion("2.0");
+
+ // When
+ Resource updatedResource = resourceActivationRepoService.updateResource("123", update, false);
+
+ // Then
+ assertNotNull(updatedResource);
+ assertEquals("updated_name", updatedResource.getName());
+ assertEquals("updated_name", updatedResource.getName());
+ assertEquals("updated_category", updatedResource.getCategory());
+ assertEquals("Updated description", updatedResource.getDescription());
+ assertNotNull(updatedResource.getStartOperatingDate());
+ assertNotNull(updatedResource.getEndOperatingDate());
+ assertEquals(ResourceUsageStateType.ACTIVE, updatedResource.getUsageState());
+ assertEquals(ResourceStatusType.AVAILABLE, updatedResource.getResourceStatus());
+ assertEquals("2.0", updatedResource.getResourceVersion());
+
+ verify(resourceActivationRepo, times(1)).save(any(Resource.class));
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#updateResource(String, ResourceUpdate, boolean)}
+ * to verify that a {@link ResourceNotFoundException} is thrown when the resource does not exist.
+ * @throws ResourceNotFoundException
+ */
+ @Test
+ public void testUpdateResourceWhenResourceIsNotFound() throws ResourceNotFoundException {
+ // Given
+ doReturn(null).when(resourceActivationRepoService).getResourceEager(anyString());
+ ResourceUpdate update = new ResourceUpdate();
+
+ // When
+ ResourceNotFoundException exception = assertThrows(ResourceNotFoundException.class, () -> {
+ resourceActivationRepoService.updateResource("123", update, false);
+ });
+
+ // Then
+ assertEquals("Resource not found with UUID: 123", exception.getMessage());
+ verify(resourceActivationRepo, never()).save(any(Resource.class));
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#deleteByUuid(String)} to verify successful resource deletion.
+ *
+ * @throws ApiException if there is an error during the deletion process (not expected).
+ */
+ @Test
+ public void testDeleteByUuidWhenResourceIsFound() throws ApiException {
+ // When
+ resourceActivationRepoService.deleteByUuid("123");
+
+ // Then
+ verify(resourceActivationRepo, times(1)).delete(resource);
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#deleteByUuid(String)} to verify that an {@link ApiException}
+ * is thrown when the resource to delete is not found.
+ */
+ @Test
+ public void testDeleteByUuidWhenResourceIsNotFound() {
+ // Given
+ when(resourceActivationRepo.findByUuid("123")).thenReturn(Optional.empty());
+
+ // When
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ resourceActivationRepoService.deleteByUuid("123");
+ });
+
+ // Then
+ assertEquals("Resource not found with UUID: 123", exception.getMessage());
+ verify(resourceActivationRepo, never()).delete(any(Resource.class));
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#addOrUpdateResourceByNameCategoryVersion(String, String, String, ResourceCreate)}
+ * when an existing resource is found and updated.
+ *
+ * @throws ApiException if there is an error during the update process.
+ */
+ @Test
+ public void testAddOrUpdateResourceByNameCategoryVersionWhenResourceExists() throws ApiException {
+ // Given
+ ResourceUpdate update = new ResourceUpdate();
+ update.setName("updated_name");
+
+ String name = "test_resource";
+ String category = "Category 1";
+ String version = "1.0";
+
+ List existingResources = Collections.singletonList(resource);
+
+ // Mock the repository to return the existing resource
+ when(resourceActivationRepo.findByNameAndCategoryAndResourceVersion(anyString(), anyString(), anyString()))
+ .thenReturn(existingResources);
+
+ // Mock the updateResource method to return the updated resource
+ when(resourceActivationRepoService.updateResource("123", update, false))
+ .thenReturn(resource);
+
+ // When
+ Resource result = resourceActivationRepoService.addOrUpdateResourceByNameCategoryVersion(name, category, version, resourceCreate);
+
+ // Then
+ assertNotNull(result);
+ assertEquals("test_resource", result.getName());
+ verify(resourceActivationRepoService, times(1)).updateResource("123", update, false);
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#addOrUpdateResourceByNameCategoryVersion(String, String, String, ResourceCreate)}
+ * when no existing resource is found, and a new one is created.
+ *
+ * @throws ApiException if there is an error during the creation process.
+ */
+ @Test
+ public void testAddOrUpdateResourceByNameCategoryVersionWhenResourceDoesNotExist() throws ApiException {
+ // Given
+ String name = "test_resource";
+ String category = "Category 1";
+ String version = "1.0";
+
+ // Mock an empty list of existing resources
+ List noResources = new ArrayList<>();
+
+ // Mock the repository to return no existing resources
+ when(resourceActivationRepo.findByNameAndCategoryAndResourceVersion(anyString(), anyString(), anyString()))
+ .thenReturn(noResources);
+
+ // Mock the addResource method to return the newly created resource
+ when(resourceActivationRepoService.addResource(resourceCreate)).thenReturn(resource);
+
+ // When
+ Resource result = resourceActivationRepoService.addOrUpdateResourceByNameCategoryVersion(name, category, version, resourceCreate);
+
+ // Then
+ assertNotNull(result);
+ assertEquals("test_resource", result.getName());
+ verify(resourceActivationRepoService, times(1)).addResource(any(ResourceCreate.class));
+ verify(resourceActivationRepoService, never()).updateResource(result.getId(), resourceCreate, false);
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#addOrUpdateResourceByNameCategoryVersion(String, String, String, ResourceCreate)}
+ * to handle an {@link ApiException} being thrown during the process.
+ *
+ * @throws ApiException expected exception during the process.
+ */
+ @Test
+ public void testAddOrUpdateResourceByNameCategoryVersionThrowsApiException() throws ApiException {
+ // Given
+ String name = "Faulty Resource";
+ String category = "Faulty Category";
+ String version = "1.0";
+
+ // Mock an existing resource
+ List existingResources = Collections.singletonList(resource);
+
+ // Mock the repository to return the existing resource
+ when(resourceActivationRepo.findByNameAndCategoryAndResourceVersion(name, category, version))
+ .thenReturn(existingResources);
+
+ // Mock the updateResource method to throw an ApiException
+ doThrow(new ResourceNotFoundException("Error updating resource"))
+ .when(resourceActivationRepoService).updateResource(resource.getId(), resourceCreate, false);
+
+ // When and Then
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ resourceActivationRepoService.addOrUpdateResourceByNameCategoryVersion(name, category, version, resourceCreate);
+ });
+
+ assertEquals("Error updating resource", exception.getMessage());
+ verify(resourceActivationRepoService, times(1)).updateResource(resource.getId(), resourceCreate, false);
+ verify(resourceActivationRepoService, never()).addResource(resourceCreate); // Add should not be called
+ }
+
+ /**
+ * Test for {@link ResourceActivationRepoService#raiseResourceAttributeValueChangeEventNotification(Resource)}
+ * to ensure a resource attribute value change notification is published.
+ */
+ @Test
+ public void testFindAllActiveResourcesToTerminate() {
+ // Given
+ List resources = new ArrayList<>();
+ Resource resource1 = mock(Resource.class);
+ when(resource1.getId()).thenReturn("uuid1");
+ resources.add(resource1);
+
+ when(resourceActivationRepo.findActiveToTerminate()).thenReturn(resources);
+
+ // When
+ List result = resourceActivationRepoService.findAllActiveResourcesToTerminate();
+
+ // Then
+ assertNotNull(result);
+ assertEquals(1, result.size());
+ assertEquals("uuid1", result.get(0));
+ verify(resourceActivationRepo, times(1)).findActiveToTerminate();
+ }
+}
diff --git a/src/test/resources/reposervices/ram702/resource.json b/src/test/resources/reposervices/ram702/resource.json
new file mode 100644
index 0000000000000000000000000000000000000000..609d70d4f3ca0813c7a44862fef56d851c971b99
--- /dev/null
+++ b/src/test/resources/reposervices/ram702/resource.json
@@ -0,0 +1,61 @@
+{
+ "uuid": "eec4bf3c-3698-4815-b205-70b43df083d4",
+ "endOperatingDate": "2024-09-24T11:43:57.355Z",
+ "startOperatingDate": "2024-09-23T11:45:13.347536Z",
+ "@baseType": "BaseEntity",
+ "@schemaLocation": null,
+ "@type": null,
+ "href": null,
+ "name": "test_resource",
+ "id": "eec4bf3c-3698-4815-b205-70b43df083d4",
+ "category": "test_category",
+ "description": "A test resource",
+ "resourceVersion": "1.0",
+ "activationFeature": [],
+ "administrativeState": null,
+ "attachment": [],
+ "note": [
+ {
+ "uuid": "312ec354-1a82-4f54-b1ad-e36ce81301ba",
+ "date": "2024-09-24T10:52:41.912820494Z",
+ "@baseType": "BaseEntity",
+ "@schemaLocation": null,
+ "@type": null,
+ "href": null,
+ "author": "API",
+ "system": null,
+ "text": "Resource status: null"
+ }
+ ],
+ "operationalState": null,
+ "place": null,
+ "relatedParty": [
+ {
+ "uuid": "bdfa090e-2dbf-4b61-ab02-b28eaa946423",
+ "@baseType": "BaseRootEntity",
+ "@schemaLocation": null,
+ "@type": "org.etsi.osl.tmf.prm669.model.RelatedParty",
+ "href": null,
+ "name": "admin",
+ "role": "REQUESTER",
+ "@referredType": "SimpleUsername_Individual",
+ "id": "admin",
+ "extendedInfo": ""
+ }
+ ],
+ "resourceCharacteristic": [],
+ "resourceRelationship": [],
+ "resourceSpecification": {
+ "@baseType": "BaseRootEntity",
+ "@schemaLocation": null,
+ "@type": "org.etsi.osl.tmf.rcm634.model.ResourceSpecificationRef",
+ "href": null,
+ "name": "Application@argoproj.io/v1alpha1@default@https://10.255.28.198:6443/",
+ "version": null,
+ "@referredType": null,
+ "id": "87094d78-75c2-4876-9a75-d3cc34119915"
+ },
+ "resourceStatus": "AVAILABLE",
+ "usageState": "ACTIVE",
+ "value": null
+}
\ No newline at end of file
diff --git a/src/test/resources/reposervices/ram702/resourceCreate.json b/src/test/resources/reposervices/ram702/resourceCreate.json
new file mode 100644
index 0000000000000000000000000000000000000000..59d75d8230583d50ebcac08dc99cd52a0966e290
--- /dev/null
+++ b/src/test/resources/reposervices/ram702/resourceCreate.json
@@ -0,0 +1,35 @@
+{
+ "endOperatingDate": "2024-09-24T11:43:57.355Z",
+ "startOperatingDate": "2024-09-23T11:45:13.347536Z",
+ "atBaseType": null,
+ "atSchemaLocation": null,
+ "atType": null,
+ "category": null,
+ "description": null,
+ "name": "test_resource",
+ "resourceVersion": null,
+ "activationFeature": null,
+ "administrativeState": null,
+ "attachment": null,
+ "note": null,
+ "operationalState": null,
+ "place": null,
+ "relatedParty": null,
+ "resourceCharacteristic": null,
+ "resourceRelationship": null,
+ "resourceSpecification": {
+ "@baseType": "BaseRootEntity",
+ "@schemaLocation": null,
+ "@type": "org.etsi.osl.tmf.rcm634.model.ResourceSpecificationRef",
+ "href": null,
+ "name": "Application@argoproj.io/v1alpha1@default@https://10.255.28.198:6443/",
+ "version": null,
+ "@referredType": null,
+ "id": "87094d78-75c2-4876-9a75-d3cc34119915"
+ },
+ "resourceStatus": null,
+ "usageState": null,
+ "@baseType": null,
+ "@schemaLocation": null,
+ "@type": null
+}
\ No newline at end of file