2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 * SPDX-License-Identifier: Apache-2.0
17 * ============LICENSE_END=========================================================
19 package org.openecomp.sdc.be.servlets;
21 import static org.assertj.core.api.Assertions.assertThat;
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.ArgumentMatchers.anyBoolean;
24 import static org.mockito.ArgumentMatchers.anyString;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.Mockito.doThrow;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
31 import java.io.FileReader;
32 import java.io.IOException;
33 import java.net.URISyntaxException;
35 import java.nio.file.Path;
36 import java.util.Optional;
37 import javax.servlet.ServletContext;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpSession;
40 import javax.ws.rs.client.Entity;
41 import javax.ws.rs.core.MediaType;
42 import javax.ws.rs.core.Response;
43 import org.apache.commons.lang3.tuple.ImmutablePair;
44 import org.eclipse.jetty.http.HttpStatus;
45 import org.glassfish.hk2.utilities.binding.AbstractBinder;
46 import org.glassfish.jersey.client.ClientConfig;
47 import org.glassfish.jersey.media.multipart.FormDataMultiPart;
48 import org.glassfish.jersey.media.multipart.MultiPartFeature;
49 import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
50 import org.glassfish.jersey.server.ResourceConfig;
51 import org.glassfish.jersey.test.JerseyTest;
52 import org.glassfish.jersey.test.TestProperties;
53 import org.json.simple.JSONObject;
54 import org.json.simple.parser.JSONParser;
55 import org.json.simple.parser.ParseException;
56 import org.junit.jupiter.api.AfterEach;
57 import org.junit.jupiter.api.BeforeEach;
58 import org.junit.jupiter.api.Test;
59 import org.mockito.Mock;
60 import org.mockito.MockitoAnnotations;
61 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
62 import org.openecomp.sdc.be.components.impl.ModelBusinessLogic;
63 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
64 import org.openecomp.sdc.be.components.impl.ResourceImportManager;
65 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
66 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
67 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
68 import org.openecomp.sdc.be.config.ConfigurationManager;
69 import org.openecomp.sdc.be.config.SpringConfig;
70 import org.openecomp.sdc.be.dao.api.ActionStatus;
71 import org.openecomp.sdc.be.impl.ComponentsUtils;
72 import org.openecomp.sdc.be.impl.ServletUtils;
73 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
74 import org.openecomp.sdc.be.model.Model;
75 import org.openecomp.sdc.be.model.Resource;
76 import org.openecomp.sdc.be.model.User;
77 import org.openecomp.sdc.be.servlets.builder.ServletResponseBuilder;
78 import org.openecomp.sdc.be.servlets.exception.OperationExceptionMapper;
79 import org.openecomp.sdc.be.user.Role;
80 import org.openecomp.sdc.be.user.UserBusinessLogic;
81 import org.openecomp.sdc.common.api.ConfigurationSource;
82 import org.openecomp.sdc.common.api.Constants;
83 import org.openecomp.sdc.common.impl.ExternalConfiguration;
84 import org.openecomp.sdc.common.impl.FSConfigurationSource;
85 import org.openecomp.sdc.exception.ResponseFormat;
86 import org.springframework.context.ApplicationContext;
87 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
88 import org.springframework.web.context.WebApplicationContext;
90 class ResourceUploadServletTest extends JerseyTest {
91 private static final String USER_ID = "cs0008";
92 private static final User USER = new User(USER_ID);
95 private HttpServletRequest request;
97 private HttpSession session;
99 private ServletContext servletContext;
101 private WebAppContextWrapper webAppContextWrapper;
103 private WebApplicationContext webApplicationContext;
105 private UserBusinessLogic userBusinessLogic;
107 private ComponentInstanceBusinessLogic componentInstanceBusinessLogic;
109 private ComponentsUtils componentsUtils;
111 private ServletUtils servletUtils;
113 private ResourceImportManager resourceImportManager;
115 private ResourceBusinessLogic resourceBusinessLogic;
117 private ResponseFormat responseFormat;
119 private ModelBusinessLogic modelBusinessLogic;
121 private ResponseFormatManager responseFormatManager;
122 private final String modelName = "ETSI-SOL001-331";
124 private final Path rootPath = Path.of("/v1/catalog/upload");
125 private final Path bulkImportPath = rootPath.resolve("resource/import");
126 private final String multipartPath = "/v1/catalog/upload/multipart";
130 void resetMock() throws Exception {
138 void after() throws Exception {
143 protected ResourceConfig configure() {
144 MockitoAnnotations.openMocks(this);
145 forceSet(TestProperties.CONTAINER_PORT, "0");
146 final ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
147 return new ResourceConfig(ResourceUploadServlet.class)
148 .register(new AbstractBinder() {
150 protected void configure() {
151 bind(request).to(HttpServletRequest.class);
152 bind(userBusinessLogic).to(UserBusinessLogic.class);
153 bind(componentInstanceBusinessLogic).to(ComponentInstanceBusinessLogic.class);
154 bind(componentsUtils).to(ComponentsUtils.class);
155 bind(servletUtils).to(ServletUtils.class);
156 bind(resourceImportManager).to(ResourceImportManager.class);
157 bind(resourceBusinessLogic).to(ResourceBusinessLogic.class);
158 bind(modelBusinessLogic).to(ModelBusinessLogic.class);
161 .register(new OperationExceptionMapper(new ServletResponseBuilder(), responseFormatManager))
162 .register(MultiPartFeature.class)
163 .property("contextConfig", context);
167 protected void configureClient(final ClientConfig config) {
168 config.register(MultiPartFeature.class);
172 when(request.getSession()).thenReturn(session);
173 when(session.getServletContext()).thenReturn(servletContext);
174 when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR))
175 .thenReturn(webAppContextWrapper);
176 when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext);
177 when(webApplicationContext.getBean(ModelBusinessLogic.class)).thenReturn(modelBusinessLogic);
178 when(webApplicationContext.getBean(UserBusinessLogic.class)).thenReturn(userBusinessLogic);
179 when(userBusinessLogic.getUser(USER_ID, false)).thenReturn(USER);
180 when(request.getHeader(Constants.USER_ID_HEADER)).thenReturn(USER_ID);
181 when(webApplicationContext.getBean(ServletUtils.class)).thenReturn(servletUtils);
182 when(servletUtils.getComponentsUtils()).thenReturn(componentsUtils);
186 final String appConfigDir = "src/test/resources/config/catalog-be";
187 final ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
188 final ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
189 final org.openecomp.sdc.be.config.Configuration configuration = new org.openecomp.sdc.be.config.Configuration();
190 configuration.setJanusGraphInMemoryGraph(true);
191 configurationManager.setConfiguration(configuration);
192 ExternalConfiguration.setAppName("catalog-be");
195 private void initTestData() {
197 user.setUserId(USER_ID);
198 user.setRole(Role.ADMIN.name());
199 when(userBusinessLogic.getUser(USER_ID)).thenReturn(user);
203 void uploadMultipartWithModelSuccessTest() throws IOException, ParseException, URISyntaxException {
204 when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200);
205 when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat);
206 when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic);
207 when(userBusinessLogic.getUser(anyString())).thenReturn(user);
208 when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true);
209 when(resourceImportManager.importNormativeResource(anyString(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()))
210 .thenReturn(new ImmutablePair<>(new Resource(), ActionStatus.CREATED));
211 when(modelBusinessLogic.findModel(modelName)).thenReturn(Optional.of(new Model(modelName)));
212 final var response = target().path(multipartPath).request(MediaType.APPLICATION_JSON)
213 .header(Constants.USER_ID_HEADER, USER_ID)
214 .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType001.zip",
215 "src/test/resources/node-types/nodeTypeWithModelsField.json"), MediaType.MULTIPART_FORM_DATA), Response.class);
216 assertThat(response.getStatus()).isEqualTo(HttpStatus.OK_200);
220 void uploadMultipartWithoutModelsFieldSuccessTest() throws IOException, ParseException, URISyntaxException {
221 when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200);
222 when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat);
223 when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic);
224 when(userBusinessLogic.getUser(anyString())).thenReturn(user);
225 when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true);
226 when(resourceImportManager.importNormativeResource(anyString(), any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()))
227 .thenReturn(new ImmutablePair<>(new Resource(), ActionStatus.CREATED));
228 final var response = target().path(multipartPath).request(MediaType.APPLICATION_JSON)
229 .header(Constants.USER_ID_HEADER, USER_ID)
230 .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType002.zip",
231 "src/test/resources/node-types/nodeTypeWithoutModelsField.json"), MediaType.MULTIPART_FORM_DATA), Response.class);
232 assertThat(response.getStatus()).isEqualTo(HttpStatus.OK_200);
236 void uploadMultipartFailWithEmptyModelsTest() throws IOException, ParseException, URISyntaxException {
237 when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
238 when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
239 when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic);
240 when(userBusinessLogic.getUser(anyString())).thenReturn(user);
241 when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true);
242 when(modelBusinessLogic.findModel("")).thenReturn(Optional.empty());
243 final Response response = target().path(multipartPath).request(MediaType.APPLICATION_JSON)
244 .header(Constants.USER_ID_HEADER, USER_ID)
245 .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType002.zip",
246 "src/test/resources/node-types/nodeTypeWithEmptyModels.json"), MediaType.MULTIPART_FORM_DATA), Response.class);
247 assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
251 void uploadMultipartFailWithModelNotFoundTest() throws IOException, ParseException, URISyntaxException {
252 when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic);
253 when(userBusinessLogic.getUser(anyString())).thenReturn(user);
254 when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true);
255 when(modelBusinessLogic.findModel(modelName)).thenReturn(Optional.empty());
256 final var response = target().path(multipartPath).request(MediaType.APPLICATION_JSON)
257 .header(Constants.USER_ID_HEADER, USER_ID)
258 .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType001.zip",
259 "src/test/resources/node-types/nodeTypeWithModelsField.json"), MediaType.MULTIPART_FORM_DATA), Response.class);
260 assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
264 void uploadMultipartThrowsBusinessExceptionTest() throws IOException, ParseException, URISyntaxException {
265 when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
266 when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
267 when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic);
268 when(userBusinessLogic.getUser(anyString())).thenReturn(user);
269 when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true);
270 final var response = target().path(multipartPath).request(MediaType.APPLICATION_JSON)
271 .header(Constants.USER_ID_HEADER, USER_ID)
272 .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType001.zip",
273 "src/test/resources/node-types/invalid.json"), MediaType.MULTIPART_FORM_DATA), Response.class);
274 assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
278 void bulkImportSuccessTest() throws IOException, ParseException {
279 when(responseFormat.getStatus()).thenReturn(HttpStatus.CREATED_201);
280 when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat);
282 final Response response = doValidCallToBulkImport(USER_ID);
284 verify(resourceImportManager).importAllNormativeResource(anyString(), any(), eq(USER), eq(false), eq(false));
285 assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED_201);
289 void bulkImportFailTest_businessException() throws IOException, ParseException {
290 doThrow(new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR))
291 .when(resourceImportManager)
292 .importAllNormativeResource(anyString(), any(), eq(USER), eq(false), eq(false));
294 final Response response = doValidCallToBulkImport(USER_ID);
296 assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
300 void bulkImportFailTest_unexpectedException() throws IOException, ParseException {
301 when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
302 when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
304 doThrow(RuntimeException.class)
305 .when(resourceImportManager)
306 .importAllNormativeResource(anyString(), any(), eq(USER), eq(false), eq(false));
308 final Response response = doValidCallToBulkImport(USER_ID);
310 assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
314 void bulkImportFailTest_invalidUser() throws IOException, ParseException {
315 var userId = "userId";
316 when(userBusinessLogic.getUser(userId, false)).thenThrow(ComponentException.class);
317 when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
318 when(componentsUtils.getResponseFormat(any(ComponentException.class))).thenReturn(responseFormat);
320 final Response response = doValidCallToBulkImport(userId);
322 assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
325 private Response doValidCallToBulkImport(final String userId) throws IOException, ParseException {
326 final Path nodeTypesYamlPath = Path.of("src/test/resources/node-types/nodeTypes.yaml");
327 final Path nodeTypeMetadataJsonPath = Path.of("src/test/resources/node-types/payload.json");
328 return target().path(bulkImportPath.toString()).request(MediaType.APPLICATION_JSON)
329 .header(Constants.USER_ID_HEADER, userId)
331 Entity.entity(buildBulkImportMultiPartData(nodeTypesYamlPath, nodeTypeMetadataJsonPath), MediaType.MULTIPART_FORM_DATA),
336 private String parseFileToJsonString(final Path jsonFilePath) throws IOException, ParseException {
337 final JSONObject inputData = (JSONObject) new JSONParser().parse(
338 new FileReader(jsonFilePath.toString()));
339 return inputData.toJSONString();
342 private File getFile(final String fileName) throws URISyntaxException {
343 final URL resource = this.getClass().getClassLoader().getResource(fileName);
344 if (resource == null) {
345 throw new IllegalArgumentException("file not found! " + fileName);
347 return new File(resource.toURI());
350 private FormDataMultiPart buildFormDataMultiPart(final String zipFilePath, final String inputJsonData)
351 throws IOException, ParseException, URISyntaxException {
352 final FileDataBodyPart filePart = new FileDataBodyPart("resourceZip", getFile(zipFilePath));
353 final FormDataMultiPart multipartEntity = new FormDataMultiPart();
354 multipartEntity.bodyPart(filePart);
355 multipartEntity.field("resourceMetadata", parseFileToJsonString(Path.of(inputJsonData)));
356 return multipartEntity;
359 private FormDataMultiPart buildBulkImportMultiPartData(final Path nodeTypesYamlPath, final Path payloadJsonPath)
360 throws IOException, ParseException {
361 final FileDataBodyPart formDataBodyPart = new FileDataBodyPart("nodeTypesYaml", nodeTypesYamlPath.toFile());
362 final FormDataMultiPart multipartEntity = new FormDataMultiPart();
363 multipartEntity.bodyPart(formDataBodyPart);
364 multipartEntity.field("nodeTypeMetadataJson", parseFileToJsonString(payloadJsonPath), MediaType.APPLICATION_JSON_TYPE);
365 multipartEntity.field("createNewVersion", String.valueOf(false), MediaType.TEXT_PLAIN_TYPE);
366 return multipartEntity;