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