diff --git a/.gitignore b/.gitignore index 4dc05f920fe..74147dbea41 100644 --- a/.gitignore +++ b/.gitignore @@ -152,9 +152,6 @@ api/api-server-maintenance-mode-v1/logs/ api/api-server-maintenance-mode-v1/out/ api/api-server-maintenance-mode-v1/target/ api/api-server-maintenance-mode-v1/config/ -api/api-elastic-profile-v1/out/ -api/api-elastic-profile-v1/logs/ -api/api-elastic-profile-v1/target/ api/api-elastic-profile-v2/out/ api/api-elastic-profile-v2/logs/ api/api-elastic-profile-v2/target/ diff --git a/api/api-elastic-profile-v1/build.gradle b/api/api-elastic-profile-v1/build.gradle deleted file mode 100644 index 6ef146e0458..00000000000 --- a/api/api-elastic-profile-v1/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018 ThoughtWorks, Inc. - * - * 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. - */ - -apply plugin: 'jacoco' -apply plugin: 'groovy' - -dependencies { - compile project(':api:api-base') - - testCompile project(path: ':api:api-base', configuration: 'testOutput') - - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: project.versions.junit5 - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.versions.junit5 -} diff --git a/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/ElasticProfileControllerV1.java b/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/ElasticProfileControllerV1.java deleted file mode 100644 index d6ce5fc7546..00000000000 --- a/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/ElasticProfileControllerV1.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2019 ThoughtWorks, Inc. - * - * 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. - */ - -package com.thoughtworks.go.apiv1.elasticprofile; - -import com.thoughtworks.go.api.ApiController; -import com.thoughtworks.go.api.ApiVersion; -import com.thoughtworks.go.api.CrudController; -import com.thoughtworks.go.api.base.OutputWriter; -import com.thoughtworks.go.api.spring.ApiAuthenticationHelper; -import com.thoughtworks.go.api.util.GsonTransformer; -import com.thoughtworks.go.apiv1.elasticprofile.representers.ElasticProfileRepresenter; -import com.thoughtworks.go.apiv1.elasticprofile.representers.ElasticProfilesRepresenter; -import com.thoughtworks.go.config.PluginProfiles; -import com.thoughtworks.go.config.elastic.ElasticProfile; -import com.thoughtworks.go.config.exceptions.EntityType; -import com.thoughtworks.go.config.exceptions.HttpException; -import com.thoughtworks.go.server.service.ElasticProfileService; -import com.thoughtworks.go.server.service.EntityHashingService; -import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult; -import com.thoughtworks.go.spark.Routes; -import com.thoughtworks.go.spark.spring.SparkSpringController; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import spark.Request; -import spark.Response; - -import java.io.IOException; -import java.util.function.Consumer; - -import static com.thoughtworks.go.api.util.HaltApiResponses.*; -import static java.lang.String.format; -import static spark.Spark.*; - -@Component -public class ElasticProfileControllerV1 extends ApiController implements SparkSpringController, CrudController { - private static final String PROFILE_ID_PARAM = "profile_id"; - private final ElasticProfileService elasticProfileService; - private final ApiAuthenticationHelper apiAuthenticationHelper; - private final EntityHashingService entityHashingService; - - @Autowired - public ElasticProfileControllerV1(ElasticProfileService elasticProfileService, ApiAuthenticationHelper apiAuthenticationHelper, EntityHashingService entityHashingService) { - super(ApiVersion.v1); - this.elasticProfileService = elasticProfileService; - this.apiAuthenticationHelper = apiAuthenticationHelper; - this.entityHashingService = entityHashingService; - } - - @Override - public String controllerBasePath() { - return Routes.ElasticProfileAPI.BASE; - } - - @Override - public void setupRoutes() { - path(controllerBasePath(), () -> { - before("", mimeType, this::setContentType); - before("/*", mimeType, this::setContentType); - before("", mimeType, apiAuthenticationHelper::checkAdminUserOrGroupAdminUserAnd403); - before("/*", mimeType, apiAuthenticationHelper::checkAdminUserOrGroupAdminUserAnd403); - - get("", mimeType, this::index); - get(Routes.ElasticProfileAPI.ID, mimeType, this::show); - - post("", mimeType, this::create); - put(Routes.ElasticProfileAPI.ID, mimeType, this::update); - delete(Routes.ElasticProfileAPI.ID, mimeType, this::destroy); - - exception(HttpException.class, this::httpException); - }); - } - - public String index(Request request, Response response) throws IOException { - final PluginProfiles elasticProfiles = elasticProfileService.getPluginProfiles(); - return writerForTopLevelObject(request, response, - outputWriter -> ElasticProfilesRepresenter.toJSON(outputWriter, elasticProfiles)); - } - - public String show(Request request, Response response) throws IOException { - final ElasticProfile elasticProfile = fetchEntityFromConfig(request.params(PROFILE_ID_PARAM)); - - if (isGetOrHeadRequestFresh(request, elasticProfile)) { - return notModified(response); - } - - setEtagHeader(elasticProfile, response); - return writerForTopLevelObject(request, response, writer -> ElasticProfileRepresenter.toJSON(writer, elasticProfile)); - } - - public String create(Request request, Response response) { - final ElasticProfile elasticProfileToCreate = buildEntityFromRequestBody(request); - haltIfEntityWithSameIdExists(elasticProfileToCreate); - - final HttpLocalizedOperationResult operationResult = new HttpLocalizedOperationResult(); - elasticProfileService.create(currentUsername(), elasticProfileToCreate, operationResult); - - return handleCreateOrUpdateResponse(request, response, elasticProfileToCreate, operationResult); - } - - public String update(Request request, Response response) { - final String profileId = request.params(PROFILE_ID_PARAM); - final ElasticProfile existingElasticProfile = fetchEntityFromConfig(profileId); - final ElasticProfile newElasticProfile = buildEntityFromRequestBody(request); - - if (isRenameAttempt(profileId, newElasticProfile.getId())) { - throw haltBecauseRenameOfEntityIsNotSupported("elasticProfile"); - } - - if (isPutRequestStale(request, existingElasticProfile)) { - throw haltBecauseEtagDoesNotMatch("elasticProfile", existingElasticProfile.getId()); - } - - final HttpLocalizedOperationResult operationResult = new HttpLocalizedOperationResult(); - elasticProfileService.update(currentUsername(), etagFor(existingElasticProfile), newElasticProfile, operationResult); - - return handleCreateOrUpdateResponse(request, response, newElasticProfile, operationResult); - } - - public String destroy(Request request, Response response) throws IOException { - ElasticProfile elasticProfile = fetchEntityFromConfig(request.params(PROFILE_ID_PARAM)); - HttpLocalizedOperationResult result = new HttpLocalizedOperationResult(); - elasticProfileService.delete(currentUsername(), elasticProfile, result); - - return renderHTTPOperationResult(result, request, response); - } - - - - private boolean isRenameAttempt(String profileIdFromRequestParam, String profileIdFromRequestBody) { - return !StringUtils.equals(profileIdFromRequestBody, profileIdFromRequestParam); - } - - @Override - public String etagFor(ElasticProfile entityFromServer) { - return entityHashingService.md5ForEntity(entityFromServer); - } - - @Override - public EntityType getEntityType() { - return EntityType.ElasticProfile; - } - - @Override - public ElasticProfile doFetchEntityFromConfig(String profileID) { - return elasticProfileService.findProfile(profileID); - } - - @Override - public ElasticProfile buildEntityFromRequestBody(Request req) { - return ElasticProfileRepresenter.fromJSON(GsonTransformer.getInstance().jsonReaderFrom(req.body())); - } - - @Override - public Consumer jsonWriter(ElasticProfile elasticProfile) { - return writer -> ElasticProfileRepresenter.toJSON(writer, elasticProfile); - } - - private void haltIfEntityWithSameIdExists(ElasticProfile elasticProfile) { - if (elasticProfileService.findProfile(elasticProfile.getId()) == null) { - return; - } - elasticProfile.addError("id", format("Elastic profile ids should be unique. Elastic profile with id '%s' already exists.", elasticProfile.getId())); - throw haltBecauseEntityAlreadyExists(jsonWriter(elasticProfile), "elasticProfile", elasticProfile.getId()); - } -} diff --git a/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfileRepresenter.java b/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfileRepresenter.java deleted file mode 100644 index 528cbbf1a3f..00000000000 --- a/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfileRepresenter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2018 ThoughtWorks, Inc. - * - * 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. - */ - -package com.thoughtworks.go.apiv1.elasticprofile.representers; - -import com.thoughtworks.go.api.base.OutputWriter; -import com.thoughtworks.go.api.representers.ConfigurationPropertyRepresenter; -import com.thoughtworks.go.api.representers.ErrorGetter; -import com.thoughtworks.go.api.representers.JsonReader; -import com.thoughtworks.go.config.elastic.ElasticProfile; -import com.thoughtworks.go.spark.Routes; - -import java.util.Collections; -import java.util.Map; - -public class ElasticProfileRepresenter { - public static void toJSON(OutputWriter outputWriter, ElasticProfile elasticProfile) { - outputWriter - .addLinks(linksWriter -> linksWriter - .addLink("self", Routes.ElasticProfileAPI.id(elasticProfile.getId())) - .addAbsoluteLink("doc", Routes.ElasticProfileAPI.DOC) - .addLink("find", Routes.ElasticProfileAPI.find())) - .add("id", elasticProfile.getId()) - .add("plugin_id", elasticProfile.getPluginId()) - .addChildList("properties", listWriter -> - elasticProfile.forEach(property -> - listWriter.addChild(propertyWriter -> - ConfigurationPropertyRepresenter.toJSON(propertyWriter, property)))); - if (elasticProfile.hasErrors()) { - Map fieldMapping = Collections.singletonMap("pluginId", "plugin_id"); - outputWriter.addChild("errors", errorWriter -> new ErrorGetter(fieldMapping).toJSON(errorWriter, elasticProfile)); - } - } - - public static ElasticProfile fromJSON(JsonReader jsonReader) { - ElasticProfile elasticProfile = new ElasticProfile( - jsonReader.getString("id"), - jsonReader.getString("plugin_id")); - elasticProfile.addConfigurations(ConfigurationPropertyRepresenter.fromJSONArray(jsonReader, "properties")); - return elasticProfile; - } -} diff --git a/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfilesRepresenter.java b/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfilesRepresenter.java deleted file mode 100644 index 1f2e5247ae6..00000000000 --- a/api/api-elastic-profile-v1/src/main/java/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfilesRepresenter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2018 ThoughtWorks, Inc. - * - * 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. - */ - -package com.thoughtworks.go.apiv1.elasticprofile.representers; - -import com.thoughtworks.go.api.base.OutputWriter; -import com.thoughtworks.go.config.PluginProfiles; -import com.thoughtworks.go.config.elastic.ElasticProfile; -import com.thoughtworks.go.spark.Routes; - -public class ElasticProfilesRepresenter { - public static void toJSON(OutputWriter writer, PluginProfiles elasticProfiles) { - writer.addLinks( - outputLinkWriter -> outputLinkWriter - .addLink("self", Routes.ElasticProfileAPI.BASE) - .addAbsoluteLink("doc", Routes.ElasticProfileAPI.DOC) - .addLink("find", Routes.ElasticProfileAPI.find())) - .addChild("_embedded", - embeddedWriter -> embeddedWriter.addChildList("profiles", - elasticProfilesWriter -> elasticProfiles.forEach( - store -> elasticProfilesWriter.addChild( - elasticProfileWriter -> ElasticProfileRepresenter.toJSON(elasticProfileWriter, store))))); - } -} diff --git a/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/ElasticProfileControllerV1Test.groovy b/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/ElasticProfileControllerV1Test.groovy deleted file mode 100644 index 3590dff2190..00000000000 --- a/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/ElasticProfileControllerV1Test.groovy +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright 2018 ThoughtWorks, Inc. - * - * 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. - */ - -package com.thoughtworks.go.apiv1.elasticprofile - -import com.thoughtworks.go.api.SecurityTestTrait -import com.thoughtworks.go.api.spring.ApiAuthenticationHelper -import com.thoughtworks.go.apiv1.elasticprofile.representers.ElasticProfileRepresenter -import com.thoughtworks.go.apiv1.elasticprofile.representers.ElasticProfilesRepresenter -import com.thoughtworks.go.config.elastic.ElasticProfile -import com.thoughtworks.go.config.elastic.ElasticProfiles -import com.thoughtworks.go.i18n.LocalizedMessage -import com.thoughtworks.go.server.domain.Username -import com.thoughtworks.go.server.service.ElasticProfileService -import com.thoughtworks.go.server.service.EntityHashingService -import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult -import com.thoughtworks.go.server.service.result.LocalizedOperationResult -import com.thoughtworks.go.spark.ControllerTrait -import com.thoughtworks.go.spark.GroupAdminUserSecurity -import com.thoughtworks.go.spark.SecurityServiceTrait -import groovy.json.JsonBuilder -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.invocation.InvocationOnMock - -import static com.thoughtworks.go.api.util.HaltApiMessages.* -import static com.thoughtworks.go.domain.packagerepository.ConfigurationPropertyMother.create -import static org.mockito.ArgumentMatchers.any -import static org.mockito.ArgumentMatchers.eq -import static org.mockito.Mockito.doAnswer -import static org.mockito.Mockito.when -import static org.mockito.MockitoAnnotations.initMocks - -class ElasticProfileControllerV1Test implements SecurityServiceTrait, ControllerTrait { - @Mock - private ElasticProfileService elasticProfileService - - @Mock - private EntityHashingService entityHashingService - - @BeforeEach - void setup() { - initMocks(this) - } - - @Override - ElasticProfileControllerV1 createControllerInstance() { - return new ElasticProfileControllerV1(elasticProfileService, new ApiAuthenticationHelper(securityService, goConfigService), entityHashingService) - } - - @Nested - class Index { - - @Nested - class Security implements SecurityTestTrait, GroupAdminUserSecurity { - @Override - String getControllerMethodUnderTest() { - return 'index' - } - - @Override - void makeHttpCall() { - getWithApiHeader(controller.controllerPath()) - } - } - - @Nested - class AsGroupAdmin { - @BeforeEach - void setUp() { - enableSecurity() - loginAsGroupAdmin() - } - - @Test - void 'should list all elastic profiles'() { - def elasticProfiles = new ElasticProfiles( - new ElasticProfile("docker", "cd.go.docker", create("docker-uri", false, "unix:///var/run/docker")), - new ElasticProfile("ecs", "cd.go.ecs", create("ACCESS_KEY", true, "encrypted-key")), - new ElasticProfile("k8s", "cd.go.k8s", create("cluster-uri", false, "https://foo.bar")) - ) - - when(elasticProfileService.getPluginProfiles()).thenReturn(elasticProfiles) - - getWithApiHeader(controller.controllerPath()) - - assertThatResponse() - .isOk() - .hasContentType(controller.mimeType) - .hasBodyWithJsonObject(elasticProfiles, ElasticProfilesRepresenter) - } - } - } - - @Nested - class Show { - - @Nested - class Security implements SecurityTestTrait, GroupAdminUserSecurity { - @Override - String getControllerMethodUnderTest() { - return 'show' - } - - @Override - void makeHttpCall() { - getWithApiHeader(controller.controllerPath("/docker")) - } - } - - @Nested - class AsGroupAdmin { - @BeforeEach - void setUp() { - enableSecurity() - loginAsGroupAdmin() - } - - @Test - void 'should return elastic profile of specified id'() { - def dockerElasticProfile = new ElasticProfile("docker", "cd.go.docker", create("docker-uri", false, "unix:///var/run/docker")) - - when(entityHashingService.md5ForEntity(dockerElasticProfile)).thenReturn('md5') - when(elasticProfileService.findProfile('docker')).thenReturn(dockerElasticProfile) - - getWithApiHeader(controller.controllerPath('/docker')) - - assertThatResponse() - .isOk() - .hasEtag('"md5"') - .hasContentType(controller.mimeType) - .hasBodyWithJsonObject(dockerElasticProfile, ElasticProfileRepresenter) - } - - @Test - void 'should return 404 if elastic profile with id does not exist'() { - getWithApiHeader(controller.controllerPath('/docker')) - - assertThatResponse() - .isNotFound() - .hasJsonMessage(controller.entityType.notFoundMessage("docker")) - .hasContentType(controller.mimeType) - } - - @Test - void 'should return 304 if elastic profile is not modified'() { - def dockerElasticProfile = new ElasticProfile("docker", "cd.go.docker", create("docker-uri", false, "unix:///var/run/docker")) - - when(entityHashingService.md5ForEntity(dockerElasticProfile)).thenReturn('md5') - when(elasticProfileService.findProfile('docker')).thenReturn(dockerElasticProfile) - - getWithApiHeader(controller.controllerPath('/docker'), ['if-none-match': '"md5"']) - - assertThatResponse() - .isNotModified() - .hasContentType(controller.mimeType) - } - - @Test - void 'should return 200 with elastic profile if etag does not match'() { - def dockerElasticProfile = new ElasticProfile("docker", "cd.go.docker", create("docker-uri", false, "unix:///var/run/docker")) - - when(entityHashingService.md5ForEntity(dockerElasticProfile)).thenReturn('md5-new') - when(elasticProfileService.findProfile('docker')).thenReturn(dockerElasticProfile) - - getWithApiHeader(controller.controllerPath('/docker'), ['if-none-match': '"md5"']) - - assertThatResponse() - .isOk() - .hasEtag('"md5-new"') - .hasContentType(controller.mimeType) - .hasBodyWithJsonObject(dockerElasticProfile, ElasticProfileRepresenter) - } - } - } - - @Nested - class Create { - - @Nested - class Security implements SecurityTestTrait, GroupAdminUserSecurity { - - @Override - String getControllerMethodUnderTest() { - return "create" - } - - @Override - void makeHttpCall() { - postWithApiHeader(controller.controllerPath(), '{}') - } - } - - @Nested - class AsGroupAdmin { - @BeforeEach - void setUp() { - enableSecurity() - loginAsGroupAdmin() - } - - @Test - void 'should create elastic profile from given json payload'() { - def jsonPayload = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://foo" - ] - ]] - - when(entityHashingService.md5ForEntity(Mockito.any() as ElasticProfile)).thenReturn('some-md5') - - postWithApiHeader(controller.controllerPath(), jsonPayload) - - assertThatResponse() - .isOk() - .hasEtag('"some-md5"') - .hasContentType(controller.mimeType) - .hasBodyWithJsonObject(new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")), ElasticProfileRepresenter) - } - - @Test - void 'should not create elastic profile in case of validation error and return the profile with errors'() { - def jsonPayload = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://foo" - ] - ]] - - when(elasticProfileService.create(Mockito.any() as Username, Mockito.any() as ElasticProfile, Mockito.any() as LocalizedOperationResult)) - .then({ InvocationOnMock invocation -> - ElasticProfile elasticProfile = invocation.getArguments()[1] - elasticProfile.addError("plugin_id", "Plugin not installed.") - HttpLocalizedOperationResult result = invocation.getArguments().last() - result.unprocessableEntity("validation failed") - }) - - postWithApiHeader(controller.controllerPath(), jsonPayload) - - def expectedResponseBody = [ - message: "validation failed", - data : [ - id : "docker", - plugin_id : "cd.go.docker", - properties: [[key: "DockerURI", value: "http://foo"]], - errors : [plugin_id: ["Plugin not installed."]] - ] - ] - - assertThatResponse() - .isUnprocessableEntity() - .hasContentType(controller.mimeType) - .hasJsonBody(new JsonBuilder(expectedResponseBody).toString()) - - } - - @Test - void 'should not create elastic profile if one already exist with same id'() { - def existingElasticProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - def jsonPayload = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://foo" - ] - ]] - - when(entityHashingService.md5ForEntity(Mockito.any() as ElasticProfile)).thenReturn('some-md5') - when(elasticProfileService.findProfile("docker")).thenReturn(existingElasticProfile) - - postWithApiHeader(controller.controllerPath(), jsonPayload) - - - def expectedResponseBody = [ - message: "Failed to add elasticProfile 'docker'. Another elasticProfile with the same name already exists.", - data : [ - id : "docker", - plugin_id : "cd.go.docker", - properties: [[key: "DockerURI", value: "http://foo"]], - errors : [id: ["Elastic profile ids should be unique. Elastic profile with id 'docker' already exists."]] - ] - ] - - assertThatResponse() - .isUnprocessableEntity() - .hasContentType(controller.mimeType) - .hasJsonBody(new JsonBuilder(expectedResponseBody).toString()) - } - } - } - - @Nested - class Update { - - @Nested - class Security implements SecurityTestTrait, GroupAdminUserSecurity { - - @Override - String getControllerMethodUnderTest() { - return "update" - } - - @Override - void makeHttpCall() { - putWithApiHeader(controller.controllerPath("/docker"), '{}') - } - } - - @Nested - class AsGroupAdmin { - @BeforeEach - void setUp() { - enableSecurity() - loginAsGroupAdmin() - } - - @Test - void 'should update elastic profile if etag matches'() { - def existingProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - def updatedProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://new-uri")) - def jsonPayload = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://new-uri" - ] - ]] - - when(entityHashingService.md5ForEntity(existingProfile)).thenReturn('some-md5') - when(entityHashingService.md5ForEntity(updatedProfile)).thenReturn('new-md5') - when(elasticProfileService.findProfile("docker")).thenReturn(existingProfile) - - putWithApiHeader(controller.controllerPath("/docker"), ['if-match': 'some-md5'], jsonPayload) - - assertThatResponse() - .isOk() - .hasEtag('"new-md5"') - .hasContentType(controller.mimeType) - .hasBodyWithJsonObject(updatedProfile, ElasticProfileRepresenter) - } - - @Test - void 'should not update elastic profile if etag does not match'() { - def existingProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - def jsonPayload = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://new-uri" - ] - ]] - - when(entityHashingService.md5ForEntity(existingProfile)).thenReturn('some-md5') - when(elasticProfileService.findProfile("docker")).thenReturn(existingProfile) - - putWithApiHeader(controller.controllerPath("/docker"), ['if-match': 'wrong-md5'], jsonPayload) - - assertThatResponse() - .isPreconditionFailed() - .hasContentType(controller.mimeType) - .hasJsonMessage(etagDoesNotMatch("elasticProfile", "docker")) - } - - @Test - void 'should return 404 if the profile does not exists'() { - when(elasticProfileService.findProfile("docker")).thenReturn(null) - putWithApiHeader(controller.controllerPath("/docker"), ['if-match': 'wrong-md5'], [:]) - - assertThatResponse() - .isNotFound() - .hasContentType(controller.mimeType) - .hasJsonMessage(controller.entityType.notFoundMessage("docker")) - } - - @Test - void 'should return 422 if attempted rename'() { - def existingProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - def jsonPayload = [ - id : 'docker-new', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://new-uri" - ] - ]] - - when(entityHashingService.md5ForEntity(existingProfile)).thenReturn('some-md5') - when(elasticProfileService.findProfile("docker")).thenReturn(existingProfile) - - putWithApiHeader(controller.controllerPath("/docker"), ['if-match': 'some-md5'], jsonPayload) - - assertThatResponse() - .isUnprocessableEntity() - .hasContentType(controller.mimeType) - .hasJsonMessage(renameOfEntityIsNotSupportedMessage('elasticProfile')) - } - - @Test - void 'should return 422 for validation error'() { - def existingProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - def jsonPayload = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://foo" - ] - ]] - - when(entityHashingService.md5ForEntity(existingProfile)).thenReturn('some-md5') - when(elasticProfileService.findProfile("docker")).thenReturn(existingProfile) - - when(elasticProfileService.update(Mockito.any() as Username, Mockito.any() as String, Mockito.any() as ElasticProfile, Mockito.any() as LocalizedOperationResult)) - .then({ InvocationOnMock invocation -> - ElasticProfile elasticProfile = invocation.getArguments()[2] - elasticProfile.addError("plugin_id", "Plugin not installed.") - HttpLocalizedOperationResult result = invocation.getArguments().last() - result.unprocessableEntity("validation failed") - }) - - putWithApiHeader(controller.controllerPath("/docker"), ['if-match': 'some-md5'], jsonPayload) - - def expectedResponseBody = [ - message: "validation failed", - data : [ - id : "docker", - plugin_id : "cd.go.docker", - properties: [[key: "DockerURI", value: "http://foo"]], - errors : [plugin_id: ["Plugin not installed."]] - ] - ] - - assertThatResponse() - .isUnprocessableEntity() - .hasContentType(controller.mimeType) - .hasJsonBody(new JsonBuilder(expectedResponseBody).toString()) - } - } - } - - @Nested - class Destroy { - @Nested - class Security implements SecurityTestTrait, GroupAdminUserSecurity { - - @Override - String getControllerMethodUnderTest() { - return "destroy" - } - - @Override - void makeHttpCall() { - deleteWithApiHeader(controller.controllerPath("/docker")) - } - } - - @Nested - class AsGroupAdmin { - @Test - void 'should delete elastic profile with given id'() { - def elasticProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - - when(elasticProfileService.findProfile("docker")).thenReturn(elasticProfile) - when(elasticProfileService.delete(Mockito.any() as Username, Mockito.any() as ElasticProfile, Mockito.any() as HttpLocalizedOperationResult)).then({ InvocationOnMock invocation -> - HttpLocalizedOperationResult result = invocation.arguments.last() - result.setMessage(LocalizedMessage.resourceDeleteSuccessful('elastic profile', elasticProfile.getId())) - }) - - deleteWithApiHeader(controller.controllerPath('/docker')) - - assertThatResponse() - .isOk() - .hasContentType(controller.mimeType) - .hasJsonMessage(LocalizedMessage.resourceDeleteSuccessful('elastic profile', elasticProfile.getId())) - } - - @Test - void 'should return 404 if elastic profile with id does not exist'() { - when(elasticProfileService.findProfile("docker")).thenReturn(null) - - deleteWithApiHeader(controller.controllerPath('/docker')) - - assertThatResponse() - .isNotFound() - .hasContentType(controller.mimeType) - .hasJsonMessage(controller.entityType.notFoundMessage("docker")) - } - - @Test - void 'should return validation error on failure'() { - def elasticProfile = new ElasticProfile("docker", "cd.go.docker", create("DockerURI", false, "http://foo")) - - when(elasticProfileService.findProfile('docker')).thenReturn(elasticProfile) - doAnswer({ InvocationOnMock invocation -> - ((HttpLocalizedOperationResult) invocation.arguments.last()).unprocessableEntity("save failed") - }).when(elasticProfileService).delete(any() as Username, eq(elasticProfile), any() as LocalizedOperationResult) - - deleteWithApiHeader(controller.controllerPath('/docker')) - - assertThatResponse() - .isUnprocessableEntity() - .hasContentType(controller.mimeType) - .hasJsonMessage('save failed') - } - } - } -} diff --git a/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfileRepresenterTest.groovy b/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfileRepresenterTest.groovy deleted file mode 100644 index dcbc86b0e0a..00000000000 --- a/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfileRepresenterTest.groovy +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2018 ThoughtWorks, Inc. - * - * 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. - */ - -package com.thoughtworks.go.apiv1.elasticprofile.representers - -import com.thoughtworks.go.api.util.GsonTransformer -import com.thoughtworks.go.config.elastic.ElasticProfile -import com.thoughtworks.go.plugin.access.elastic.ElasticAgentMetadataStore -import com.thoughtworks.go.plugin.api.info.PluginDescriptor -import com.thoughtworks.go.plugin.domain.common.Metadata -import com.thoughtworks.go.plugin.domain.common.PluggableInstanceSettings -import com.thoughtworks.go.plugin.domain.common.PluginConfiguration -import com.thoughtworks.go.plugin.domain.elastic.ElasticAgentPluginInfo -import org.junit.jupiter.api.Test - -import static com.thoughtworks.go.CurrentGoCDVersion.apiDocsUrl -import static com.thoughtworks.go.api.base.JsonUtils.toObjectString -import static com.thoughtworks.go.domain.packagerepository.ConfigurationPropertyMother.create -import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson -import static org.assertj.core.api.Assertions.assertThat - -class ElasticProfileRepresenterTest { - - @Test - void shouldCreateObjectFromJson() { - def elasticProfile = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://foo" - ] - ] - ] - - def expectedObject = new ElasticProfile('docker', 'cd.go.docker', create('DockerURI', false, 'http://foo')) - - def jsonReader = GsonTransformer.instance.jsonReaderFrom(elasticProfile) - def object = ElasticProfileRepresenter.fromJSON(jsonReader) - - assertThat(object).isEqualTo(expectedObject) - } - - @Test - void shouldAddErrorsToJson() { - def elasticProfile = new ElasticProfile('docker', 'cd.go.docker', create('DockerURI', false, 'http://foo')) - elasticProfile.addError("pluginId", "Invalid Plugin Id") - - def expectedJson = [ - _links : [ - self: [href: 'http://test.host/go/api/elastic/profiles/docker'], - doc : [href: apiDocsUrl('#elastic-agent-profiles')], - find: [href: 'http://test.host/go/api/elastic/profiles/:profile_id'], - ], - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "DockerURI", - "value": "http://foo" - ] - ], - errors : [ - "plugin_id": ["Invalid Plugin Id"] - ] - ] - - def json = toObjectString({ ElasticProfileRepresenter.toJSON(it, elasticProfile) }) - - assertThatJson(json).isEqualTo(expectedJson) - } - - @Test - void shouldEncryptSecureValues() { - def elasticProfile = [ - id : 'docker', - plugin_id : 'cd.go.docker', - properties: [ - [ - "key" : "Password", - "value": "passw0rd1" - ] - ] - ] - def elasticAgentMetadataStore = ElasticAgentMetadataStore.instance() - PluggableInstanceSettings pluggableInstanceSettings = new PluggableInstanceSettings(Arrays.asList( - new PluginConfiguration("Password", new Metadata(true, true)))) - elasticAgentMetadataStore.setPluginInfo(new ElasticAgentPluginInfo(pluginDescriptor(), pluggableInstanceSettings, pluggableInstanceSettings, null, null, null)) - def jsonReader = GsonTransformer.instance.jsonReaderFrom(elasticProfile) - - def object = ElasticProfileRepresenter.fromJSON(jsonReader) - - assertThat(object.getProperty("Password").isSecure()).isTrue() - } - - private static PluginDescriptor pluginDescriptor() { - return new PluginDescriptor() { - @Override - String id() { - return "cd.go.docker" - } - - @Override - String version() { - return null - } - - @Override - PluginDescriptor.About about() { - return null - } - } - } -} diff --git a/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfilesRepresenterTest.groovy b/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfilesRepresenterTest.groovy deleted file mode 100644 index 77b06584a95..00000000000 --- a/api/api-elastic-profile-v1/src/test/groovy/com/thoughtworks/go/apiv1/elasticprofile/representers/ElasticProfilesRepresenterTest.groovy +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2018 ThoughtWorks, Inc. - * - * 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. - */ - -package com.thoughtworks.go.apiv1.elasticprofile.representers - -import com.thoughtworks.go.config.elastic.ElasticProfile -import com.thoughtworks.go.config.elastic.ElasticProfiles -import com.thoughtworks.go.security.GoCipher -import org.junit.jupiter.api.Test - -import static com.thoughtworks.go.CurrentGoCDVersion.apiDocsUrl -import static com.thoughtworks.go.api.base.JsonUtils.toObjectString -import static com.thoughtworks.go.domain.packagerepository.ConfigurationPropertyMother.create -import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson - -class ElasticProfilesRepresenterTest { - def expectedJson = [ - _links : [ - self: [href: 'http://test.host/go/api/elastic/profiles'], - doc : [href: apiDocsUrl('#elastic-agent-profiles')], - find: [href: 'http://test.host/go/api/elastic/profiles/:profile_id'], - ], - _embedded: [ - profiles: [ - [ - _links : [ - self: [href: 'http://test.host/go/api/elastic/profiles/docker'], - doc : [href: apiDocsUrl('#elastic-agent-profiles')], - find: [href: 'http://test.host/go/api/elastic/profiles/:profile_id'], - ], - id : 'docker', - plugin_id : 'cd.go.docker', - "properties": [ - [ - "key" : "docker-uri", - "value": "unix:///var/run/docker" - ] - ], - ], - [ - _links : [ - self: [href: 'http://test.host/go/api/elastic/profiles/ecs'], - doc : [href: apiDocsUrl('#elastic-agent-profiles')], - find: [href: 'http://test.host/go/api/elastic/profiles/:profile_id'], - ], - id : 'ecs', - plugin_id : 'cd.go.ecs', - "properties": [ - [ - "key" : "ACCESS_KEY", - "encrypted_value": new GoCipher().encrypt('encrypted-key') - ] - ], - ] - ] - ] - ] - - @Test - void 'should serialize elastic profiles to json'() { - def elasticProfiles = new ElasticProfiles( - new ElasticProfile("docker", "cd.go.docker", create("docker-uri", false, "unix:///var/run/docker")), - new ElasticProfile("ecs", "cd.go.ecs", create("ACCESS_KEY", true, "encrypted-key")) - ) - - def actualJson = toObjectString({ ElasticProfilesRepresenter.toJSON(it, elasticProfiles) }) - - assertThatJson(actualJson).isEqualTo(expectedJson) - } -} diff --git a/server/webapp/WEB-INF/applicationContext-global.xml b/server/webapp/WEB-INF/applicationContext-global.xml index fe268e1c196..bb88763f40e 100644 --- a/server/webapp/WEB-INF/applicationContext-global.xml +++ b/server/webapp/WEB-INF/applicationContext-global.xml @@ -48,7 +48,6 @@ - diff --git a/settings.gradle b/settings.gradle index 76fa115ae0b..1d05800eac7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -47,7 +47,6 @@ include ':api:api-data-sharing-reporting-v2' include ':api:api-data-sharing-settings-v1' include ':api:api-data-sharing-usage-data-v3' include ':api:api-elastic-profile-operation-v1' -include ':api:api-elastic-profile-v1' include ':api:api-elastic-profile-v2' include ':api:api-encryption-v1' include ':api:api-environments-v2'