Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

response envelope #199

Merged
merged 31 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
461f917
response envelope
salamonpavel May 22, 2024
45adbfc
fix tests
salamonpavel May 22, 2024
38a9668
fix tests
salamonpavel May 22, 2024
34cfee0
fix tests
salamonpavel May 22, 2024
3f09ecf
fixes, minors
salamonpavel May 22, 2024
8fc40ba
typo
salamonpavel May 22, 2024
57cb850
typo
salamonpavel May 22, 2024
3790506
Merge branch 'master' into feature/197-response-envelope
salamonpavel May 22, 2024
64bf833
Merge remote-tracking branch 'origin/master' into feature/197-respons…
salamonpavel May 24, 2024
03a0191
merge conflicts resolved
salamonpavel May 24, 2024
9c307b8
reads to format for Single/MultiSuccessResponse
salamonpavel May 24, 2024
859834f
Merge branch 'master' into feature/197-response-envelope
salamonpavel May 27, 2024
7933f7e
requestId, ResponseEnvelope
salamonpavel May 27, 2024
976d3bf
envelopes only for v2 endpoints
salamonpavel May 27, 2024
37539d8
v2 base endpoint
salamonpavel May 27, 2024
017aa0f
v1 and v2
salamonpavel May 27, 2024
a77c65e
v1 and v2
salamonpavel May 27, 2024
b571214
cleanup
salamonpavel May 27, 2024
121e2b3
routes
salamonpavel May 27, 2024
d1bf834
requestId as String
salamonpavel May 27, 2024
39273a8
fix tests
salamonpavel May 27, 2024
da5fb84
matchType instance for uuid
salamonpavel May 27, 2024
dfc3a4f
fix tests
salamonpavel May 27, 2024
71d98d3
fix tests
salamonpavel May 27, 2024
5fd5f88
fix tests
salamonpavel May 27, 2024
1513849
renamed method to createOrUpdateAdditionalDataV2
salamonpavel May 28, 2024
fe7ac88
Merge branch 'master' into feature/197-response-envelope
salamonpavel May 28, 2024
9bc90b6
Merge branch 'refs/heads/master' into feature/197-response-envelope
salamonpavel Jun 12, 2024
b251a56
Merge branch 'refs/heads/master' into feature/197-response-envelope
salamonpavel Jun 13, 2024
0c1f2ab
resolve conflicts, add envelope
salamonpavel Jun 13, 2024
182556f
fix of tests
salamonpavel Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
package za.co.absa.atum.server.api.controller

import za.co.absa.atum.server.api.exception.ServiceError
import za.co.absa.atum.server.model.{ErrorResponse, GeneralErrorResponse, InternalServerErrorResponse}
import za.co.absa.atum.server.model.ErrorResponse.{ErrorResponse, GeneralErrorResponse, InternalServerErrorResponse}
import za.co.absa.atum.server.model.SuccessResponse.{MultiSuccessResponse, SingleSuccessResponse}
import za.co.absa.fadb.exceptions.StatusException
import zio._

Expand All @@ -40,4 +41,16 @@ trait BaseController {
}

}

protected def mapToSingleSuccessResponse[A](
effect: IO[ErrorResponse, A]
): IO[ErrorResponse, SingleSuccessResponse[A]] = {
effect.map(SingleSuccessResponse(_))
}

