Skip to content

Commit

Permalink
refactor: BatchEntityError.error as ProblemDetail
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasBousselin committed Jan 21, 2025
1 parent 487d0a9 commit 4ac2e4e
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.egm.stellio.search.entity.web.JsonLdNgsiLdEntity
import com.egm.stellio.search.entity.web.entityId
import com.egm.stellio.shared.model.APIException
import com.egm.stellio.shared.model.BadRequestDataException
import com.egm.stellio.shared.model.toAPIException
import com.egm.stellio.shared.util.Sub
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
Expand Down Expand Up @@ -90,7 +91,7 @@ class EntityOperationService(
entityService.createEntity(jsonLdNgsiLdEntity.second, jsonLdNgsiLdEntity.first, sub).map {
BatchEntitySuccess(jsonLdNgsiLdEntity.entityId())
}.mapLeft { apiException ->
BatchEntityError(jsonLdNgsiLdEntity.entityId(), arrayListOf(apiException.message))
BatchEntityError(jsonLdNgsiLdEntity.entityId(), apiException.toProblemDetail())
}.bind()
}
}.fold(
Expand All @@ -114,7 +115,7 @@ class EntityOperationService(
BatchEntitySuccess(id)
}
.mapLeft { apiException ->
BatchEntityError(id, arrayListOf(apiException.message))
BatchEntityError(id, apiException.toProblemDetail())
}.bind()
}
}.fold(
Expand Down Expand Up @@ -242,10 +243,10 @@ class EntityOperationService(
}.map {
BatchEntitySuccess(entity.entityId(), it)
}.mapLeft {
BatchEntityError(entity.entityId(), arrayListOf(it.message))
BatchEntityError(entity.entityId(), it.toProblemDetail())
}
}.fold(
onFailure = { BatchEntityError(entity.entityId(), arrayListOf(it.message!!)).left() },
onFailure = { BatchEntityError(entity.entityId(), it.toAPIException().toProblemDetail()).left() },
onSuccess = { it }
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.egm.stellio.shared.model.NgsiLdEntity
import com.egm.stellio.shared.util.toUri
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonValue
import org.springframework.http.ProblemDetail
import java.net.URI

data class BatchOperationResult(
Expand All @@ -25,7 +26,7 @@ data class BatchOperationResult(
@JsonIgnore
fun addEntitiesToErrors(entities: List<Pair<String, APIException>>) =
entities.forEach {
errors.add(BatchEntityError(it.first.toUri(), arrayListOf(it.second.message)))
errors.add(BatchEntityError(it.first.toUri(), it.second.toProblemDetail()))
}
}

Expand All @@ -41,7 +42,7 @@ data class BatchEntitySuccess(
*/
data class BatchEntityError(
val entityId: URI,
val error: MutableList<String>,
val error: ProblemDetail,
val registrationId: URI? = null
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class EntityHandler(
{ (exception, csr) ->
BatchEntityError(
expandedEntity.id.toUri(),
mutableListOf(exception.type.toString(), exception.message, exception.detail),
exception.toProblemDetail(),
csr?.id
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.egm.stellio.search.entity.web.BatchEntityError
import com.egm.stellio.search.entity.web.BatchEntitySuccess
import com.egm.stellio.search.entity.web.BatchOperationResult
import com.egm.stellio.search.entity.web.JsonLdNgsiLdEntity
import com.egm.stellio.shared.model.AccessDeniedException
import com.egm.stellio.shared.model.BadRequestDataException
import com.egm.stellio.shared.model.ExpandedEntity
import com.egm.stellio.shared.model.InternalErrorException
Expand Down Expand Up @@ -126,12 +127,14 @@ class EntityOperationServiceTests {

@Test
fun `processEntities should count as error a process which raises a BadRequestDataException`() = runTest {
val error = BadRequestDataException("error")

coEvery {
entityService.appendAttributes(firstEntityURI, any(), any(), any())
} returns EMPTY_UPDATE_RESULT.right()
coEvery {
entityService.appendAttributes(secondEntityURI, any(), any(), any())
} returns BadRequestDataException("error").left()
} returns error.left()

val batchOperationResult =
entityOperationService.processEntities(
Expand All @@ -149,7 +152,7 @@ class EntityOperationServiceTests {
batchOperationResult.success
)
assertEquals(
listOf(BatchEntityError(secondEntityURI, arrayListOf("error"))),
listOf(BatchEntityError(secondEntityURI, error.toProblemDetail())),
batchOperationResult.errors
)
}
Expand Down Expand Up @@ -188,7 +191,7 @@ class EntityOperationServiceTests {
listOf(
BatchEntityError(
secondEntityURI,
arrayListOf("attribute#1 : reason, attribute#2 : reason")
BadRequestDataException("attribute#1 : reason, attribute#2 : reason").toProblemDetail()
)
),
batchOperationResult.errors
Expand Down Expand Up @@ -223,10 +226,11 @@ class EntityOperationServiceTests {

@Test
fun `batch create should ask to create entities and transmit back any error`() = runTest {
val badRequestException = BadRequestDataException("Invalid entity")
coEvery { entityService.createEntity(firstEntity, any(), any()) } returns Unit.right()
coEvery {
entityService.createEntity(secondEntity, any(), any())
} returns BadRequestDataException("Invalid entity").left()
} returns badRequestException.left()

val batchOperationResult = entityOperationService.create(
listOf(
Expand All @@ -239,7 +243,7 @@ class EntityOperationServiceTests {
assertEquals(arrayListOf(BatchEntitySuccess(firstEntityURI)), batchOperationResult.success)
assertEquals(
arrayListOf(
BatchEntityError(secondEntityURI, arrayListOf("Invalid entity"))
BatchEntityError(secondEntityURI, badRequestException.toProblemDetail())
),
batchOperationResult.errors
)
Expand Down Expand Up @@ -329,10 +333,11 @@ class EntityOperationServiceTests {
@Test
fun `batch delete should return deleted entity ids and in errors when deletion is partially successful`() =
runTest {
val internalError = InternalErrorException("Something went wrong during deletion")
coEvery { entityService.deleteEntity(firstEntityURI, sub) } returns Unit.right()
coEvery {
entityService.deleteEntity(secondEntityURI, sub)
} returns InternalErrorException("Something went wrong during deletion").left()
} returns internalError.left()

val batchOperationResult = entityOperationService.delete(
listOf(
Expand All @@ -350,7 +355,7 @@ class EntityOperationServiceTests {
listOf(
BatchEntityError(
secondEntityURI,
mutableListOf("Something went wrong during deletion")
internalError.toProblemDetail()
)
),
batchOperationResult.errors
Expand All @@ -359,11 +364,11 @@ class EntityOperationServiceTests {

@Test
fun `batch delete should return error messages when deletion in DB has failed`() = runTest {
val deleteEntityErrorMessage = "Something went wrong with deletion request"
val deleteEntityError = InternalErrorException("Something went wrong with deletion request")

coEvery {
entityService.deleteEntity(any(), any())
} returns InternalErrorException(deleteEntityErrorMessage).left()
} returns deleteEntityError.left()

val batchOperationResult = entityOperationService.delete(
listOf(
Expand All @@ -378,11 +383,11 @@ class EntityOperationServiceTests {
listOf(
BatchEntityError(
firstEntityURI,
mutableListOf(deleteEntityErrorMessage)
deleteEntityError.toProblemDetail()
),
BatchEntityError(
secondEntityURI,
mutableListOf(deleteEntityErrorMessage)
deleteEntityError.toProblemDetail()
)
),
batchOperationResult.errors
Expand Down Expand Up @@ -548,11 +553,21 @@ class EntityOperationServiceTests {

coEvery { entityOperationService.create(any(), any()) } returns BatchOperationResult(
emptyList<BatchEntitySuccess>().toMutableList(),
arrayListOf(BatchEntityError(firstEntity.id, mutableListOf(ENTITIY_CREATION_FORBIDDEN_MESSAGE)))
arrayListOf(
BatchEntityError(
firstEntity.id,
AccessDeniedException(ENTITIY_CREATION_FORBIDDEN_MESSAGE).toProblemDetail()
)
)
)
coEvery { entityOperationService.replace(any(), any()) } returns BatchOperationResult(
emptyList<BatchEntitySuccess>().toMutableList(),
arrayListOf(BatchEntityError(secondEntity.id, mutableListOf(ENTITY_ADMIN_FORBIDDEN_MESSAGE)))
arrayListOf(
BatchEntityError(
secondEntity.id,
AccessDeniedException(ENTITY_ADMIN_FORBIDDEN_MESSAGE).toProblemDetail()
)
)
)

val (batchOperationResult, createdIds) = entityOperationService.upsert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.egm.stellio.shared.model.AccessDeniedException
import com.egm.stellio.shared.model.AlreadyExistsException
import com.egm.stellio.shared.model.BadRequestDataException
import com.egm.stellio.shared.model.CompactedEntity
import com.egm.stellio.shared.model.ConflictException
import com.egm.stellio.shared.model.DEFAULT_DETAIL
import com.egm.stellio.shared.model.ExpandedEntity
import com.egm.stellio.shared.model.InternalErrorException
Expand Down Expand Up @@ -361,6 +362,85 @@ class EntityHandlerTests {
)
}

@Test
fun `create entity should return a 207 if some contextSources failed`() {
val jsonLdFile = ClassPathResource("/ngsild/aquac/breedingService.jsonld")
val capturedExpandedEntity = slot<ExpandedEntity>()
val conflictstatus = ConflictException("error") to gimmeRawCSR()

coEvery {
distributedEntityProvisionService
.distributeCreateEntity(any(), capture(capturedExpandedEntity), any())
} answers {
listOf(Unit.right(), conflictstatus.left()) to capturedExpandedEntity.captured
} andThenAnswer {
listOf(conflictstatus.left()) to capturedExpandedEntity.captured
}

coEvery {
entityService.createEntity(any(), any(), sub.getOrNull())
} returns AccessDeniedException("User forbidden to create entities").left()

val expectedJson = """
{
"success": [],
"errors":[
{
"entityId":"urn:ngsi-ld:BreedingService:0214",
"error":[
"https://uri.etsi.org/ngsi-ld/errors/Conflict",
"error",
"$DEFAULT_DETAIL"
],
"registrationId":"urn:ngsi-ld:ContextSourceRegistration:test"
},
{
"entityId":"urn:ngsi-ld:BreedingService:0214",
"error":[
"https://uri.etsi.org/ngsi-ld/errors/AccessDenied",
"User forbidden to create entities",
"$DEFAULT_DETAIL"
],
"registrationId":null
}
]
}
""".trimIndent()

webClient.post()
.uri("/ngsi-ld/v1/entities")
.bodyValue(jsonLdFile)
.exchange()
.expectStatus().isEqualTo(207)
.expectBody().json(expectedJson)
webClient.post()
.uri("/ngsi-ld/v1/entities")
.bodyValue(jsonLdFile)
.exchange()
.expectStatus().isEqualTo(207)
.expectBody().json(expectedJson)
}

@Test
fun `create entity should return a 409 if a context source containing all the information return a conflict`() {
val jsonLdFile = ClassPathResource("/ngsild/aquac/breedingService.jsonld")

webClient.post()
.uri("/ngsi-ld/v1/entities?local=true")
.bodyValue(jsonLdFile)
.exchange()
.expectStatus().isEqualTo(501)
.expectBody().json(
"""
{
"type": "https://uri.etsi.org/ngsi-ld/errors/NotImplemented",
"title": "The ['local'] parameters have not been implemented yet. This endpoint does not accept any query parameters. ",
"detail": "$DEFAULT_DETAIL"
}
""".trimIndent()
)
}

fun initializeRetrieveEntityMocks() {
val compactedEntity = slot<CompactedEntity>()

Expand Down
Loading

0 comments on commit 4ac2e4e

Please sign in to comment.