feat: As a developer I want to delete projects
This commit is contained in:
parent
497ee99e92
commit
203b376ef1
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"info": {
|
||||
"_postman_id": "a20516f0-5f7f-4d15-9f26-ada358993ff8",
|
||||
"_postman_id": "1823096a-1cb2-438d-9872-73ec0ca94cfa",
|
||||
"name": "security-c4po-api",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
|
@ -10,9 +10,6 @@
|
|||
"item": [
|
||||
{
|
||||
"name": "getProjects",
|
||||
"protocolProfileBehavior": {
|
||||
"disableBodyPruning": true
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
|
@ -41,9 +38,38 @@
|
|||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "http://localhost:8443/projects",
|
||||
"protocol": "http",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"projects"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "saveProject",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"oauth2": [
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "",
|
||||
"raw": "{\n \"client\": \"Novatec\",\n \"title\": \"log4j pentest\",\n \"tester\" : \"Stipe\",\n \"createdBy\" : \"10e06d7a-8dd0-4ecd-8963-056b45079c4f\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
|
@ -65,30 +91,11 @@
|
|||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "saveProject",
|
||||
"name": "deleteProject",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"client\": \"Novatec\",\n \"title\": \"log4j Pentest\",\n \"tester\": \"Stipe\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "http://localhost:8443/projects",
|
||||
"protocol": "http",
|
||||
"host": [
|
||||
"localhost"
|
||||
],
|
||||
"port": "8443",
|
||||
"path": [
|
||||
"projects"
|
||||
]
|
||||
}
|
||||
"url": null
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
|
|
|
@ -55,10 +55,28 @@ include::{snippets}/saveProject/http-response.adoc[]
|
|||
|
||||
include::{snippets}/saveProject/response-fields.adoc[]
|
||||
|
||||
=== Delete project
|
||||
|
||||
To delete a project, call the DELETE request /projects/{projectId}
|
||||
|
||||
==== Request example
|
||||
|
||||
include::{snippets}/deleteProject/http-request.adoc[]
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/deleteProject/http-response.adoc[]
|
||||
|
||||
==== Response structure
|
||||
|
||||
include::{snippets}/deleteProject/response-fields.adoc[]
|
||||
|
||||
== Change History
|
||||
|
||||
|===
|
||||
|Date |Change
|
||||
|2022-02-01
|
||||
|Added DELETE endpoint to save Projects
|
||||
|2021-12-22
|
||||
|Added POST endpoint to save Projects
|
||||
|2021-02-14
|
||||
|
|
|
@ -28,6 +28,12 @@ fun Project.toProjectResponseBody(): ResponseBody {
|
|||
)
|
||||
}
|
||||
|
||||
fun Project.toProjectDeleteResponseBody(): ResponseBody {
|
||||
return mapOf(
|
||||
"id" to id
|
||||
)
|
||||
}
|
||||
|
||||
data class ProjectOverview(
|
||||
val projects: List<Project>
|
||||
)
|
||||
|
@ -44,6 +50,10 @@ data class ProjectRequestBody(
|
|||
val tester: String? = null
|
||||
)
|
||||
|
||||
data class ProjectDeleteRequestBody(
|
||||
val id: String
|
||||
)
|
||||
|
||||
fun ProjectRequestBody.toProject(): Project {
|
||||
return Project(
|
||||
id = UUID.randomUUID().toString(),
|
||||
|
@ -53,5 +63,7 @@ fun ProjectRequestBody.toProject(): Project {
|
|||
tester = this.tester,
|
||||
// ToDo: Should be changed to SUB from Token after adding AUTH Header
|
||||
createdBy = UUID.randomUUID().toString()
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ import java.util.*
|
|||
origins = [],
|
||||
allowCredentials = "false",
|
||||
allowedHeaders = ["*"],
|
||||
methods = [RequestMethod.GET, RequestMethod.POST]
|
||||
methods = [RequestMethod.GET, RequestMethod.DELETE, RequestMethod.POST]
|
||||
)
|
||||
|
||||
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
||||
class ProjectController(private val projectService: ProjectService) {
|
||||
|
||||
|
@ -41,4 +42,11 @@ class ProjectController(private val projectService: ProjectService) {
|
|||
ResponseEntity.accepted().body(it.toProjectResponseBody())
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
fun deleteProject(@PathVariable(value = "id") id: String): Mono<ResponseEntity<ResponseBody>> {
|
||||
return this.projectService.deleteProject(id).map{
|
||||
ResponseEntity.ok().body(it.toProjectDeleteResponseBody())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.securityc4po.api.project
|
||||
|
||||
import org.springframework.data.mongodb.repository.DeleteQuery
|
||||
import org.springframework.data.mongodb.repository.Query
|
||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
@ -10,4 +11,7 @@ interface ProjectRepository: ReactiveMongoRepository<ProjectEntity, String> {
|
|||
|
||||
@Query("{'data._id' : ?0}")
|
||||
fun findProjectById(id: String): Mono<ProjectEntity>
|
||||
|
||||
@DeleteQuery("{'data._id' : ?0}")
|
||||
fun deleteProjectById(id: String): Mono<Long>
|
||||
}
|
|
@ -6,6 +6,8 @@ import com.securityc4po.api.extensions.getLoggerFor
|
|||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.kotlin.core.publisher.switchIfEmpty
|
||||
|
||||
|
||||
@Service
|
||||
@SuppressFBWarnings(BC_BAD_CAST_TO_ABSTRACT_COLLECTION, MESSAGE_BAD_CAST_TO_ABSTRACT_COLLECTION)
|
||||
|
@ -33,4 +35,14 @@ class ProjectService(private val projectRepository: ProjectRepository) {
|
|||
logger.warn("Project could not be stored in Database. Thrown exception: ", it)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteProject(id: String): Mono<Project> {
|
||||
return projectRepository.findProjectById(id).switchIfEmpty{
|
||||
logger.info("Project with id $id not found. Deletion not possible.")
|
||||
Mono.empty()
|
||||
}.flatMap{ projectEntity: ProjectEntity ->
|
||||
val project = projectEntity.toProject()
|
||||
projectRepository.deleteProjectById(id).map{project}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ abstract class BaseContainerizedTest {
|
|||
|
||||
companion object {
|
||||
val mongoDbContainer = KGenericContainer(ImageFromDockerfile("c4poapibasecontainerizedtest").withDockerfileFromBuilder {
|
||||
it.from("mongo")
|
||||
it.from("mongo:4.4.6")
|
||||
it.env("MONGO_INITDB_ROOT_USERNAME", "root")
|
||||
it.env("MONGO_INITDB_ROOT_PASSWORD", "cjwkbencowepoc324pon2mop3mp4")
|
||||
it.env("MONGO_INITDB_DATABASE", "admin")
|
||||
|
|
|
@ -149,6 +149,46 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
|||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class DeleteProject {
|
||||
@Test
|
||||
fun deleteProject() {
|
||||
webTestClient.delete().uri("/projects/${project.id}")
|
||||
.header("Authorization", "Bearer $tokenAdmin")
|
||||
.exchange()
|
||||
.expectStatus().isOk
|
||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||
.expectBody()
|
||||
.json(Json.write(project.toProjectDeleteResponseBody()))
|
||||
.consumeWith(
|
||||
WebTestClientRestDocumentation.document(
|
||||
"{methodName}",
|
||||
Preprocessors.preprocessRequest(
|
||||
Preprocessors.prettyPrint(),
|
||||
Preprocessors.modifyUris().removePort(),
|
||||
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||
),
|
||||
Preprocessors.preprocessResponse(
|
||||
Preprocessors.prettyPrint()
|
||||
),
|
||||
PayloadDocumentation.relaxedResponseFields(
|
||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||
.description("The id of the deleted project")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val project = Project(
|
||||
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
|
||||
client = "E Corp",
|
||||
title = "Some Mock API (v1.0) Scanning",
|
||||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Novatester",
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
}
|
||||
|
||||
private fun persistBasicTestScenario() {
|
||||
// setup test data
|
||||
val projectOne = Project(
|
||||
|
|
|
@ -116,6 +116,36 @@ class ProjectControllerIntTest : BaseIntTest() {
|
|||
)
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class DeleteProject {
|
||||
@Test
|
||||
fun `deleted project successfully`() {
|
||||
webTestClient.delete().uri("/projects/{id}", projectTwo.id)
|
||||
.header("Authorization", "Bearer $tokenAdmin")
|
||||
.exchange()
|
||||
.expectStatus().isOk
|
||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||
.expectBody().json(Json.write(projectTwo.toProjectDeleteResponseBody()))
|
||||
}
|
||||
/*@Test
|
||||
fun `delete project by non-existing id`() {
|
||||
webTestClient.delete().uri("/projects/{id}", "98754a47-796b-4b3f-abf9-c46c668596c5")
|
||||
.header("Authorization", "Bearer $tokenAdmin")
|
||||
.exchange()
|
||||
.expectStatus().isNoContent
|
||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||
.expectBody().isEmpty
|
||||
}*/
|
||||
val projectTwo = Project(
|
||||
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
|
||||
client = "Allsafe",
|
||||
title = "CashMyData (iOS)",
|
||||
createdAt = "2021-01-10T18:05:00Z",
|
||||
tester = "Elliot",
|
||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||
)
|
||||
}
|
||||
|
||||
private fun persistBasicTestScenario() {
|
||||
// setup test data
|
||||
val projectOne = Project(
|
||||
|
|
|
@ -2,7 +2,7 @@ version: '3.1'
|
|||
|
||||
services:
|
||||
c4po-db:
|
||||
image: mongo:latest
|
||||
image: mongo:4.4.6
|
||||
container_name: c4po-db
|
||||
volumes:
|
||||
- ../volumes/mongodb/data:/data/db
|
||||
|
|
Loading…
Reference in New Issue