protected def mapToMultiSuccessResponse[A](
effect: IO[ErrorResponse, Seq[A]]
): IO[ErrorResponse, MultiSuccessResponse[A]] = {
effect.map(MultiSuccessResponse(_))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package za.co.absa.atum.server.api.controller

import za.co.absa.atum.model.dto.CheckpointDTO
import za.co.absa.atum.server.model.ErrorResponse
import za.co.absa.atum.server.model.ErrorResponse.ErrorResponse
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse
import zio.IO
import zio.macros.accessible

@accessible
trait CheckpointController {
def createCheckpoint(checkpointDTO: CheckpointDTO): IO[ErrorResponse, CheckpointDTO]
def createCheckpoint(checkpointDTO: CheckpointDTO): IO[ErrorResponse, SingleSuccessResponse[CheckpointDTO]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ package za.co.absa.atum.server.api.controller

import za.co.absa.atum.model.dto.CheckpointDTO
import za.co.absa.atum.server.api.service.CheckpointService
import za.co.absa.atum.server.model.ErrorResponse
import za.co.absa.atum.server.model.ErrorResponse.ErrorResponse
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse
import zio._

class CheckpointControllerImpl(checkpointService: CheckpointService)
extends CheckpointController with BaseController {
class CheckpointControllerImpl(checkpointService: CheckpointService) extends CheckpointController with BaseController {

override def createCheckpoint(checkpointDTO: CheckpointDTO): IO[ErrorResponse, CheckpointDTO] = {
serviceCallWithStatus[Unit, CheckpointDTO](
checkpointService.saveCheckpoint(checkpointDTO),
_ => checkpointDTO
override def createCheckpoint(
checkpointDTO: CheckpointDTO
): IO[ErrorResponse, SingleSuccessResponse[CheckpointDTO]] = {
mapToSingleSuccessResponse(
serviceCallWithStatus[Unit, CheckpointDTO](
checkpointService.saveCheckpoint(checkpointDTO),
_ => checkpointDTO
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@
package za.co.absa.atum.server.api.controller

import za.co.absa.atum.model.dto.{AdditionalDataSubmitDTO, AtumContextDTO, PartitioningSubmitDTO}
import za.co.absa.atum.server.model.ErrorResponse
import za.co.absa.atum.server.model.ErrorResponse.ErrorResponse
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse
import zio.IO
import zio.macros.accessible

@accessible
trait PartitioningController {
def createPartitioningIfNotExists(partitioningSubmitDTO: PartitioningSubmitDTO): IO[ErrorResponse, AtumContextDTO]
def createOrUpdateAdditionalData(additionalData: AdditionalDataSubmitDTO): IO[ErrorResponse, AdditionalDataSubmitDTO]
def createPartitioningIfNotExists(
partitioningSubmitDTO: PartitioningSubmitDTO
): IO[ErrorResponse, SingleSuccessResponse[AtumContextDTO]]

def createOrUpdateAdditionalData(
additionalData: AdditionalDataSubmitDTO
): IO[ErrorResponse, SingleSuccessResponse[AdditionalDataSubmitDTO]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,47 @@

package za.co.absa.atum.server.api.controller


import za.co.absa.atum.model.dto.{AdditionalDataSubmitDTO, AtumContextDTO, PartitioningSubmitDTO}
import za.co.absa.atum.server.api.exception.ServiceError
import za.co.absa.atum.server.api.service.PartitioningService
import za.co.absa.atum.server.model.{ErrorResponse, InternalServerErrorResponse}
import za.co.absa.atum.server.model.ErrorResponse.{ErrorResponse, InternalServerErrorResponse}
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse
import zio._

class PartitioningControllerImpl(partitioningService: PartitioningService)
extends PartitioningController with BaseController {
extends PartitioningController
with BaseController {

override def createPartitioningIfNotExists(
partitioningSubmitDTO: PartitioningSubmitDTO
): IO[ErrorResponse, AtumContextDTO] = {
for {
_ <- partitioningService.createPartitioningIfNotExists(partitioningSubmitDTO)
): IO[ErrorResponse, SingleSuccessResponse[AtumContextDTO]] = {
val atumContextDTOEffect = for {
_ <- partitioningService
.createPartitioningIfNotExists(partitioningSubmitDTO)
.mapError(serviceError => InternalServerErrorResponse(serviceError.message))
measures <- partitioningService.getPartitioningMeasures(partitioningSubmitDTO.partitioning)
.mapError {
serviceError: ServiceError => InternalServerErrorResponse(serviceError.message)
}
additionalData <- partitioningService.getPartitioningAdditionalData(partitioningSubmitDTO.partitioning)
.mapError {
serviceError: ServiceError => InternalServerErrorResponse(serviceError.message)
}
measures <- partitioningService
.getPartitioningMeasures(partitioningSubmitDTO.partitioning)
.mapError { serviceError: ServiceError =>
InternalServerErrorResponse(serviceError.message)
}
additionalData <- partitioningService
.getPartitioningAdditionalData(partitioningSubmitDTO.partitioning)
.mapError { serviceError: ServiceError =>
InternalServerErrorResponse(serviceError.message)
}
} yield AtumContextDTO(partitioningSubmitDTO.partitioning, measures.toSet, additionalData)

mapToSingleSuccessResponse(atumContextDTOEffect)
}

override def createOrUpdateAdditionalData(
lsulak marked this conversation as resolved.
Show resolved Hide resolved
additionalData: AdditionalDataSubmitDTO
): IO[ErrorResponse, AdditionalDataSubmitDTO] = {
serviceCallWithStatus[Unit, AdditionalDataSubmitDTO](
partitioningService.createOrUpdateAdditionalData(additionalData),
_ => additionalData
): IO[ErrorResponse, SingleSuccessResponse[AdditionalDataSubmitDTO]] = {
mapToSingleSuccessResponse(
serviceCallWithStatus[Unit, AdditionalDataSubmitDTO](
partitioningService.createOrUpdateAdditionalData(additionalData),
_ => additionalData
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import sttp.tapir.json.play.jsonBody
import sttp.tapir.ztapir._
import sttp.tapir.{EndpointOutput, PublicEndpoint}
import za.co.absa.atum.server.Constants.Endpoints.{Api, V1}
import za.co.absa.atum.server.model._
import za.co.absa.atum.server.model.ErrorResponse._

trait BaseEndpoints {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,43 @@
package za.co.absa.atum.server.api.http

import sttp.model.StatusCode
import sttp.tapir.{PublicEndpoint, endpoint}
import sttp.tapir.generic.auto.schemaForCaseClass
import sttp.tapir.json.play.jsonBody
import sttp.tapir.ztapir._
import za.co.absa.atum.model.dto.{AtumContextDTO, CheckpointDTO, PartitioningSubmitDTO, AdditionalDataSubmitDTO}
import sttp.tapir.{PublicEndpoint, endpoint}
import za.co.absa.atum.model.dto.{AdditionalDataSubmitDTO, AtumContextDTO, CheckpointDTO, PartitioningSubmitDTO}
import za.co.absa.atum.server.Constants.Endpoints._
import za.co.absa.atum.server.model.ErrorResponse
import za.co.absa.atum.server.model.ErrorResponse.ErrorResponse
import za.co.absa.atum.server.model.PlayJsonImplicits._
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse

trait Endpoints extends BaseEndpoints {

protected val createCheckpointEndpoint: PublicEndpoint[CheckpointDTO, ErrorResponse, CheckpointDTO, Any] = {
protected val createCheckpointEndpoint
: PublicEndpoint[CheckpointDTO, ErrorResponse, SingleSuccessResponse[CheckpointDTO], Any] = {
apiV1.post
.in(CreateCheckpoint)
.in(jsonBody[CheckpointDTO])
.out(statusCode(StatusCode.Created))
.out(jsonBody[CheckpointDTO])
.out(jsonBody[SingleSuccessResponse[CheckpointDTO]])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I don't know if we want to do this - for potential compatibility reasons

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's talk about it on the internal MS chat

}

protected val createPartitioningEndpoint
: PublicEndpoint[PartitioningSubmitDTO, ErrorResponse, AtumContextDTO, Any] = {
: PublicEndpoint[PartitioningSubmitDTO, ErrorResponse, SingleSuccessResponse[AtumContextDTO], Any] = {
apiV1.post
.in(CreatePartitioning)
.in(jsonBody[PartitioningSubmitDTO])
.out(statusCode(StatusCode.Ok))
.out(jsonBody[AtumContextDTO])
.out(jsonBody[SingleSuccessResponse[AtumContextDTO]])
}

protected val createOrUpdateAdditionalDataEndpoint
: PublicEndpoint[AdditionalDataSubmitDTO, ErrorResponse, AdditionalDataSubmitDTO, Any] = {
: PublicEndpoint[AdditionalDataSubmitDTO, ErrorResponse, SingleSuccessResponse[AdditionalDataSubmitDTO], Any] = {
apiV1.post
.in(CreateOrUpdateAdditionalData)
.in(jsonBody[AdditionalDataSubmitDTO])
.out(statusCode(StatusCode.Ok))
.out(jsonBody[AdditionalDataSubmitDTO])
.out(jsonBody[SingleSuccessResponse[AdditionalDataSubmitDTO]])
}

protected val zioMetricsEndpoint: PublicEndpoint[Unit, Unit, String, Any] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import sttp.tapir.server.interceptor.decodefailure.DefaultDecodeFailureHandler.r
import sttp.tapir.server.interceptor.metrics.MetricsRequestInterceptor
import sttp.tapir.server.model.ValuedEndpointOutput
import sttp.tapir.ztapir.{headers, statusCode}
import za.co.absa.atum.server.model.BadRequestResponse
import za.co.absa.atum.server.model.ErrorResponse.BadRequestResponse
import zio.interop.catz._

trait ServerOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,28 @@ package za.co.absa.atum.server.model

import play.api.libs.json.{Json, Reads, Writes}

sealed trait ErrorResponse {
def message: String
}

object ErrorResponse {

sealed trait ErrorResponse {
def message: String
}

implicit val reads: Reads[ErrorResponse] = Json.reads[ErrorResponse]
implicit val writes: Writes[ErrorResponse] = Json.writes[ErrorResponse]
}

final case class BadRequestResponse(message: String) extends ErrorResponse
final case class BadRequestResponse(message: String) extends ErrorResponse

object BadRequestResponse {
implicit val reads: Reads[BadRequestResponse] = Json.reads[BadRequestResponse]
implicit val writes: Writes[BadRequestResponse] = Json.writes[BadRequestResponse]
}
implicit val readsBadRequestResponse: Reads[BadRequestResponse] = Json.reads[BadRequestResponse]
implicit val writesBadRequestResponse: Writes[BadRequestResponse] = Json.writes[BadRequestResponse]

final case class GeneralErrorResponse(message: String) extends ErrorResponse
final case class GeneralErrorResponse(message: String) extends ErrorResponse

object GeneralErrorResponse {
implicit val reads: Reads[GeneralErrorResponse] = Json.reads[GeneralErrorResponse]
implicit val writes: Writes[GeneralErrorResponse] = Json.writes[GeneralErrorResponse]
}
implicit val readsGeneralErrorResponse: Reads[GeneralErrorResponse] = Json.reads[GeneralErrorResponse]
implicit val writesGeneralErrorResponse: Writes[GeneralErrorResponse] = Json.writes[GeneralErrorResponse]

final case class InternalServerErrorResponse(message: String) extends ErrorResponse

final case class InternalServerErrorResponse(message: String) extends ErrorResponse
implicit val readsInternalServerErrorResponse: Reads[InternalServerErrorResponse] = Json.reads[InternalServerErrorResponse]
implicit val writesInternalServerErrorResponse: Writes[InternalServerErrorResponse] = Json.writes[InternalServerErrorResponse]

object InternalServerErrorResponse {
implicit val reads: Reads[InternalServerErrorResponse] = Json.reads[InternalServerErrorResponse]
implicit val writes: Writes[InternalServerErrorResponse] = Json.writes[InternalServerErrorResponse]
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import play.api.libs.functional.syntax.toFunctionalBuilderOps
import play.api.libs.json._
import za.co.absa.atum.model.dto.MeasureResultDTO.{ResultValueType, TypedValue}
import za.co.absa.atum.model.dto._
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse

object PlayJsonImplicits {

Expand Down Expand Up @@ -93,4 +94,7 @@ object PlayJsonImplicits {
implicit val readsAtumContextDTO: Reads[AtumContextDTO] = Json.reads[AtumContextDTO]
implicit val writesAtumContextDTO: Writes[AtumContextDTO] = Json.writes[AtumContextDTO]

implicit def readsSingleSuccessResponse[T: Reads]: Reads[SingleSuccessResponse[T]] = Json.reads[SingleSuccessResponse[T]]
implicit def writesSingleSuccessResponse[T: Writes]: Writes[SingleSuccessResponse[T]] = Json.writes[SingleSuccessResponse[T]]
lsulak marked this conversation as resolved.
Show resolved Hide resolved

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2021 ABSA Group Limited
*
* 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 za.co.absa.atum.server.model

object SuccessResponse {
lsulak marked this conversation as resolved.
Show resolved Hide resolved

sealed trait SuccessResponse

case class SingleSuccessResponse[T](data: T) extends SuccessResponse
case class MultiSuccessResponse[T](data: Seq[T]) extends SuccessResponse

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,19 @@

package za.co.absa.atum.server.api.controller

import org.junit.runner.RunWith
import org.mockito.Mockito.{mock, when}
import za.co.absa.atum.server.api.TestData
import za.co.absa.atum.server.api.exception.ServiceError
import za.co.absa.atum.server.api.service.CheckpointService
import za.co.absa.atum.server.model.{GeneralErrorResponse, InternalServerErrorResponse}
import za.co.absa.atum.server.model.ErrorResponse.{GeneralErrorResponse, InternalServerErrorResponse}
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse
import za.co.absa.fadb.exceptions.ErrorInDataException
import za.co.absa.fadb.status.FunctionStatus
import zio.test.Assertion.failsWithA
import zio._
import zio.test._
import zio.test.junit.ZTestJUnitRunner

@RunWith(classOf[ZTestJUnitRunner])
class CheckpointControllerIntegrationTests extends ZIOSpecDefault with TestData {
object CheckpointControllerIntegrationTests extends ZIOSpecDefault with TestData {

private val checkpointServiceMock = mock(classOf[CheckpointService])

Expand All @@ -49,7 +47,7 @@ class CheckpointControllerIntegrationTests extends ZIOSpecDefault with TestData
test("Returns expected CheckpointDTO") {
for {
result <- CheckpointController.createCheckpoint(checkpointDTO1)
} yield assertTrue(result == checkpointDTO1)
} yield assertTrue(result == SingleSuccessResponse(checkpointDTO1))
},
test("Returns expected InternalServerErrorResponse") {
assertZIO(CheckpointController.createCheckpoint(checkpointDTO3).exit)(failsWithA[InternalServerErrorResponse])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import org.mockito.Mockito.{mock, when}
import za.co.absa.atum.server.api.TestData
import za.co.absa.atum.server.api.exception.ServiceError
import za.co.absa.atum.server.api.service.PartitioningService
import za.co.absa.atum.server.model.InternalServerErrorResponse
import zio.test.Assertion.{equalTo, failsWithA}
import za.co.absa.atum.server.model.ErrorResponse.InternalServerErrorResponse
import za.co.absa.atum.server.model.SuccessResponse.SingleSuccessResponse
import zio._
import zio.test.Assertion.{equalTo, failsWithA}
import zio.test._

object PartitioningControllerIntegrationTests extends ZIOSpecDefault with TestData {
Expand All @@ -47,7 +48,7 @@ object PartitioningControllerIntegrationTests extends ZIOSpecDefault with TestDa
test("Returns expected AtumContextDTO") {
for {
result <- PartitioningController.createPartitioningIfNotExists(partitioningSubmitDTO1)
} yield assertTrue (result == atumContextDTO1)
} yield assertTrue(result == SingleSuccessResponse(atumContextDTO1))
},
test("Returns expected InternalServerErrorResponse") {
assertZIO(PartitioningController.createPartitioningIfNotExists(partitioningSubmitDTO2).exit)(
Expand All @@ -57,7 +58,9 @@ object PartitioningControllerIntegrationTests extends ZIOSpecDefault with TestDa
),
suite("CreateOrUpdateAdditionalDataSuite")(
test("Returns expected AdditionalDataSubmitDTO") {
assertZIO(PartitioningController.createOrUpdateAdditionalData(additionalDataSubmitDTO1))(equalTo(additionalDataSubmitDTO1))
assertZIO(PartitioningController.createOrUpdateAdditionalData(additionalDataSubmitDTO1))(
equalTo(SingleSuccessResponse(additionalDataSubmitDTO1))
)
},
test("Returns expected InternalServerErrorResponse") {
assertZIO(PartitioningController.createOrUpdateAdditionalData(additionalDataSubmitDTO2).exit)(
Expand Down
Loading
Loading