chore: Removing deprecated devfile endpoints. Updating the 2.2 schema to the latest version

Signed-off-by: Ilya Buziuk <ibuziuk@redhat.com>
pull/389/head
Ilya Buziuk 2022-11-15 17:19:08 +01:00 committed by Ilya Buziuk
parent e897e7b2f2
commit 642732668b
5 changed files with 537 additions and 1291 deletions

View File

@ -1,323 +0,0 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.multiuser.permission.devfile.server.filters;
import static io.restassured.RestAssured.given;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.testng.Assert.assertEquals;
import io.restassured.response.Response;
import java.util.Collections;
import java.util.HashSet;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.devfile.server.DevfileService;
import org.eclipse.che.api.devfile.server.UserDevfileManager;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileEntityProvider;
import org.eclipse.che.api.workspace.server.devfile.DevfileParser;
import org.eclipse.che.api.workspace.server.devfile.DevfileVersionDetector;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileIntegrityValidator;
import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValidator;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.subject.Subject;
import org.eclipse.che.multiuser.permission.devfile.server.TestObjectGenerator;
import org.eclipse.che.multiuser.permission.devfile.server.UserDevfileDomain;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
/** Tests for {@link UserDevfilePermissionsFilter}. */
@Listeners(value = {EverrestJetty.class, MockitoTestNGListener.class})
public class UserDevfilePermissionsFilterTest {
private static final String USERNAME = "userok";
ApiExceptionMapper mapper;
CheJsonProvider jsonProvider = new CheJsonProvider(new HashSet<>());
private DevfileEntityProvider devfileEntityProvider =
new DevfileEntityProvider(
new DevfileParser(
new DevfileSchemaValidator(new DevfileSchemaProvider(), new DevfileVersionDetector()),
new DevfileIntegrityValidator(Collections.emptyMap())));
@SuppressWarnings("unused")
private static final EnvironmentFilter FILTER = new EnvironmentFilter();
@Mock private static Subject subject;
@Mock private UserDevfileManager userDevfileManager;
private UserDevfilePermissionsFilter permissionsFilter;
@Mock private DevfileService devfileService;
private UserDevfileDto userDevfileDto = TestObjectGenerator.createUserDevfileDto();
private UserDevfileImpl userDevfile =
new UserDevfileImpl(userDevfileDto, TestObjectGenerator.TEST_ACCOUNT);
//
@BeforeMethod
public void setUp() throws Exception {
lenient().when(subject.getUserName()).thenReturn(USERNAME);
lenient().when(userDevfileManager.getById(any())).thenReturn(userDevfile);
permissionsFilter = spy(new UserDevfilePermissionsFilter(userDevfileManager));
lenient()
.doThrow(new ForbiddenException(""))
.when(subject)
.checkPermission(anyString(), anyString(), anyString());
}
@Test
public void shouldNotCheckAnyPermissionOnDevfileCreate() {
// given
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(userDevfileDto)
.when()
.post(SECURE_PATH + "/devfile/");
// then
assertEquals(response.getStatusCode(), 204);
verifyNoMoreInteractions(subject);
}
@Test
public void shouldNotCheckAnyPermissionOnDevfileSearch()
throws BadRequestException, ForbiddenException, NotFoundException, ServerException {
// given
Mockito.when(devfileService.getUserDevfiles(any(), any(), any()))
.thenReturn(jakarta.ws.rs.core.Response.ok().build());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile/search");
// then
assertEquals(response.getStatusCode(), 200);
verifyNoMoreInteractions(subject);
}
@Test
public void shouldNotCheckAnyPermissionOnDevfileSchema()
throws NotFoundException, ServerException {
// given
Mockito.when(devfileService.getSchema(CURRENT_API_VERSION))
.thenReturn(jakarta.ws.rs.core.Response.ok().build());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile");
// then
assertEquals(response.getStatusCode(), 200);
verifyNoMoreInteractions(subject);
}
@Test
public void shouldCheckReadPermissionsOnFetchingUserDevfileById() throws Exception {
// given
Mockito.when(devfileService.getById(eq(userDevfileDto.getId()))).thenReturn(userDevfileDto);
doNothing()
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 200);
verify(devfileService).getById(eq(userDevfileDto.getId()));
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
}
@Test
public void shouldBeAbleToFailOnCheckPermissionDevfileReadByID() throws ForbiddenException {
// given
doThrow(new ForbiddenException("forbidden"))
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.get(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 403);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.READ));
}
@Test
public void shouldChecksPermissionDevfileUpdate() throws ForbiddenException {
// given
doNothing()
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(userDevfileDto)
.when()
.put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 204);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
}
@Test
public void shouldBeAbleToFailOnCheckPermissionDevfileUpdate() throws ForbiddenException {
// given
doThrow(new ForbiddenException("forbidden"))
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(userDevfileDto)
.when()
.put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 403);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.UPDATE));
}
@Test
public void shouldChecksPermissionDevfileDelete() throws ForbiddenException {
// given
doNothing()
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 204);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
}
@Test
public void shouldBeAbleToFailOnCheckPermissionDevfileDelete() throws ForbiddenException {
// given
doThrow(new ForbiddenException("forbidden"))
.when(subject)
.checkPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 403);
verify(permissionsFilter)
.doCheckPermission(
eq(UserDevfileDomain.DOMAIN_ID),
eq(userDevfileDto.getId()),
eq(UserDevfileDomain.DELETE));
}
@Filter
public static class EnvironmentFilter implements RequestFilter {
public void doFilter(GenericContainerRequest request) {
EnvironmentContext.getCurrent().setSubject(subject);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -12,53 +12,26 @@
package org.eclipse.che.api.devfile.server;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.devfile.server.DtoConverter.asDto;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import static org.eclipse.che.api.workspace.server.devfile.Constants.SUPPORTED_VERSIONS;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.ForbiddenException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.schema.DevfileSchemaProvider;
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.commons.lang.URLEncodedUtils;
import org.eclipse.che.dto.server.DtoFactory;
/** Defines Devfile REST API. */
@Tag(name = "devfile", description = "Devfile REST API")
@ -66,16 +39,9 @@ import org.eclipse.che.dto.server.DtoFactory;
public class DevfileService extends Service {
private final DevfileSchemaProvider schemaCachedProvider;
private final UserDevfileManager userDevfileManager;
private final DevfileServiceLinksInjector linksInjector;
@Inject
public DevfileService(
DevfileSchemaProvider schemaCachedProvider,
UserDevfileManager userDevfileManager,
DevfileServiceLinksInjector linksInjector) {
this.userDevfileManager = userDevfileManager;
this.linksInjector = linksInjector;
public DevfileService(DevfileSchemaProvider schemaCachedProvider) {
this.schemaCachedProvider = schemaCachedProvider;
}
@ -116,236 +82,4 @@ public class DevfileService extends Service {
throw new ServerException(e);
}
}
@Path("/devfile")
@POST
@Consumes({APPLICATION_JSON, "text/yaml", "text/x-yaml"})
@Produces(APPLICATION_JSON)
@Operation(
summary = "Creates a new persistent Devfile from yaml representation",
responses = {
@ApiResponse(
responseCode = "201",
description = "The devfile successfully created",
content = @Content(schema = @Schema(implementation = UserDevfileDto.class))),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(
responseCode = "403",
description = "The user does not have access to create a new devfile"),
@ApiResponse(
responseCode = "409",
description = "Conflict error occurred during the devfile creation"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public Response createFromDevfileYaml(
@Parameter(description = "The devfile to create", required = true) DevfileDto devfile)
throws ConflictException, BadRequestException, ForbiddenException, NotFoundException,
ServerException {
requiredNotNull(devfile, "Devfile");
return Response.status(201)
.entity(
linksInjector.injectLinks(
asDto(
userDevfileManager.createDevfile(
DtoFactory.newDto(UserDevfileDto.class)
.withDevfile(devfile)
.withName(NameGenerator.generate("devfile-", 16)))),
getServiceContext()))
.build();
}
@POST
@Consumes({APPLICATION_JSON})
@Produces(APPLICATION_JSON)
@Operation(
summary = "Creates a new persistent Devfile",
responses = {
@ApiResponse(
responseCode = "201",
description = "The devfile successfully created",
content = @Content(schema = @Schema(implementation = UserDevfileDto.class))),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(
responseCode = "403",
description = "The user does not have access to create a new devfile"),
@ApiResponse(
responseCode = "409",
description =
"Conflict error occurred during the devfile creation"
+ "(e.g. The devfile with such name already exists)"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public Response createFromUserDevfile(
@Parameter(description = "The devfile to create", required = true)
UserDevfileDto userDevfileDto)
throws ConflictException, BadRequestException, ForbiddenException, NotFoundException,
ServerException {
requiredNotNull(userDevfileDto, "Devfile");
return Response.status(201)
.entity(
linksInjector.injectLinks(
asDto(userDevfileManager.createDevfile(userDevfileDto)), getServiceContext()))
.build();
}
@GET
@Path("/{id}")
@Produces(APPLICATION_JSON)
@Operation(
summary = "Get devfile by its identifier",
responses = {
@ApiResponse(
responseCode = "200",
description = "The response contains requested workspace entity"),
@ApiResponse(
responseCode = "404",
description = "The devfile with specified id does not exist"),
@ApiResponse(responseCode = "403", description = "The user is not allowed to read devfile"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public UserDevfileDto getById(
@Parameter(description = "UserDevfile identifier") @PathParam("id") String id)
throws NotFoundException, ServerException, ForbiddenException, BadRequestException {
requiredNotNull(id, "id");
return linksInjector.injectLinks(asDto(userDevfileManager.getById(id)), getServiceContext());
}
@GET
@Path("search")
@Produces(APPLICATION_JSON)
@Operation(
summary =
"Get devfiles which user can read. This operation can be performed only by authorized user. "
+ "It is possible to add additional constraints for the desired devfiles by specifying\n"
+ "multiple query parameters that is representing fields of the devfile. All constrains\n"
+ "would be combined with \"And\" condition. Also, it is possible to specify 'like:' prefix\n"
+ "for the query parameters. In this case instead of an exact match would be used SQL pattern like search.\n"
+ "Examples id=sdfsdf5&devfile.meta.name=like:%dfdf&",
responses = {
@ApiResponse(
responseCode = "200",
description = "The devfiles successfully fetched",
content =
@Content(
array = @ArraySchema(schema = @Schema(implementation = UserDevfileDto.class)))),
@ApiResponse(
responseCode = "500",
description = "Internal server error occurred during devfiles fetching")
})
public Response getUserDevfiles(
@Parameter(description = "The number of the items to skip")
@DefaultValue("0")
@QueryParam("skipCount")
Integer skipCount,
@Parameter(description = "The limit of the items in the response, default is 30, maximum 60")
@DefaultValue("30")
@QueryParam("maxItems")
Integer maxItems,
@Parameter(
description =
"A list of fields and directions of sort. By default items would be sorted by id. Example id:asc,name:desc.")
@QueryParam("order")
String order)
throws ServerException, BadRequestException {
final Set<String> skip = ImmutableSet.of("token", "skipCount", "maxItems", "order");
Map<String, Set<String>> queryParams = URLEncodedUtils.parse(uriInfo.getRequestUri());
final List<Pair<String, String>> query =
queryParams.entrySet().stream()
.filter(param -> !param.getValue().isEmpty())
.filter(param -> !skip.contains(param.getKey()))
.map(entry -> Pair.of(entry.getKey(), entry.getValue().iterator().next()))
.collect(toList());
List<Pair<String, String>> searchOrder = Collections.emptyList();
if (order != null && !order.isEmpty()) {
try {
searchOrder =
Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.withKeyValueSeparator(":")
.split(order)
.entrySet()
.stream()
.map(e -> Pair.of(e.getKey(), e.getValue()))
.collect(toList());
} catch (IllegalArgumentException e) {
throw new BadRequestException("Invalid `order` query parameter format." + e.getMessage());
}
}
Page<? extends UserDevfile> userDevfilesPage =
userDevfileManager.getUserDevfiles(maxItems, skipCount, query, searchOrder);
List<UserDevfileDto> list =
userDevfilesPage.getItems().stream()
.map(DtoConverter::asDto)
.map(dto -> linksInjector.injectLinks(asDto(dto), getServiceContext()))
.collect(toList());
return Response.ok().entity(list).header("Link", createLinkHeader(userDevfilesPage)).build();
}
@PUT
@Path("/{id}")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Operation(
summary = "Update the devfile by replacing all the existing data with update",
responses = {
@ApiResponse(responseCode = "200", description = "The devfile successfully updated"),
@ApiResponse(
responseCode = "400",
description = "Missed required parameters, parameters are not valid"),
@ApiResponse(
responseCode = "403",
description = "The user does not have access to update the devfile"),
@ApiResponse(
responseCode = "409",
description =
"Conflict error occurred during devfile update"
+ "(e.g. Workspace with such name already exists)"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public UserDevfileDto update(
@Parameter(description = "The devfile id") @PathParam("id") String id,
@Parameter(description = "The devfile update", required = true) UserDevfileDto update)
throws BadRequestException, ServerException, ForbiddenException, NotFoundException,
ConflictException {
requiredNotNull(update, "User Devfile configuration");
update.setId(id);
return linksInjector.injectLinks(
asDto(userDevfileManager.updateUserDevfile(update)), getServiceContext());
}
@DELETE
@Path("/{id}")
@Operation(
summary = "Removes the devfile",
responses = {
@ApiResponse(responseCode = "204", description = "The devfile successfully removed"),
@ApiResponse(
responseCode = "403",
description = "The user does not have access to remove the devfile"),
@ApiResponse(responseCode = "500", description = "Internal server error occurred")
})
public void delete(@Parameter(description = "The devfile id") @PathParam("id") String id)
throws BadRequestException, ServerException, ForbiddenException {
userDevfileManager.removeUserDevfile(id);
}
/**
* Checks object reference is not {@code null}
*
* @param object object reference to check
* @param subject used as subject of exception message "{subject} required"
* @throws BadRequestException when object reference is {@code null}
*/
private void requiredNotNull(Object object, String subject) throws BadRequestException {
if (object == null) {
throw new BadRequestException(subject + " required");
}
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.che.api.devfile.server;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriBuilder;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.api.devfile.shared.Constants;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.dto.server.DtoFactory;
import org.everrest.core.impl.uri.UriBuilderImpl;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(MockitoTestNGListener.class)
public class DevfileServiceLinksInjectorTest {
private static final String URI_BASE = "http://localhost:8080";
private static final String SERVICE_PATH = "/devfile";
@Mock ServiceContext context;
@BeforeMethod
public void setUp() {
final UriBuilder uriBuilder = new UriBuilderImpl();
uriBuilder.uri(URI_BASE);
when(context.getBaseUriBuilder()).thenReturn(uriBuilder);
}
@Test
public void shouldInjectLinks() {
// given
final UserDevfileDto userDevfileDto = DtoFactory.newDto(UserDevfileDto.class).withId("id123");
DevfileServiceLinksInjector linksInjector = new DevfileServiceLinksInjector();
// when
final UserDevfileDto withLinks = linksInjector.injectLinks(userDevfileDto, context);
// then
assertEquals(withLinks.getLinks().size(), 1);
assertNotNull(withLinks.getLink(Constants.LINK_REL_SELF));
assertEquals(withLinks.getLinks().get(0).getMethod(), HttpMethod.GET);
assertEquals(withLinks.getLinks().get(0).getHref(), URI_BASE + SERVICE_PATH + "/id123");
assertEquals(withLinks.getLinks().get(0).getProduces(), MediaType.APPLICATION_JSON);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -12,12 +12,7 @@
package org.eclipse.che.api.devfile.server;
import static io.restassured.RestAssured.given;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.TEST_ACCOUNT;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.TEST_SUBJECT;
import static org.eclipse.che.api.devfile.server.TestObjectGenerator.USER_DEVFILE_ID;
import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION;
import static org.eclipse.che.api.workspace.server.devfile.Constants.SUPPORTED_VERSIONS;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
@ -25,38 +20,20 @@ import static org.everrest.assured.JettyHttpServer.ADMIN_USER_NAME;
import static org.everrest.assured.JettyHttpServer.ADMIN_USER_PASSWORD;
import static org.everrest.assured.JettyHttpServer.SECURE_PATH;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.restassured.response.Response;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.Page;
import org.eclipse.che.api.core.model.workspace.devfile.UserDevfile;
import org.eclipse.che.api.core.notification.EventService;
import org.eclipse.che.api.core.rest.ApiExceptionMapper;
import org.eclipse.che.api.core.rest.CheJsonProvider;
import org.eclipse.che.api.core.rest.ServiceContext;
import org.eclipse.che.api.core.rest.WebApplicationExceptionMapper;
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.devfile.server.model.impl.UserDevfileImpl;
import org.eclipse.che.api.devfile.server.spi.UserDevfileDao;
import org.eclipse.che.api.devfile.shared.dto.UserDevfileDto;
import org.eclipse.che.api.workspace.server.devfile.DevfileEntityProvider;
@ -68,16 +45,12 @@ import org.eclipse.che.api.workspace.server.devfile.validator.DevfileSchemaValid
import org.eclipse.che.api.workspace.shared.dto.devfile.DevfileDto;
import org.eclipse.che.api.workspace.shared.dto.devfile.MetadataDto;
import org.eclipse.che.commons.env.EnvironmentContext;
import org.eclipse.che.commons.lang.NameGenerator;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.dto.server.DtoFactory;
import org.everrest.assured.EverrestJetty;
import org.everrest.core.Filter;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.RequestFilter;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.mockito.testng.MockitoTestNGListener;
import org.testng.annotations.BeforeMethod;
@ -164,345 +137,12 @@ public class DevfileServiceTest {
@BeforeMethod
public void setup() {
this.userDevfileService = new DevfileService(schemaProvider, userDevfileManager, linksInjector);
this.userDevfileService = new DevfileService(schemaProvider);
lenient()
.when(linksInjector.injectLinks(any(UserDevfileDto.class), any(ServiceContext.class)))
.thenAnswer((Answer<UserDevfileDto>) invocation -> invocation.getArgument(0));
}
@Test(dataProvider = "validUserDevfiles")
public void shouldCreateUserDevfileFromJson(UserDevfileDto userDevfileDto) throws Exception {
final UserDevfileImpl userDevfileImpl =
new UserDevfileImpl("id-123123", TEST_ACCOUNT, userDevfileDto);
when(userDevfileManager.createDevfile(any(UserDevfile.class))).thenReturn(userDevfileImpl);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(DtoFactory.getInstance().toJson(userDevfileDto))
.when()
.post(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 201);
UserDevfileDto dto = unwrapDto(response, UserDevfileDto.class);
assertEquals(dto.getNamespace(), TEST_ACCOUNT.getName());
assertEquals(new UserDevfileImpl(dto, TEST_ACCOUNT), userDevfileImpl);
verify(userDevfileManager).createDevfile(any(UserDevfile.class));
}
@Test(dataProvider = "invalidUserDevfiles")
public void shouldFailToCreateInvalidUserDevfileFromJson(
UserDevfileDto userDevfileDto, String expectedErrorMessage) throws Exception {
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.body(DtoFactory.getInstance().toJson(userDevfileDto))
.when()
.post(SECURE_PATH + "/devfile");
assertEquals(response.getStatusCode(), 400);
ServiceError error = unwrapDto(response, ServiceError.class);
assertNotNull(error);
assertEquals(error.getMessage(), expectedErrorMessage);
verifyNoMoreInteractions(userDevfileManager);
}
@Test
public void shouldGetUserDevfileById() throws Exception {
final UserDevfileImpl userDevfile = TestObjectGenerator.createUserDevfile();
when(userDevfileManager.getById(eq("id-22323"))).thenReturn(userDevfile);
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/devfile/id-22323")
.then()
.extract()
.response();
assertEquals(response.getStatusCode(), 200);
assertEquals(
new UserDevfileImpl(unwrapDto(response, UserDevfileDto.class), TEST_ACCOUNT), userDevfile);
verify(userDevfileManager).getById(eq("id-22323"));
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldThrowNotFoundExceptionWhenUserDevfileIsNotExistOnGetById() throws Exception {
final String errMessage = format("UserDevfile with id %s is not found", USER_DEVFILE_ID);
doThrow(new NotFoundException(errMessage)).when(userDevfileManager).getById(anyString());
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.expect()
.statusCode(404)
.when()
.get(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
assertEquals(unwrapDto(response, ServiceError.class).getMessage(), errMessage);
}
@Test
public void shouldThrowNotFoundExceptionWhenUpdatingNonExistingUserDevfile() throws Exception {
// given
final UserDevfile userDevfile =
DtoConverter.asDto(TestObjectGenerator.createUserDevfile("devfile-name"));
doThrow(new NotFoundException(format("User devfile with id %s is not found.", USER_DEVFILE_ID)))
.when(userDevfileManager)
.updateUserDevfile(any(UserDevfile.class));
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(userDevfile))
.when()
.put(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
// then
assertEquals(response.getStatusCode(), 404);
assertEquals(
unwrapDto(response, ServiceError.class).getMessage(),
format("User devfile with id %s is not found.", USER_DEVFILE_ID));
}
@Test
public void shouldBeAbleToUpdateUserDevfile() throws Exception {
// given
final UserDevfileDto devfileDto = TestObjectGenerator.createUserDevfileDto();
final UserDevfileImpl userDevfileImpl = new UserDevfileImpl(devfileDto, TEST_ACCOUNT);
when(userDevfileManager.updateUserDevfile(any(UserDevfile.class))).thenReturn(userDevfileImpl);
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(devfileDto))
.when()
.put(SECURE_PATH + "/devfile/" + devfileDto.getId());
// then
assertEquals(response.getStatusCode(), 200);
assertEquals(
new UserDevfileImpl(unwrapDto(response, UserDevfileDto.class), TEST_ACCOUNT),
userDevfileImpl);
verify(userDevfileManager).updateUserDevfile(devfileDto);
verify(linksInjector).injectLinks(any(), any());
}
@Test(dataProvider = "invalidUserDevfiles")
public void shouldFailToUpdateWithInvalidUserDevfile(
UserDevfileDto userDevfileDto, String expectedErrorMessage) throws Exception {
// given
userDevfileDto = userDevfileDto.withId("id-123");
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(userDevfileDto))
.when()
.put(SECURE_PATH + "/devfile/" + userDevfileDto.getId());
// then
assertEquals(response.getStatusCode(), 400);
ServiceError error = unwrapDto(response, ServiceError.class);
assertNotNull(error);
assertEquals(error.getMessage(), expectedErrorMessage);
verifyNoMoreInteractions(userDevfileManager);
verifyNoMoreInteractions(linksInjector);
}
@Test
public void shouldOverrideIdOnUpdateUserDevfile() throws Exception {
// given
final UserDevfileDto devfileDto = TestObjectGenerator.createUserDevfileDto();
final UserDevfileImpl userDevfileImpl = new UserDevfileImpl(devfileDto, TEST_ACCOUNT);
final String newID = NameGenerator.generate("id", 24);
final UserDevfileImpl expectedUserDevfileImpl =
new UserDevfileImpl(newID, TEST_ACCOUNT, userDevfileImpl);
final UserDevfileDto expectedDto =
org.eclipse.che.api.devfile.server.DtoConverter.asDto(expectedUserDevfileImpl);
when(userDevfileManager.updateUserDevfile(any(UserDevfile.class)))
.thenReturn(expectedUserDevfileImpl);
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType(APPLICATION_JSON)
.body(DtoFactory.getInstance().toJson(devfileDto))
.when()
.put(SECURE_PATH + "/devfile/" + newID);
// then
assertEquals(response.getStatusCode(), 200);
assertEquals(
new UserDevfileImpl(unwrapDto(response, UserDevfileDto.class), TEST_ACCOUNT),
expectedUserDevfileImpl);
verify(userDevfileManager).updateUserDevfile(expectedDto);
verify(linksInjector).injectLinks(any(), any());
}
@Test
public void shouldRemoveUserDevfileByGivenIdentifier() throws Exception {
// given
// when
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.expect()
.statusCode(204)
.when()
.delete(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
// then
verify(userDevfileManager).removeUserDevfile(USER_DEVFILE_ID);
}
@Test
public void shouldNotThrowAnyExceptionWhenRemovingNonExistingUserDevfile() throws Exception {
// given
Mockito.doNothing().when(userDevfileManager).removeUserDevfile(anyString());
// when
Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.when()
.delete(SECURE_PATH + "/devfile/" + USER_DEVFILE_ID);
// then
assertEquals(response.getStatusCode(), 204);
}
@Test
public void shouldGetUserDevfilesAvailableToUser() throws Exception {
// given
final UserDevfileDto devfileDto = TestObjectGenerator.createUserDevfileDto();
final UserDevfileImpl userDevfileImpl = new UserDevfileImpl(devfileDto, TEST_ACCOUNT);
doReturn(new Page<>(ImmutableList.of(userDevfileImpl), 0, 1, 1))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.when()
.get(SECURE_PATH + "/devfile/search")
.then()
.extract()
.response();
// then
assertEquals(response.getStatusCode(), 200);
final List<UserDevfileDto> res = unwrapDtoList(response, UserDevfileDto.class);
assertEquals(res.size(), 1);
assertEquals(res.get(0).withLinks(emptyList()), devfileDto);
verify(userDevfileManager).getUserDevfiles(eq(30), eq(0), anyList(), anyList());
}
@Test
public void shouldBeAbleToSetLimitAndOffsetOnUserDevfileSearch() throws Exception {
// given
doReturn(new Page<>(Collections.emptyList(), 0, 1, 0))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.queryParam("maxItems", 5)
.queryParam("skipCount", 52)
.when()
.get(SECURE_PATH + "/devfile/search")
.then()
.extract()
.response();
// then
// then
assertEquals(response.getStatusCode(), 200);
verify(userDevfileManager).getUserDevfiles(eq(5), eq(52), anyList(), anyList());
}
@Test
public void shouldBeAbleToSetFiltertOnUserDevfileSearch() throws Exception {
// given
doReturn(new Page<>(Collections.emptyList(), 0, 1, 0))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
Map<String, String> parameters =
ImmutableMap.of("id", "sdfsdf5", "devfile.meta.name", "like:%dfdf");
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.queryParam("id", "sdfsdf5")
.queryParam("devfile.meta.name", "like:%dfdf")
.when()
.get(SECURE_PATH + "/devfile/search")
.then()
.extract()
.response();
// then
assertEquals(response.getStatusCode(), 200);
Class<List<Pair<String, String>>> listClass =
(Class<List<Pair<String, String>>>) (Class) ArrayList.class;
ArgumentCaptor<List<Pair<String, String>>> filterCaptor = ArgumentCaptor.forClass(listClass);
verify(userDevfileManager).getUserDevfiles(eq(30), eq(0), filterCaptor.capture(), anyList());
assertEquals(
filterCaptor.getValue(),
ImmutableList.of(new Pair("devfile.meta.name", "like:%dfdf"), new Pair("id", "sdfsdf5")));
}
@Test
public void shouldBeAbleToSetOrderOnUserDevfileSearch() throws Exception {
// given
doReturn(new Page<>(Collections.emptyList(), 0, 1, 0))
.when(userDevfileManager)
.getUserDevfiles(anyInt(), anyInt(), anyList(), anyList());
// when
final Response response =
given()
.auth()
.basic(ADMIN_USER_NAME, ADMIN_USER_PASSWORD)
.contentType("application/json")
.queryParam("order", "id:asc,name:desc")
.when()
.get(SECURE_PATH + "/devfile/search")
.then()
.extract()
.response();
// then
assertEquals(response.getStatusCode(), 200);
Class<List<Pair<String, String>>> listClass =
(Class<List<Pair<String, String>>>) (Class) ArrayList.class;
ArgumentCaptor<List<Pair<String, String>>> orderCaptor = ArgumentCaptor.forClass(listClass);
verify(userDevfileManager).getUserDevfiles(eq(30), eq(0), anyList(), orderCaptor.capture());
assertEquals(
orderCaptor.getValue(), ImmutableList.of(new Pair("id", "asc"), new Pair("name", "desc")));
}
@DataProvider
public Object[][] validUserDevfiles() {
return new Object[][] {