From 8e0ab131858b43adf5276ceb50b212959cd3f8cf Mon Sep 17 00:00:00 2001 From: Pawel Batko Date: Tue, 19 Oct 2021 18:32:29 +0200 Subject: [PATCH] [DPP-628] Self service error codes in time service CHANGELOG_BEGIN CHANGELOG_END --- .../api/validation/CommandsValidator.scala | 2 +- .../CompletionServiceRequestValidator.scala | 2 +- .../validation/LedgerOffsetValidator.scala | 2 +- .../api/validation/PartyValidator.scala | 2 +- .../TransactionFilterValidator.scala | 2 +- .../TransactionServiceRequestValidator.scala | 2 +- .../api/validation/ValueValidator.scala | 2 +- .../api/validation/ErrorFactories.scala | 6 +- .../api/validation/FieldValidations.scala | 2 +- .../api/validation/ErrorFactoriesSpec.scala | 6 +- .../platform/apiserver/ApiServices.scala | 5 +- .../apiserver/services/ApiTimeService.scala | 71 +++++++++++-------- 12 files changed, 63 insertions(+), 41 deletions(-) diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CommandsValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CommandsValidator.scala index 25ab82124a13..33b97504a0c4 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CommandsValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CommandsValidator.scala @@ -19,7 +19,7 @@ import com.daml.ledger.api.validation.CommandsValidator.{Submitters, effectiveSu import com.daml.lf.command._ import com.daml.lf.data._ import com.daml.lf.value.{Value => Lf} -import com.daml.platform.server.api.validation.ErrorFactories._ +import com.daml.platform.server.api.validation.ErrorFactories.Default._ import com.daml.platform.server.api.validation.FieldValidations.{requirePresence, _} import io.grpc.StatusRuntimeException import scalaz.syntax.tag._ diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CompletionServiceRequestValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CompletionServiceRequestValidator.scala index 14cb31e1034a..522d83a9b577 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CompletionServiceRequestValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/CompletionServiceRequestValidator.scala @@ -14,7 +14,7 @@ import com.daml.ledger.api.v1.command_completion_service.{ } import com.daml.platform.server.api.validation.FieldValidations._ import io.grpc.StatusRuntimeException -import com.daml.platform.server.api.validation.ErrorFactories._ +import com.daml.platform.server.api.validation.ErrorFactories.Default._ class CompletionServiceRequestValidator(ledgerId: LedgerId, partyNameChecker: PartyNameChecker) { diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/LedgerOffsetValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/LedgerOffsetValidator.scala index d4df17f843e3..ec97710df80b 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/LedgerOffsetValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/LedgerOffsetValidator.scala @@ -7,7 +7,7 @@ import com.daml.error.ContextualizedErrorLogger import com.daml.ledger.api.domain import com.daml.ledger.api.v1.ledger_offset.LedgerOffset import com.daml.ledger.api.v1.ledger_offset.LedgerOffset.LedgerBoundary -import com.daml.platform.server.api.validation.ErrorFactories.{ +import com.daml.platform.server.api.validation.ErrorFactories.Default.{ invalidArgument, missingField, offsetAfterLedgerEnd, diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/PartyValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/PartyValidator.scala index 9fde087f974e..7ccbc5bfeabb 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/PartyValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/PartyValidator.scala @@ -5,7 +5,7 @@ package com.daml.ledger.api.validation import com.daml.error.ContextualizedErrorLogger import com.daml.lf.data.Ref.Party -import com.daml.platform.server.api.validation.ErrorFactories.invalidArgument +import com.daml.platform.server.api.validation.ErrorFactories.Default.invalidArgument import com.daml.platform.server.api.validation.FieldValidations.requireParties import io.grpc.StatusRuntimeException diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionFilterValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionFilterValidator.scala index 840dcc1557e3..61d841a7f270 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionFilterValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionFilterValidator.scala @@ -22,7 +22,7 @@ object TransactionFilterValidator { contextualizedErrorLogger: ContextualizedErrorLogger ): Either[StatusRuntimeException, domain.TransactionFilter] = { if (txFilter.filtersByParty.isEmpty) { - Left(ErrorFactories.invalidArgument(None)("filtersByParty cannot be empty")) + Left(ErrorFactories.Default.invalidArgument(None)("filtersByParty cannot be empty")) } else { val convertedFilters = txFilter.filtersByParty.toList.traverse { case (k, v) => diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionServiceRequestValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionServiceRequestValidator.scala index f45024929f0c..91d8d3fa09a8 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionServiceRequestValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/TransactionServiceRequestValidator.scala @@ -16,7 +16,7 @@ import com.daml.ledger.api.v1.transaction_service.{ GetTransactionByIdRequest, GetTransactionsRequest, } -import com.daml.platform.server.api.validation.ErrorFactories._ +import com.daml.platform.server.api.validation.ErrorFactories.Default._ import com.daml.platform.server.api.validation.FieldValidations._ import io.grpc.StatusRuntimeException diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/ValueValidator.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/ValueValidator.scala index 244444115836..4661da0b77af 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/ValueValidator.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/ledger/api/validation/ValueValidator.scala @@ -10,7 +10,7 @@ import com.daml.ledger.api.domain import com.daml.ledger.api.v1.value.Value.Sum import com.daml.ledger.api.v1.{value => api} import com.daml.lf.value.{Value => Lf} -import com.daml.platform.server.api.validation.ErrorFactories._ +import com.daml.platform.server.api.validation.ErrorFactories.Default._ import com.daml.platform.server.api.validation.FieldValidations.{requirePresence, _} import io.grpc.StatusRuntimeException diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/ErrorFactories.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/ErrorFactories.scala index 6d7ea01906b6..fd50bf722b1e 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/ErrorFactories.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/ErrorFactories.scala @@ -277,7 +277,10 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch * TODO error codes: Remove default implementation once all Ledger API services * output versioned error codes. */ -object ErrorFactories extends ErrorFactories(new ErrorCodesVersionSwitcher(false)) { +object ErrorFactories { + + val Default: ErrorFactories = apply(new ErrorCodesVersionSwitcher(false)) + def apply(errorCodesVersionSwitcher: ErrorCodesVersionSwitcher): ErrorFactories = new ErrorFactories(errorCodesVersionSwitcher) @@ -298,4 +301,5 @@ object ErrorFactories extends ErrorFactories(new ErrorCodesVersionSwitcher(false statusBuilder.addDetails(definiteAnswers(definiteAnswer)) } } + } diff --git a/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/FieldValidations.scala b/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/FieldValidations.scala index cacef76ef83b..4258f50048de 100644 --- a/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/FieldValidations.scala +++ b/ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/server/api/validation/FieldValidations.scala @@ -195,7 +195,7 @@ class FieldValidations private (errorFactories: ErrorFactories) { /** Default implementation exposing field validations with the legacy error factories. * TODO error codes: Remove default implementation once all consumers output versioned error codes. */ -object FieldValidations extends FieldValidations(ErrorFactories) { +object FieldValidations extends FieldValidations(ErrorFactories.Default) { def apply(errorFactories: ErrorFactories): FieldValidations = new FieldValidations(errorFactories) } diff --git a/ledger/ledger-api-common/src/test/suite/scala/com/digitalasset/platform/server/api/validation/ErrorFactoriesSpec.scala b/ledger/ledger-api-common/src/test/suite/scala/com/digitalasset/platform/server/api/validation/ErrorFactoriesSpec.scala index 2ba482d91820..7d6c85f5431b 100644 --- a/ledger/ledger-api-common/src/test/suite/scala/com/digitalasset/platform/server/api/validation/ErrorFactoriesSpec.scala +++ b/ledger/ledger-api-common/src/test/suite/scala/com/digitalasset/platform/server/api/validation/ErrorFactoriesSpec.scala @@ -82,7 +82,7 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope ) forEvery(testCases) { (definiteAnswer, expectedDetails) => - val exception = aborted("my message", definiteAnswer) + val exception = ErrorFactories.Default.aborted("my message", definiteAnswer) val status = StatusProto.fromThrowable(exception) status.getCode shouldBe Code.ABORTED.value() status.getMessage shouldBe "my message" @@ -142,7 +142,7 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope } "fail on creating a ledgerIdMismatch error due to a wrong definite answer" in { - an[IllegalArgumentException] should be thrownBy ledgerIdMismatch( + an[IllegalArgumentException] should be thrownBy ErrorFactories.Default.ledgerIdMismatch( LedgerId("expected"), LedgerId("received"), definiteAnswer = Some(true), @@ -238,7 +238,7 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope "should create an ApiException without the stack trace" in { val status = Status.newBuilder().setCode(Code.INTERNAL.value()).build() - val exception = grpcError(status) + val exception = ErrorFactories.Default.grpcError(status) exception.getStackTrace shouldBe Array.empty } } diff --git a/ledger/participant-integration-api/src/main/scala/platform/apiserver/ApiServices.scala b/ledger/participant-integration-api/src/main/scala/platform/apiserver/ApiServices.scala index d3ccb7ce8593..847836c4eb9a 100644 --- a/ledger/participant-integration-api/src/main/scala/platform/apiserver/ApiServices.scala +++ b/ledger/participant-integration-api/src/main/scala/platform/apiserver/ApiServices.scala @@ -169,7 +169,10 @@ private[daml] object ApiServices { val apiTimeServiceOpt = optTimeServiceBackend.map(tsb => - new TimeServiceAuthorization(ApiTimeService.create(ledgerId, tsb), authorizer) + new TimeServiceAuthorization( + ApiTimeService.create(ledgerId, tsb, errorsVersionsSwitcher), + authorizer, + ) ) val writeServiceBackedApiServices = intitializeWriteServiceBackedApiServices( diff --git a/ledger/participant-integration-api/src/main/scala/platform/apiserver/services/ApiTimeService.scala b/ledger/participant-integration-api/src/main/scala/platform/apiserver/services/ApiTimeService.scala index e176ff2ad766..76fd359bb074 100644 --- a/ledger/participant-integration-api/src/main/scala/platform/apiserver/services/ApiTimeService.scala +++ b/ledger/participant-integration-api/src/main/scala/platform/apiserver/services/ApiTimeService.scala @@ -7,7 +7,11 @@ import akka.NotUsed import akka.stream.Materializer import akka.stream.scaladsl.Source import com.daml.api.util.TimestampConversion._ -import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger} +import com.daml.error.{ + ContextualizedErrorLogger, + DamlContextualizedErrorLogger, + ErrorCodesVersionSwitcher, +} import com.daml.grpc.adapter.ExecutionSequencerFactory import com.daml.ledger.api.domain.LedgerId import com.daml.ledger.api.v1.testing.time_service.TimeServiceGrpc.TimeService @@ -17,8 +21,7 @@ import com.daml.platform.akkastreams.dispatcher.SignalDispatcher import com.daml.platform.api.grpc.GrpcApiService import com.daml.platform.apiserver.TimeServiceBackend import com.daml.platform.server.api.ValidationLogger -import com.daml.platform.server.api.validation.ErrorFactories -import com.daml.platform.server.api.validation.FieldValidations._ +import com.daml.platform.server.api.validation.{ErrorFactories, FieldValidations} import com.google.protobuf.empty.Empty import io.grpc.{ServerServiceDefinition, StatusRuntimeException} import scalaz.syntax.tag._ @@ -29,6 +32,7 @@ import scala.concurrent.{ExecutionContext, Future} private[apiserver] final class ApiTimeService private ( val ledgerId: LedgerId, backend: TimeServiceBackend, + errorCodesVersionSwitcher: ErrorCodesVersionSwitcher, )(implicit protected val mat: Materializer, protected val esf: ExecutionSequencerFactory, @@ -45,27 +49,32 @@ private[apiserver] final class ApiTimeService private ( s"${getClass.getSimpleName} initialized with ledger ID ${ledgerId.unwrap}, start time ${backend.getCurrentTime}" ) + private val errorFactories = ErrorFactories(errorCodesVersionSwitcher) + private val fieldValidation = FieldValidations(errorFactories) + private val dispatcher = SignalDispatcher[Instant]() override protected def getTimeSource(request: GetTimeRequest): Source[GetTimeResponse, NotUsed] = - matchLedgerId(ledgerId)(LedgerId(request.ledgerId)).fold( - t => Source.failed(ValidationLogger.logFailureWithContext(request, t)), - { ledgerId => - logger.info(s"Received request for time with ledger ID $ledgerId") - dispatcher - .subscribe() - .map(_ => backend.getCurrentTime) - .scan[Option[Instant]](Some(backend.getCurrentTime)) { - case (Some(previousTime), currentTime) if previousTime == currentTime => None - case (_, currentTime) => Some(currentTime) - } - .mapConcat { - case None => Nil - case Some(t) => List(GetTimeResponse(Some(fromInstant(t)))) - } - .via(logger.logErrorsOnStream) - }, - ) + fieldValidation + .matchLedgerId(ledgerId)(LedgerId(request.ledgerId)) + .fold( + t => Source.failed(ValidationLogger.logFailureWithContext(request, t)), + { ledgerId => + logger.info(s"Received request for time with ledger ID $ledgerId") + dispatcher + .subscribe() + .map(_ => backend.getCurrentTime) + .scan[Option[Instant]](Some(backend.getCurrentTime)) { + case (Some(previousTime), currentTime) if previousTime == currentTime => None + case (_, currentTime) => Some(currentTime) + } + .mapConcat { + case None => Nil + case Some(t) => List(GetTimeResponse(Some(fromInstant(t)))) + } + .via(logger.logErrorsOnStream) + }, + ) @SuppressWarnings(Array("org.wartremover.warts.JavaSerializable")) override def setTime(request: SetTimeRequest): Future[Empty] = { @@ -80,7 +89,7 @@ private[apiserver] final class ApiTimeService private ( if (success) Right(requestedTime) else Left( - ErrorFactories.invalidArgument(None)( + errorFactories.invalidArgument(None)( s"current_time mismatch. Provided: $expectedTime. Actual: ${backend.getCurrentTime}" ) ) @@ -88,15 +97,17 @@ private[apiserver] final class ApiTimeService private ( } val result = for { - _ <- matchLedgerId(ledgerId)(LedgerId(request.ledgerId)) - expectedTime <- requirePresence(request.currentTime, "current_time").map(toInstant) - requestedTime <- requirePresence(request.newTime, "new_time").map(toInstant) + _ <- fieldValidation.matchLedgerId(ledgerId)(LedgerId(request.ledgerId)) + expectedTime <- fieldValidation + .requirePresence(request.currentTime, "current_time") + .map(toInstant) + requestedTime <- fieldValidation.requirePresence(request.newTime, "new_time").map(toInstant) _ <- { if (!requestedTime.isBefore(expectedTime)) Right(()) else Left( - ErrorFactories.invalidArgument(None)( + errorFactories.invalidArgument(None)( s"new_time [$requestedTime] is before current_time [$expectedTime]. Setting time backwards is not allowed." ) ) @@ -131,11 +142,15 @@ private[apiserver] final class ApiTimeService private ( } private[apiserver] object ApiTimeService { - def create(ledgerId: LedgerId, backend: TimeServiceBackend)(implicit + def create( + ledgerId: LedgerId, + backend: TimeServiceBackend, + errorCodesVersionSwitcher: ErrorCodesVersionSwitcher, + )(implicit mat: Materializer, esf: ExecutionSequencerFactory, executionContext: ExecutionContext, loggingContext: LoggingContext, ): TimeService with GrpcApiService = - new ApiTimeService(ledgerId, backend) + new ApiTimeService(ledgerId, backend, errorCodesVersionSwitcher) }