Remove pre-1.18 error codes [DPP-773] (#12841)

* Remove legacy error codes

CHANGELOG_BEGIN
CHANGELOG_END

* Remove ValidatorFixture

* Removed redundant helper function

* Remove redundant object

* Rebased.

run-full-compat: true

* Rebased.

run-full-compat: true
This commit is contained in:
Kamil Bozek 2022-02-15 15:28:36 +01:00 committed by GitHub
parent d97f9815b2
commit dfd38186fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 1365 additions and 3447 deletions

View File

@ -75,7 +75,6 @@ da_scala_library(
"//language-support/java/bindings:bindings-java",
"//ledger-api/grpc-definitions:ledger_api_proto_scala",
"//ledger-api/rs-grpc-bridge",
"//ledger/error",
"//ledger/ledger-api-auth",
"//ledger/ledger-api-common",
"//ledger/participant-state-index",

View File

@ -3,7 +3,6 @@
package com.daml.ledger.rxjava.grpc.helpers
import com.daml.error.ErrorCodesVersionSwitcher
import java.net.{InetSocketAddress, SocketAddress}
import java.time.{Clock, Duration}
import java.util.concurrent.TimeUnit
@ -54,7 +53,6 @@ final class LedgerServices(val ledgerId: String) {
() => Clock.systemUTC().instant(),
ledgerId,
participantId,
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = true),
new InMemoryUserManagementStore(),
executionContext,
userRightsCheckIntervalInSeconds = 1,
@ -102,7 +100,6 @@ final class LedgerServices(val ledgerId: String) {
authService,
Some(new InMemoryUserManagementStore()),
executionContext,
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = true),
)
services
.foldLeft(newServerBuilder())(_ addService _)

View File

@ -3,7 +3,6 @@
package com.daml.ledger
import com.daml.error.ErrorCodesVersionSwitcher
import java.time.Clock
import java.util.UUID
@ -35,7 +34,6 @@ package object rxjava {
() => Clock.systemUTC().instant(),
"testLedgerId",
"testParticipantId",
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = true),
new InMemoryUserManagementStore(),
ExecutionContext.parasitic,
userRightsCheckIntervalInSeconds = 1,

View File

@ -1,16 +0,0 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error
import io.grpc.StatusRuntimeException
import scala.concurrent.Future
class ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes: Boolean)
extends ValueSwitch(enableSelfServiceErrorCodes) {
def chooseAsFailedFuture[T](
v1: => StatusRuntimeException,
v2: => StatusRuntimeException,
): Future[T] = Future.failed(choose(v1 = v1, v2 = v2))
}

View File

@ -1,20 +0,0 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error
/** A mechanism to switch between the legacy error codes (v1) and the new self-service error codes (v2).
* This class is intended to facilitate transition to self-service error codes.
* Once the previous error codes are removed, this class and its specializations should be dropped as well.
*/
class ValueSwitch(enableSelfServiceErrorCodes: Boolean) {
def choose[X](
v1: => X,
v2: => X,
): X =
if (enableSelfServiceErrorCodes) {
v2
} else {
v1
}
}

View File

@ -1,45 +0,0 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.error
import io.grpc.{Status, StatusRuntimeException}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class ValueSwitchSpec extends AnyFlatSpec with Matchers {
behavior of classOf[ValueSwitch].getSimpleName
it should "use self-service (v2) error codes" in {
// given
val tested = new ValueSwitch(enableSelfServiceErrorCodes = true)
// when
val actual =
tested.choose(
v1 = fail("This argument should be evaluated lazily!"),
v2 = aStatusRuntimeException,
)
// then
actual shouldBe aStatusRuntimeException
}
it should "use legacy (v1) error codes" in {
// given
val tested = new ValueSwitch(enableSelfServiceErrorCodes = false)
// when
val actual =
tested.choose(
v1 = aStatusRuntimeException,
v2 = fail("This argument should be evaluated lazily!"),
)
// then
actual shouldBe aStatusRuntimeException
}
private lazy val aStatusRuntimeException = new StatusRuntimeException(Status.INTERNAL)
}

View File

@ -76,7 +76,6 @@ object Main {
keyValueSource,
metrics,
failOnUnexpectedEvent = false,
enableSelfServiceErrorCodes = true,
)
// Note: this method is doing quite a lot of work to transform a sequence of write sets

View File

@ -7,11 +7,7 @@ import java.time.Instant
import akka.actor.Scheduler
import com.daml.error.definitions.LedgerApiErrors
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.auth.interceptor.AuthorizationInterceptor
import com.daml.ledger.api.v1.transaction_filter.TransactionFilter
import com.daml.ledger.participant.state.index.v2.UserManagementStore
@ -31,14 +27,13 @@ final class Authorizer(
now: () => Instant,
ledgerId: String,
participantId: String,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
userManagementStore: UserManagementStore,
ec: ExecutionContext,
userRightsCheckIntervalInSeconds: Int,
akkaScheduler: Scheduler,
)(implicit loggingContext: LoggingContext) {
private val logger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private implicit val errorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
@ -209,7 +204,7 @@ final class Authorizer(
case Some(applicationId) if applicationId.nonEmpty => Right(applicationId)
case _ =>
Left(
errorFactories.invalidArgument(None)(
errorFactories.invalidArgument(
"Cannot default application_id field because claims do not specify an application-id or user-id. Is authentication turned on?"
)
)
@ -330,7 +325,6 @@ object Authorizer {
now: () => Instant,
ledgerId: String,
participantId: String,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
userManagementStore: UserManagementStore,
ec: ExecutionContext,
userRightsCheckIntervalInSeconds: Int,
@ -341,7 +335,6 @@ object Authorizer {
now = now,
ledgerId = ledgerId,
participantId = participantId,
errorCodesVersionSwitcher = errorCodesVersionSwitcher,
userManagementStore = userManagementStore,
ec = ec,
userRightsCheckIntervalInSeconds = userRightsCheckIntervalInSeconds,

View File

@ -4,7 +4,7 @@
package com.daml.ledger.api.auth.interceptor
import com.daml.error.definitions.LedgerApiErrors
import com.daml.error.{DamlContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.DamlContextualizedErrorLogger
import com.daml.ledger.api.auth._
import com.daml.ledger.api.domain.UserRight
import com.daml.ledger.participant.state.index.v2.UserManagementStore
@ -26,12 +26,11 @@ final class AuthorizationInterceptor(
authService: AuthService,
userManagementStoreO: Option[UserManagementStore],
implicit val ec: ExecutionContext,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit loggingContext: LoggingContext)
extends ServerInterceptor {
private val logger = ContextualizedLogger.get(getClass)
private val errorLogger = new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
override def interceptCall[ReqT, RespT](
call: ServerCall[ReqT, RespT],
@ -127,7 +126,7 @@ final class AuthorizationInterceptor(
Ref.UserId.fromString(userIdStr) match {
case Left(err) =>
Future.failed(
errorFactories.invalidArgument(None)(s"token $err")(errorLogger)
errorFactories.invalidArgument(s"token $err")(errorLogger)
)
case Right(userId) =>
Future.successful(userId)
@ -155,10 +154,9 @@ object AuthorizationInterceptor {
authService: AuthService,
userManagementStoreO: Option[UserManagementStore],
ec: ExecutionContext,
errorCodesStatusSwitcher: ErrorCodesVersionSwitcher,
): AuthorizationInterceptor =
LoggingContext.newLoggingContext { implicit loggingContext: LoggingContext =>
new AuthorizationInterceptor(authService, userManagementStoreO, ec, errorCodesStatusSwitcher)
new AuthorizationInterceptor(authService, userManagementStoreO, ec)
}
def convertUserRightsToClaims(userRights: Set[UserRight]): Seq[Claim] = {

View File

@ -3,7 +3,6 @@
package com.daml.ledger.api.auth
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.auth.interceptor.AuthorizationInterceptor
import io.grpc.protobuf.StatusProto
import io.grpc.{Metadata, ServerCall, Status}
@ -29,16 +28,8 @@ class AuthorizationInterceptorSpec
behavior of s"$className.interceptCall"
it should "close the ServerCall with a V1 status code on decoding failure" in {
testServerCloseError(usesSelfServiceErrorCodes = false) { case (actualStatus, actualMetadata) =>
actualStatus.getCode shouldBe Status.Code.INTERNAL
actualStatus.getDescription shouldBe "Failed to get claims from request metadata"
actualMetadata.keys() shouldBe empty
}
}
it should "close the ServerCall with a V2 status code on decoding failure" in {
testServerCloseError(usesSelfServiceErrorCodes = true) { case (actualStatus, actualMetadata) =>
testServerCloseError { case (actualStatus, actualMetadata) =>
actualStatus.getCode shouldBe Status.Code.INTERNAL
actualStatus.getDescription shouldBe "An error occurred. Please contact the operator and inquire about the request <no-correlation-id>"
@ -47,9 +38,7 @@ class AuthorizationInterceptorSpec
}
}
private def testServerCloseError(
usesSelfServiceErrorCodes: Boolean
)(assertRpcStatus: (Status, Metadata) => Assertion) = {
private def testServerCloseError(assertRpcStatus: (Status, Metadata) => Assertion) = {
val authService = mock[AuthService]
val userManagementService = mock[UserManagementStore]
val serverCall = mock[ServerCall[Nothing, Nothing]]
@ -64,13 +53,11 @@ class AuthorizationInterceptorSpec
()
}
val errorCodesStatusSwitcher = new ErrorCodesVersionSwitcher(usesSelfServiceErrorCodes)
val authorizationInterceptor =
AuthorizationInterceptor(
authService,
Some(userManagementService),
global,
errorCodesStatusSwitcher,
)
val statusCaptor = ArgCaptor[Status]

View File

@ -3,7 +3,6 @@
package com.daml.ledger.api.auth
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.auth.interceptor.AuthorizationInterceptor
import io.grpc.{Status, StatusRuntimeException}
import org.scalatest.Assertion
@ -36,41 +35,30 @@ class AuthorizerSpec
it should "authorize if claims are valid" in {
contextWithClaims {
authorizer(selfServiceErrorCodes = false)
authorizer()
.authorize(dummyReqRes)(allAuthorized)(dummyRequest)
}.map(_ shouldBe expectedSuccessfulResponse)
}
behavior of s"$className.authorize (V1 error codes)"
behavior of s"$className.authorize"
it should "return permission denied on authorization error" in {
testPermissionDenied(selfServiceErrorCodes = false)
testPermissionDenied()
}
behavior of s"$className.authorize (V2 error codes)"
it should "return permission denied on authorization error" in {
testPermissionDenied(selfServiceErrorCodes = true)
}
private def testPermissionDenied(selfServiceErrorCodes: Boolean) =
private def testPermissionDenied() =
contextWithClaims {
authorizer(selfServiceErrorCodes).authorize(dummyReqRes)(unauthorized)(dummyRequest)
authorizer().authorize(dummyReqRes)(unauthorized)(dummyRequest)
}
.transform(
assertExpectedFailure(selfServiceErrorCodes = selfServiceErrorCodes)(
Status.PERMISSION_DENIED.getCode
)
assertExpectedFailure(Status.PERMISSION_DENIED.getCode)
)
private def assertExpectedFailure[T](
selfServiceErrorCodes: Boolean
)(expectedStatusCode: Status.Code): Try[T] => Try[Assertion] = {
expectedStatusCode: Status.Code
): Try[T] => Try[Assertion] = {
case Failure(ex: StatusRuntimeException) =>
ex.getStatus.getCode shouldBe expectedStatusCode
if (selfServiceErrorCodes) {
ex.getStatus.getDescription shouldBe "An error occurred. Please contact the operator and inquire about the request <no-correlation-id>"
}
Success(succeed)
case ex => fail(s"Expected a failure with StatusRuntimeException but got $ex")
}
@ -80,11 +68,10 @@ class AuthorizerSpec
.withValue(AuthorizationInterceptor.contextKeyClaimSet, ClaimSet.Claims.Wildcard)
.call(() => f)
private def authorizer(selfServiceErrorCodes: Boolean) = Authorizer(
private def authorizer() = Authorizer(
() => Instant.ofEpochSecond(1337L),
"some-ledger-id",
"participant-id",
new ErrorCodesVersionSwitcher(selfServiceErrorCodes),
mock[UserManagementStore],
mock[ExecutionContext],
userRightsCheckIntervalInSeconds = 1,

View File

@ -79,134 +79,124 @@ class CompletionResponseTest extends AnyWordSpec with Matchers {
implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
def testIt(useSelfServiceErrorCodes: Boolean) = {
val errorFactories = ErrorFactories()
val errorFactories = ErrorFactories(useSelfServiceErrorCodes)
s"(self service error codes: $useSelfServiceErrorCodes)" should {
"convert queue completion failure" in {
val exception =
CompletionResponse.toException(
QueueCompletionFailure(TimeoutResponse(commandId)),
errorFactories,
)
exception.getStatus.getCode shouldBe {
if (useSelfServiceErrorCodes) Code.DEADLINE_EXCEEDED
else Code.ABORTED
}
}
"convert queue submit failure" in {
val status = GrpcStatus.buildStatus(
Map.empty,
GrpcStatus.toJavaBuilder(grpc.Status.RESOURCE_EXHAUSTED),
)
val exception =
CompletionResponse.toException(
QueueSubmitFailure(status),
errorFactories,
)
exception.getStatus.getCode shouldBe Code.RESOURCE_EXHAUSTED
}
"include default metadata for status not ok" in {
val exception = CompletionResponse.toException(
QueueCompletionFailure(
NotOkResponse(
Completion(
commandId = commandId,
status = Some(
Status(
Code.CANCELLED.value(),
details = Seq.empty,
)
),
),
None,
)
),
errorFactories,
)
val status = protobuf.StatusProto.fromThrowable(exception)
val packedErrorInfo = status.getDetails(0).unpack(classOf[JavaErrorInfo])
packedErrorInfo.getMetadataOrThrow(GrpcStatuses.DefiniteAnswerKey) shouldEqual "false"
}
"include metadata for status not ok" in {
val errorInfo = ErrorInfo(
metadata = Map(GrpcStatuses.DefiniteAnswerKey -> "true")
)
val exception = CompletionResponse.toException(
QueueCompletionFailure(
NotOkResponse(
Completion(
commandId = commandId,
status = Some(
Status(
Code.CANCELLED.value(),
details = Seq(
Any.pack(
errorInfo
)
),
)
),
),
None,
)
),
errorFactories,
)
val status = protobuf.StatusProto.fromThrowable(exception)
val packedErrorInfo = status.getDetails(0).unpack(classOf[JavaErrorInfo])
packedErrorInfo.getMetadataOrThrow(GrpcStatuses.DefiniteAnswerKey) shouldEqual "true"
}
"merge metadata for status not ok" in {
val errorInfo = ErrorInfo(
metadata = Map(GrpcStatuses.DefiniteAnswerKey -> "true")
)
val requestInfo = RequestInfo(requestId = "aRequestId")
val exception = CompletionResponse.toException(
QueueCompletionFailure(
NotOkResponse(
Completion(
commandId = commandId,
status = Some(
Status(
Code.INTERNAL.value(),
details = Seq(
Any.pack(errorInfo),
Any.pack(requestInfo),
),
)
),
),
None,
)
),
errorFactories,
)
val status = protobuf.StatusProto.fromThrowable(exception)
status.getCode shouldBe Code.INTERNAL.value()
val details = status.getDetailsList.asScala
details.size shouldBe 2
details.exists { detail =>
detail.is(classOf[JavaErrorInfo]) && detail
.unpack(classOf[JavaErrorInfo])
.getMetadataOrThrow(GrpcStatuses.DefiniteAnswerKey) == "true"
} shouldEqual true
details.exists { detail =>
detail.is(classOf[JavaRequestInfo]) && detail
.unpack(classOf[JavaRequestInfo])
.getRequestId == "aRequestId"
} shouldEqual true
}
}
"convert queue completion failure" in {
val exception =
CompletionResponse.toException(
QueueCompletionFailure(TimeoutResponse(commandId)),
errorFactories,
)
exception.getStatus.getCode shouldBe Code.DEADLINE_EXCEEDED
}
"convert queue submit failure" in {
val status = GrpcStatus.buildStatus(
Map.empty,
GrpcStatus.toJavaBuilder(grpc.Status.RESOURCE_EXHAUSTED),
)
val exception =
CompletionResponse.toException(
QueueSubmitFailure(status),
errorFactories,
)
exception.getStatus.getCode shouldBe Code.RESOURCE_EXHAUSTED
}
"include default metadata for status not ok" in {
val exception = CompletionResponse.toException(
QueueCompletionFailure(
NotOkResponse(
Completion(
commandId = commandId,
status = Some(
Status(
Code.CANCELLED.value(),
details = Seq.empty,
)
),
),
None,
)
),
errorFactories,
)
val status = protobuf.StatusProto.fromThrowable(exception)
val packedErrorInfo = status.getDetails(0).unpack(classOf[JavaErrorInfo])
packedErrorInfo.getMetadataOrThrow(GrpcStatuses.DefiniteAnswerKey) shouldEqual "false"
}
"include metadata for status not ok" in {
val errorInfo = ErrorInfo(
metadata = Map(GrpcStatuses.DefiniteAnswerKey -> "true")
)
val exception = CompletionResponse.toException(
QueueCompletionFailure(
NotOkResponse(
Completion(
commandId = commandId,
status = Some(
Status(
Code.CANCELLED.value(),
details = Seq(
Any.pack(
errorInfo
)
),
)
),
),
None,
)
),
errorFactories,
)
val status = protobuf.StatusProto.fromThrowable(exception)
val packedErrorInfo = status.getDetails(0).unpack(classOf[JavaErrorInfo])
packedErrorInfo.getMetadataOrThrow(GrpcStatuses.DefiniteAnswerKey) shouldEqual "true"
}
"merge metadata for status not ok" in {
val errorInfo = ErrorInfo(
metadata = Map(GrpcStatuses.DefiniteAnswerKey -> "true")
)
val requestInfo = RequestInfo(requestId = "aRequestId")
val exception = CompletionResponse.toException(
QueueCompletionFailure(
NotOkResponse(
Completion(
commandId = commandId,
status = Some(
Status(
Code.INTERNAL.value(),
details = Seq(
Any.pack(errorInfo),
Any.pack(requestInfo),
),
)
),
),
None,
)
),
errorFactories,
)
val status = protobuf.StatusProto.fromThrowable(exception)
status.getCode shouldBe Code.INTERNAL.value()
val details = status.getDetailsList.asScala
details.size shouldBe 2
details.exists { detail =>
detail.is(classOf[JavaErrorInfo]) && detail
.unpack(classOf[JavaErrorInfo])
.getMetadataOrThrow(GrpcStatuses.DefiniteAnswerKey) == "true"
} shouldEqual true
details.exists { detail =>
detail.is(classOf[JavaRequestInfo]) && detail
.unpack(classOf[JavaRequestInfo])
.getRequestId == "aRequestId"
} shouldEqual true
}
testIt(useSelfServiceErrorCodes = true)
testIt(useSelfServiceErrorCodes = false)
}
}
}

View File

@ -6,7 +6,7 @@ package com.daml.ledger.api.validation
import java.time.{Duration, Instant}
import com.daml.api.util.{DurationConversion, TimestampConversion}
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.ContextualizedErrorLogger
import com.daml.ledger.api.domain.{LedgerId, optionalLedgerId}
import com.daml.ledger.api.v1.commands
import com.daml.ledger.api.v1.commands.Command.Command.{
@ -28,19 +28,16 @@ import com.daml.platform.server.api.validation.{
ErrorFactories,
FieldValidations,
}
import io.grpc.{Status, StatusRuntimeException}
import io.grpc.StatusRuntimeException
import scalaz.syntax.tag._
import scala.Ordering.Implicits.infixOrderingOps
import scala.collection.immutable
import scala.annotation.nowarn
final class CommandsValidator(
ledgerId: LedgerId,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
) {
final class CommandsValidator(ledgerId: LedgerId) {
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val fieldValidations = FieldValidations(errorFactories)
private val valueValidator = new ValueValidator(errorFactories, fieldValidations)
private val deduplicationValidator = new DeduplicationPeriodValidator(errorFactories)
@ -73,7 +70,7 @@ final class CommandsValidator(
.fromInstant(ledgerEffectiveTime)
.left
.map(_ =>
invalidArgument(definiteAnswer = Some(false))(
invalidArgument(
s"Can not represent command ledger time $ledgerEffectiveTime as a Daml timestamp"
)
)
@ -114,7 +111,7 @@ final class CommandsValidator(
case (None, Some(minRel)) => Right(currentTime.plus(DurationConversion.fromProto(minRel)))
case (Some(_), Some(_)) =>
Left(
invalidArgument(definiteAnswer = Some(false))(
invalidArgument(
"min_ledger_time_abs cannot be specified at the same time as min_ledger_time_rel"
)
)
@ -201,7 +198,7 @@ final class CommandsValidator(
choiceArgument = validatedChoiceArgument,
)
case ProtoEmpty =>
Left(missingField("command", definiteAnswer = Some(false)))
Left(missingField("command"))
}
private def validateSubmitters(
@ -213,7 +210,7 @@ final class CommandsValidator(
Either.cond(
effectiveActAs.nonEmpty,
(),
missingField("party or act_as", definiteAnswer = Some(false)),
missingField("party or act_as"),
)
val submitters = effectiveSubmitters(commands)
@ -238,7 +235,7 @@ final class CommandsValidator(
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, DeduplicationPeriod] =
optMaxDeduplicationDuration.fold[Either[StatusRuntimeException, DeduplicationPeriod]](
Left(missingLedgerConfig(Status.Code.UNAVAILABLE)(definiteAnswer = Some(false)))
Left(missingLedgerConfig())
) { maxDeduplicationDuration =>
deduplicationPeriod match {
case commands.Commands.DeduplicationPeriod.Empty =>
@ -259,7 +256,7 @@ final class CommandsValidator(
.fold(
_ =>
Left(
nonHexOffset(None)(
nonHexOffset(
fieldName = "deduplication_period",
offsetValue = offset,
message =

View File

@ -3,7 +3,7 @@
package com.daml.ledger.api.validation
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.ContextualizedErrorLogger
import com.daml.ledger.api.domain.{LedgerId, LedgerOffset, optionalLedgerId}
import com.daml.ledger.api.messages.command.completion
import com.daml.ledger.api.messages.command.completion.CompletionStreamRequest
@ -17,10 +17,9 @@ import io.grpc.StatusRuntimeException
class CompletionServiceRequestValidator(
ledgerId: LedgerId,
partyNameChecker: PartyNameChecker,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
) {
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val fieldValidations = FieldValidations(errorFactories)
private val partyValidator =
new PartyValidator(partyNameChecker, errorFactories, fieldValidations)

View File

@ -45,7 +45,7 @@ class LedgerOffsetValidator(errorFactories: ErrorFactories) {
case LedgerOffset.Value.Boundary(value) =>
convertLedgerBoundary(fieldName, value)
case LedgerOffset.Value.Empty =>
Left(missingField(fieldName + ".(" + boundary + "|value)", None))
Left(missingField(fieldName + ".(" + boundary + "|value)"))
}
}
@ -83,7 +83,7 @@ class LedgerOffsetValidator(errorFactories: ErrorFactories) {
value match {
case LedgerBoundary.Unrecognized(invalid) =>
Left(
invalidArgument(None)(
invalidArgument(
s"Unknown ledger $boundary value '$invalid' in field $fieldName.$boundary"
)
)

View File

@ -31,7 +31,7 @@ class PartyValidator(
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger): Result[Set[Party]] = {
val unknownParties = partiesInRequest.filterNot(partyNameChecker.isKnownParty)
if (unknownParties.nonEmpty)
Left(invalidArgument(None)(s"Unknown parties: ${unknownParties.mkString("[", ", ", "]")}"))
Left(invalidArgument(s"Unknown parties: ${unknownParties.mkString("[", ", ", "]")}"))
else Right(partiesInRequest)
}
}

View File

@ -26,7 +26,7 @@ class TransactionFilterValidator(errorFactories: ErrorFactories) {
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, domain.TransactionFilter] = {
if (txFilter.filtersByParty.isEmpty) {
Left(invalidArgument(None)("filtersByParty cannot be empty"))
Left(invalidArgument("filtersByParty cannot be empty"))
} else {
val convertedFilters =
txFilter.filtersByParty.toList.traverse { case (k, v) =>

View File

@ -188,7 +188,7 @@ class TransactionServiceRequestValidator(
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger) =
transactionFilter.filtersByParty
.collectFirst { case (party, Filters(Some(inclusive))) =>
invalidArgument(None)(
invalidArgument(
s"$party attempted subscription for templates ${inclusive.templateIds.mkString("[", ", ", "]")}. Template filtration is not supported on GetTransactionTrees RPC. To get filtered data, use the GetTransactions RPC."
)
}

View File

@ -3,7 +3,7 @@
package com.daml.ledger.api.validation
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher, NoLogging}
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
import com.daml.ledger.api.domain
import com.daml.ledger.api.v1.value.Value.Sum
import com.daml.ledger.api.v1.{value => api}
@ -54,10 +54,10 @@ class ValueValidator(errorFactories: ErrorFactories, fieldValidations: FieldVali
case Sum.ContractId(cId) =>
ContractId
.fromString(cId)
.bimap(invalidArgument(definiteAnswer = Some(false)), Lf.ValueContractId(_))
.bimap(invalidArgument, Lf.ValueContractId(_))
case Sum.Numeric(value) =>
def err =
invalidArgument(definiteAnswer = Some(false))(s"""Could not read Numeric string "$value"""")
invalidArgument(s"""Could not read Numeric string "$value"""")
if (validNumericString.matcher(value).matches())
Numeric
.fromUnscaledBigDecimal(new java.math.BigDecimal(value))
@ -69,20 +69,20 @@ class ValueValidator(errorFactories: ErrorFactories, fieldValidations: FieldVali
Ref.Party
.fromString(party)
.left
.map(invalidArgument(definiteAnswer = Some(false)))
.map(invalidArgument)
.map(Lf.ValueParty)
case Sum.Bool(b) => Right(Lf.ValueBool(b))
case Sum.Timestamp(micros) =>
Time.Timestamp
.fromLong(micros)
.left
.map(invalidArgument(definiteAnswer = Some(false)))
.map(invalidArgument)
.map(Lf.ValueTimestamp)
case Sum.Date(days) =>
Time.Date
.fromDaysSinceEpoch(days)
.left
.map(invalidArgument(definiteAnswer = Some(false)))
.map(invalidArgument)
.map(Lf.ValueDate)
case Sum.Text(text) => Right(Lf.ValueText(text))
case Sum.Int64(value) => Right(Lf.ValueInt64(value))
@ -131,7 +131,7 @@ class ValueValidator(errorFactories: ErrorFactories, fieldValidations: FieldVali
map <- SortedLookupList
.fromImmArray(entries.toImmArray)
.left
.map(invalidArgument(definiteAnswer = Some(false)))
.map(invalidArgument)
} yield Lf.ValueTextMap(map)
case Sum.GenMap(genMap0) =>
@ -149,7 +149,7 @@ class ValueValidator(errorFactories: ErrorFactories, fieldValidations: FieldVali
}
genMap.map(entries => Lf.ValueGenMap(entries.toImmArray))
case Sum.Empty => Left(missingField("value", definiteAnswer = Some(false)))
case Sum.Empty => Left(missingField("value"))
}
private[validation] def validateOptionalIdentifier(
@ -161,13 +161,9 @@ class ValueValidator(errorFactories: ErrorFactories, fieldValidations: FieldVali
}
/** Implementation of self-service error codes brings logging-on-creation to
* error factories and validators, used in the Ledger API where automatic logging is desired.
* For places where the ValueValidator is needed without logging(e.g. daml-script, navigator), use this implementation instead.
*/
object NoLoggingValueValidator {
// TODO error codes: re-check if using legacy error codes here is ok
private val errorFactories = ErrorFactories(new ErrorCodesVersionSwitcher(false))
private val errorFactories = ErrorFactories()
private val valueValidator = new ValueValidator(
errorFactories = errorFactories,
fieldValidations = FieldValidations(errorFactories),

View File

@ -5,7 +5,7 @@ package com.daml.platform.server.api.services.grpc
import java.time.{Duration, Instant}
import com.daml.error.{DamlContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.DamlContextualizedErrorLogger
import com.daml.ledger.api.SubmissionIdGenerator
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.command_service.CommandServiceGrpc.CommandService
@ -23,7 +23,6 @@ import scala.concurrent.{ExecutionContext, Future}
class GrpcCommandService(
protected val service: CommandService with AutoCloseable,
val ledgerId: LedgerId,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
currentLedgerTime: () => Instant,
currentUtcTime: () => Instant,
maxDeduplicationTime: () => Option[Duration],
@ -36,8 +35,8 @@ class GrpcCommandService(
protected implicit val logger: ContextualizedLogger = ContextualizedLogger.get(getClass)
private[this] val validator = new SubmitAndWaitRequestValidator(
new CommandsValidator(ledgerId, errorCodesVersionSwitcher),
FieldValidations(ErrorFactories(errorCodesVersionSwitcher)),
new CommandsValidator(ledgerId),
FieldValidations(ErrorFactories()),
)
override def submitAndWait(request: SubmitAndWaitRequest): Future[Empty] =

View File

@ -3,7 +3,7 @@
package com.daml.platform.server.api.services.grpc
import com.daml.error.{DamlContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.DamlContextualizedErrorLogger
import com.daml.ledger.api.SubmissionIdGenerator
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.command_submission_service.CommandSubmissionServiceGrpc.{
@ -35,7 +35,6 @@ class GrpcCommandSubmissionService(
maxDeduplicationTime: () => Option[Duration],
submissionIdGenerator: SubmissionIdGenerator,
metrics: Metrics,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends ApiCommandSubmissionService
with ProxyCloseable
@ -43,8 +42,8 @@ class GrpcCommandSubmissionService(
protected implicit val logger: ContextualizedLogger = ContextualizedLogger.get(getClass)
private val validator = new SubmitRequestValidator(
new CommandsValidator(ledgerId, errorCodesVersionSwitcher),
FieldValidations(ErrorFactories(errorCodesVersionSwitcher)),
new CommandsValidator(ledgerId),
FieldValidations(ErrorFactories()),
)
override def submit(request: ApiSubmitRequest): Future[Empty] = {

View File

@ -6,11 +6,7 @@ package com.daml.platform.server.api.services.grpc
import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.health.HealthChecks
import com.daml.logging.{ContextualizedLogger, LoggingContext}
@ -32,7 +28,6 @@ import scala.util.{Failure, Success, Try}
class GrpcHealthService(
healthChecks: HealthChecks,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
maximumWatchFrequency: FiniteDuration = 1.second,
)(implicit
protected val esf: ExecutionSequencerFactory,
@ -45,7 +40,7 @@ class GrpcHealthService(
private val logger = ContextualizedLogger.get(getClass)
private val errorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
import errorFactories.invalidArgumentWasNotFound
@ -66,9 +61,7 @@ class GrpcHealthService(
.collect {
case component if !healthChecks.hasComponent(component) =>
Failure(
invalidArgumentWasNotFound(None)(
s"Component $component does not exist."
)(errorLogger)
invalidArgumentWasNotFound(s"Component $component does not exist.")(errorLogger)
)
}
.getOrElse {

View File

@ -34,9 +34,7 @@ class DeduplicationPeriodValidator(
if (duration.compareTo(maxDeduplicationDuration) > 0)
Left(
errorFactories.invalidDeduplicationPeriod(
fieldName,
s"The given deduplication duration of $duration exceeds the maximum deduplication time of $maxDeduplicationDuration",
definiteAnswer = Some(false),
Some(maxDeduplicationDuration),
)
)
@ -51,7 +49,6 @@ class DeduplicationPeriodValidator(
.invalidField(
fieldName,
"Duration must be positive",
definiteAnswer = Some(false),
)
)
else Right(duration)

View File

@ -6,9 +6,8 @@ package com.daml.platform.server.api.validation
import java.sql.{SQLNonTransientException, SQLTransientException}
import java.time.{Duration, Instant}
import com.daml.error.ErrorCode.ApiException
import com.daml.error.definitions.{IndexErrors, LedgerApiErrors}
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.ContextualizedErrorLogger
import com.daml.grpc.GrpcStatus
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.grpc.GrpcStatuses
@ -16,108 +15,61 @@ import com.daml.ledger.offset.Offset
import com.daml.lf.data.Ref.TransactionId
import com.daml.lf.transaction.GlobalKey
import com.daml.lf.value.Value
import com.daml.platform.server.api.validation.ErrorFactories.{
addDefiniteAnswerDetails,
definiteAnswers,
}
import com.daml.platform.server.api.validation.ErrorFactories.addDefiniteAnswerDetails
import com.daml.platform.server.api.{ApiException => NoStackTraceApiException}
import com.google.protobuf.{Any => AnyProto}
import com.google.rpc.status.{Status => RpcStatus}
import com.google.rpc.{ErrorInfo, Status}
import io.grpc.Status.Code
import io.grpc.protobuf.StatusProto
import io.grpc.{Metadata, StatusRuntimeException}
import io.grpc.StatusRuntimeException
import scalaz.syntax.tag._
import scala.annotation.nowarn
class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitcher) {
class ErrorFactories private () {
object SubmissionQueueErrors {
def failedToEnqueueCommandSubmission(message: String)(t: Throwable)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Status =
errorCodesVersionSwitcher.choose(
v1 = {
val status = io.grpc.Status.ABORTED
.withDescription(s"$message: ${t.getClass.getSimpleName}: ${t.getMessage}")
.withCause(t)
val statusBuilder = GrpcStatus.toJavaBuilder(status)
GrpcStatus.buildStatus(Map.empty, statusBuilder)
},
v2 = LedgerApiErrors.InternalError
.Generic(
message = s"$message: ${t.getClass.getSimpleName}: ${t.getMessage}",
throwableO = Some(t),
)
.asGrpcStatusFromContext,
)
LedgerApiErrors.InternalError
.Generic(
message = s"$message: ${t.getClass.getSimpleName}: ${t.getMessage}",
throwableO = Some(t),
)
.asGrpcStatusFromContext
def queueClosed(queueName: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Status =
errorCodesVersionSwitcher.choose(
v1 = {
val status = io.grpc.Status.ABORTED.withDescription("Queue closed")
val statusBuilder = GrpcStatus.toJavaBuilder(status)
GrpcStatus.buildStatus(Map.empty, statusBuilder)
},
v2 = LedgerApiErrors.ServiceNotRunning
.Reject(queueName)
.asGrpcStatusFromContext,
)
LedgerApiErrors.ServiceNotRunning
.Reject(queueName)
.asGrpcStatusFromContext
def timedOutOnAwaitingForCommandCompletion()(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Status =
errorCodesVersionSwitcher.choose(
v1 = {
val statusBuilder =
GrpcStatus.toJavaBuilder(Code.ABORTED.value(), Some("Timeout"), Iterable.empty)
GrpcStatus.buildStatus(Map.empty, statusBuilder)
},
v2 = LedgerApiErrors.RequestTimeOut
.Reject(
"Timed out while awaiting for a completion corresponding to a command submission.",
_definiteAnswer = false,
)
.asGrpcStatusFromContext,
)
LedgerApiErrors.RequestTimeOut
.Reject(
"Timed out while awaiting for a completion corresponding to a command submission.",
_definiteAnswer = false,
)
.asGrpcStatusFromContext
def noStatusInCompletionResponse()(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Status = {
errorCodesVersionSwitcher.choose(
v1 = {
Status
.newBuilder()
.setCode(Code.INTERNAL.value())
.setMessage("Missing status in completion response.")
.build()
},
v2 = LedgerApiErrors.InternalError
.Generic(
"Missing status in completion response.",
throwableO = None,
)
.asGrpcStatusFromContext,
)
}
): Status =
LedgerApiErrors.InternalError
.Generic(
"Missing status in completion response.",
throwableO = None,
)
.asGrpcStatusFromContext
}
def bufferFull(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Status =
errorCodesVersionSwitcher.choose(
v1 = {
val status = io.grpc.Status.RESOURCE_EXHAUSTED
.withDescription("Ingress buffer is full")
val statusBuilder = GrpcStatus.toJavaBuilder(status)
GrpcStatus.buildStatus(Map.empty, statusBuilder)
},
v2 = LedgerApiErrors.ParticipantBackpressure
.Rejection(message)
.asGrpcStatusFromContext,
)
LedgerApiErrors.ParticipantBackpressure
.Rejection(message)
.asGrpcStatusFromContext
def sqlTransientException(exception: SQLTransientException)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
@ -132,219 +84,114 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch
def transactionNotFound(transactionId: TransactionId)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = grpcError(
Status
.newBuilder()
.setCode(Code.NOT_FOUND.value())
.setMessage("Transaction not found, or not visible.")
.build()
),
v2 = LedgerApiErrors.RequestValidation.NotFound.Transaction
.Reject(transactionId)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.NotFound.Transaction
.Reject(transactionId)
.asGrpcError
def packageNotFound(packageId: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = io.grpc.Status.NOT_FOUND.asRuntimeException(),
v2 = LedgerApiErrors.RequestValidation.NotFound.Package
.Reject(_packageId = packageId)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.NotFound.Package
.Reject(_packageId = packageId)
.asGrpcError
def versionServiceInternalError(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = grpcError(
Status
.newBuilder()
.setCode(Code.INTERNAL.value())
.setMessage(message)
.build()
),
v2 = LedgerApiErrors.InternalError.VersionService(message).asGrpcError,
)
LedgerApiErrors.InternalError.VersionService(message).asGrpcError
def duplicateCommandException(existingSubmissionId: Option[String])(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
val exception = grpcError(
Status
.newBuilder()
.setCode(Code.ALREADY_EXISTS.value())
.setMessage("Duplicate command")
.addDetails(definiteAnswers(false))
.build()
)
contextualizedErrorLogger.info(exception.getMessage)
exception
},
v2 = LedgerApiErrors.ConsistencyErrors.DuplicateCommand
.Reject(_existingCommandSubmissionId = existingSubmissionId)
.asGrpcError,
)
LedgerApiErrors.ConsistencyErrors.DuplicateCommand
.Reject(_existingCommandSubmissionId = existingSubmissionId)
.asGrpcError
/** @param expected Expected ledger id.
* @param received Received ledger id.
* @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @return An exception with the [[Code.NOT_FOUND]] status code.
*/
def ledgerIdMismatch(
expected: LedgerId,
received: LedgerId,
definiteAnswer: Option[Boolean],
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger): StatusRuntimeException = {
require(!definiteAnswer.contains(true), "Wrong ledger ID can never be a definite answer.")
errorCodesVersionSwitcher.choose(
v1 = {
val statusBuilder = Status
.newBuilder()
.setCode(Code.NOT_FOUND.value())
.setMessage(
s"Ledger ID '${received.unwrap}' not found. Actual Ledger ID is '${expected.unwrap}'."
)
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
},
v2 = LedgerApiErrors.RequestValidation.LedgerIdMismatch
.Reject(
s"Ledger ID '${received.unwrap}' not found. Actual Ledger ID is '${expected.unwrap}'."
)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.LedgerIdMismatch
.Reject(
s"Ledger ID '${received.unwrap}' not found. Actual Ledger ID is '${expected.unwrap}'."
)
.asGrpcError
}
/** @param fieldName A missing field's name.
* @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @return An exception with the [[Code.INVALID_ARGUMENT]] status code.
*/
def missingField(fieldName: String, definiteAnswer: Option[Boolean])(implicit
def missingField(fieldName: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
val statusBuilder = Status
.newBuilder()
.setCode(Code.INVALID_ARGUMENT.value())
.setMessage(s"Missing field: $fieldName")
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
},
v2 = LedgerApiErrors.RequestValidation.MissingField
.Reject(fieldName)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.MissingField
.Reject(fieldName)
.asGrpcError
/** @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @param message A status' message.
/** @param message A status' message.
* @return An exception with the [[Code.INVALID_ARGUMENT]] status code.
*/
def invalidArgument(definiteAnswer: Option[Boolean])(message: String)(implicit
def invalidArgument(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = invalidArgumentV1(definiteAnswer, message),
v2 = LedgerApiErrors.RequestValidation.InvalidArgument
.Reject(message)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.InvalidArgument
.Reject(message)
.asGrpcError
// This error builder covers cases where existing logic handling invalid arguments returned NOT_FOUND.
def invalidArgumentWasNotFound(definiteAnswer: Option[Boolean])(message: String)(implicit
def invalidArgumentWasNotFound(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
val statusBuilder = Status
.newBuilder()
.setCode(Code.NOT_FOUND.value())
.setMessage(message)
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
},
v2 = LedgerApiErrors.RequestValidation.InvalidArgument
.Reject(message)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.InvalidArgument
.Reject(message)
.asGrpcError
def offsetOutOfRange(
definiteAnswer: Option[Boolean]
)(message: String)(implicit
def offsetOutOfRange(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = invalidArgumentV1(definiteAnswer, message),
v2 = LedgerApiErrors.RequestValidation.OffsetOutOfRange
.Reject(message)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.OffsetOutOfRange
.Reject(message)
.asGrpcError
def offsetAfterLedgerEnd(offsetType: String, requestedOffset: String, ledgerEnd: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = {
errorCodesVersionSwitcher.choose(
v1 = grpcError(
Status
.newBuilder()
.setCode(Code.OUT_OF_RANGE.value())
.setMessage(s"$offsetType offset ($requestedOffset) is after ledger end ($ledgerEnd)")
.build()
),
v2 = LedgerApiErrors.RequestValidation.OffsetAfterLedgerEnd
.Reject(offsetType, requestedOffset, ledgerEnd)
.asGrpcError,
)
}
): StatusRuntimeException =
LedgerApiErrors.RequestValidation.OffsetAfterLedgerEnd
.Reject(offsetType, requestedOffset, ledgerEnd)
.asGrpcError
def nonHexOffset(
definiteAnswer: Option[Boolean]
)(fieldName: String, offsetValue: String, message: String)(implicit
def nonHexOffset(fieldName: String, offsetValue: String, message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = invalidArgumentV1(definiteAnswer, message),
v2 = LedgerApiErrors.RequestValidation.NonHexOffset
.Error(
_fieldName = fieldName,
_offsetValue = offsetValue,
_message = message,
)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.NonHexOffset
.Error(
_fieldName = fieldName,
_offsetValue = offsetValue,
_message = message,
)
.asGrpcError
def invalidDeduplicationPeriod(
fieldName: String,
message: String,
definiteAnswer: Option[Boolean],
maxDeduplicationDuration: Option[Duration],
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
legacyInvalidField(fieldName, message, definiteAnswer),
LedgerApiErrors.RequestValidation.InvalidDeduplicationPeriodField
.Reject(message, maxDeduplicationDuration)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.InvalidDeduplicationPeriodField
.Reject(message, maxDeduplicationDuration)
.asGrpcError
/** @param fieldName An invalid field's name.
* @param message A status' message.
* @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @return An exception with the [[Code.INVALID_ARGUMENT]] status code.
*/
def invalidField(
fieldName: String,
message: String,
definiteAnswer: Option[Boolean],
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = legacyInvalidField(fieldName, message, definiteAnswer),
v2 = ledgerRequestValidationInvalidField(fieldName, message).asGrpcError,
)
ledgerRequestValidationInvalidField(fieldName, message).asGrpcError
private def ledgerRequestValidationInvalidField(fieldName: String, message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
@ -353,19 +200,6 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch
.Reject(s"Invalid field $fieldName: $message")
}
private def legacyInvalidField(
fieldName: String,
message: String,
definiteAnswer: Option[Boolean],
): StatusRuntimeException = {
val statusBuilder = Status
.newBuilder()
.setCode(Code.INVALID_ARGUMENT.value())
.setMessage(s"Invalid field $fieldName: $message")
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
}
/** @param message A status' message.
* @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @return An exception with the [[Code.ABORTED]] status code.
@ -380,177 +214,77 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch
grpcError(statusBuilder.build())
}
@nowarn("msg=deprecated")
def isTimeoutUnknown_wasAborted(message: String, definiteAnswer: Option[Boolean])(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = {
errorCodesVersionSwitcher.choose(
v1 = aborted(message, definiteAnswer),
v2 = LedgerApiErrors.RequestTimeOut
.Reject(message, definiteAnswer.getOrElse(false))
.asGrpcError,
)
}
): StatusRuntimeException =
LedgerApiErrors.RequestTimeOut
.Reject(message, definiteAnswer.getOrElse(false))
.asGrpcError
def packageUploadRejected(message: String, definiteAnswer: Option[Boolean])(implicit
def packageUploadRejected(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = {
errorCodesVersionSwitcher.choose(
v1 = invalidArgumentV1(definiteAnswer, message),
v2 = LedgerApiErrors.AdminServices.PackageUploadRejected.Reject(message).asGrpcError,
)
}
): StatusRuntimeException =
LedgerApiErrors.AdminServices.PackageUploadRejected.Reject(message).asGrpcError
@nowarn("msg=deprecated")
def configurationEntryRejected(message: String, definiteAnswer: Option[Boolean])(implicit
def configurationEntryRejected(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = {
errorCodesVersionSwitcher.choose(
v1 = aborted(message, definiteAnswer),
v2 = LedgerApiErrors.AdminServices.ConfigurationEntryRejected.Reject(message).asGrpcError,
)
}
): StatusRuntimeException =
LedgerApiErrors.AdminServices.ConfigurationEntryRejected.Reject(message).asGrpcError
// permission denied is intentionally without description to ensure we don't leak security relevant information by accident
def permissionDenied(cause: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = errorCodesVersionSwitcher.choose(
v1 = {
contextualizedErrorLogger.warn(s"Permission denied. Reason: $cause.")
new ApiException(
io.grpc.Status.PERMISSION_DENIED,
new Metadata(),
)
},
v2 = LedgerApiErrors.AuthorizationChecks.PermissionDenied.Reject(cause).asGrpcError,
)
): StatusRuntimeException =
LedgerApiErrors.AuthorizationChecks.PermissionDenied.Reject(cause).asGrpcError
def unauthenticatedMissingJwtToken()(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = errorCodesVersionSwitcher.choose(
v1 = new ApiException(
io.grpc.Status.UNAUTHENTICATED,
new Metadata(),
),
v2 = LedgerApiErrors.AuthorizationChecks.Unauthenticated
): StatusRuntimeException =
LedgerApiErrors.AuthorizationChecks.Unauthenticated
.MissingJwtToken()
.asGrpcError,
)
.asGrpcError
def internalAuthenticationError(securitySafeMessage: String, exception: Throwable)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
contextualizedErrorLogger.warn(
s"$securitySafeMessage: ${exception.getMessage}"
)
new ApiException(
io.grpc.Status.INTERNAL.withDescription(truncated(securitySafeMessage)),
new Metadata(),
)
},
v2 = LedgerApiErrors.AuthorizationChecks.InternalAuthorizationError
.Reject(securitySafeMessage, exception)
.asGrpcError,
)
LedgerApiErrors.AuthorizationChecks.InternalAuthorizationError
.Reject(securitySafeMessage, exception)
.asGrpcError
/** @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @return An exception with the [[Code.UNAVAILABLE]] status code.
*/
def missingLedgerConfig(
legacyGrpcStatusCode: io.grpc.Status.Code
)(definiteAnswer: Option[Boolean])(implicit
def missingLedgerConfig()(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
val statusBuilder = Status
.newBuilder()
.setCode(legacyGrpcStatusCode.value())
.setMessage("The ledger configuration is not available.")
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
},
v2 = LedgerApiErrors.RequestValidation.NotFound.LedgerConfiguration
.Reject()
.asGrpcError,
)
LedgerApiErrors.RequestValidation.NotFound.LedgerConfiguration
.Reject()
.asGrpcError
def missingLedgerConfig(
v1Status: RpcStatus,
message: String,
)(implicit
def missingLedgerConfig(message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = v1Status,
v2 = GrpcStatus.toProto(
LedgerApiErrors.RequestValidation.NotFound.LedgerConfiguration
.RejectWithMessage(message)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.RequestValidation.NotFound.LedgerConfiguration
.RejectWithMessage(message)
.asGrpcStatusFromContext
)
def participantPrunedDataAccessed(message: String, earliestOffset: Offset)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = grpcError(
Status
.newBuilder()
.setCode(Code.NOT_FOUND.value())
.setMessage(message)
.build()
),
v2 = LedgerApiErrors.RequestValidation.ParticipantPrunedDataAccessed
.Reject(message, earliestOffset.toHexString)
.asGrpcError,
)
LedgerApiErrors.RequestValidation.ParticipantPrunedDataAccessed
.Reject(message, earliestOffset.toHexString)
.asGrpcError
/** @param definiteAnswer A flag that says whether it is a definite answer. Provided only in the context of command deduplication.
* @return An exception with the [[Code.UNAVAILABLE]] status code.
/** @return An exception with the [[Code.UNAVAILABLE]] status code.
*/
def serviceNotRunning(serviceName: String)(definiteAnswer: Option[Boolean])(implicit
def serviceNotRunning(serviceName: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
val statusBuilder = Status
.newBuilder()
.setCode(Code.UNAVAILABLE.value())
.setMessage(s"$serviceName has been shut down.")
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
},
v2 = LedgerApiErrors.ServiceNotRunning.Reject(serviceName).asGrpcError,
)
LedgerApiErrors.ServiceNotRunning.Reject(serviceName).asGrpcError
def trackerFailure(msg: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
errorCodesVersionSwitcher.choose(
v1 = {
val builder = Status
.newBuilder()
.setCode(Code.INTERNAL.value())
.setMessage(msg)
grpcError(builder.build())
},
v2 = LedgerApiErrors.InternalError.Generic(msg).asGrpcError,
)
private def invalidArgumentV1(
definiteAnswer: Option[Boolean],
message: String,
): StatusRuntimeException = {
val statusBuilder = Status
.newBuilder()
.setCode(Code.INVALID_ARGUMENT.value())
.setMessage(s"Invalid argument: $message")
addDefiniteAnswerDetails(definiteAnswer, statusBuilder)
grpcError(statusBuilder.build())
}
LedgerApiErrors.InternalError.Generic(msg).asGrpcError
/** Transforms Protobuf [[Status]] objects, possibly including metadata packed as [[ErrorInfo]] objects,
* into exceptions with metadata in the trailers.
@ -583,30 +317,19 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch
def partyNotKnownOnLedger(reason: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus
.of(Code.INVALID_ARGUMENT.value(), s"Parties not known on ledger: $reason", Seq.empty),
v2 = GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.RejectDeprecated(reason)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.RejectDeprecated(reason)
.asGrpcStatusFromContext
)
def contractsNotFound(missingContractIds: Set[String])(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(
Code.ABORTED.value(),
s"Inconsistent: Could not lookup contracts: ${missingContractIds.mkString("[", ", ", "]")}",
Seq.empty,
),
v2 = GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.ContractNotFound
.MultipleContractsNotFound(missingContractIds)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.ContractNotFound
.MultipleContractsNotFound(missingContractIds)
.asGrpcStatusFromContext
)
def inconsistentContractKeys(
@ -615,111 +338,73 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch
)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(
Code.ABORTED.value(),
s"Inconsistent: Contract key lookup with different results: expected [$lookupResult], actual [$currentResult]",
Seq.empty,
),
v2 = GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InconsistentContractKey
.Reject(
s"Contract key lookup with different results: expected [$lookupResult], actual [$currentResult]"
)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InconsistentContractKey
.Reject(
s"Contract key lookup with different results: expected [$lookupResult], actual [$currentResult]"
)
.asGrpcStatusFromContext
)
def duplicateContractKey(reason: String, key: GlobalKey)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(Code.ABORTED.value(), s"Inconsistent: $reason", Seq.empty),
v2 = GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.DuplicateContractKey
.RejectWithContractKeyArg(reason, key)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.DuplicateContractKey
.RejectWithContractKeyArg(reason, key)
.asGrpcStatusFromContext
)
def partiesNotKnownToLedger(parties: Set[String])(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus
.of(
Code.INVALID_ARGUMENT.value(),
s"Parties not known on ledger: ${parties.mkString("[", ", ", "]")}",
Seq.empty,
),
v2 = GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.Reject(parties)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.Reject(parties)
.asGrpcStatusFromContext
)
def submitterCannotActViaParticipant(reason: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(
Code.PERMISSION_DENIED.value(),
s"Submitted cannot act via participant: $reason",
Seq.empty,
),
v2 = GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.SubmitterCannotActViaParticipant
.Reject(reason)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.SubmitterCannotActViaParticipant
.Reject(reason)
.asGrpcStatusFromContext
)
def invalidLedgerTime(reason: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(Code.ABORTED.value(), s"Invalid ledger time: $reason", Seq.empty),
v2 = GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InvalidLedgerTime
.RejectSimple(reason)
.asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InvalidLedgerTime
.RejectSimple(reason)
.asGrpcStatusFromContext
)
def invalidLedgerTime(
v1Status: RpcStatus,
ledgerTime: Instant,
ledgerTimeLowerBound: Instant,
ledgerTimeUpperBound: Instant,
)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status = {
val details =
s"Ledger time $ledgerTime outside of range [$ledgerTimeLowerBound, $ledgerTimeUpperBound]"
errorCodesVersionSwitcher.choose(
v1 = v1Status,
v2 = GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InvalidLedgerTime
.RejectEnriched(
details,
ledgerTime,
ledgerTimeLowerBound,
ledgerTimeUpperBound,
)
.asGrpcStatusFromContext
),
): com.google.rpc.status.Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InvalidLedgerTime
.RejectEnriched(
s"Ledger time $ledgerTime outside of range [$ledgerTimeLowerBound, $ledgerTimeUpperBound]",
ledgerTime,
ledgerTimeLowerBound,
ledgerTimeUpperBound,
)
.asGrpcStatusFromContext
)
}
def inconsistent(reason: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(Code.ABORTED.value(), s"Inconsistent: $reason", Seq.empty),
v2 = GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.Inconsistent.Reject(reason).asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.Inconsistent.Reject(reason).asGrpcStatusFromContext
)
object Deprecated {
@ -727,39 +412,24 @@ class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitch
def disputed(reason: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(Code.INVALID_ARGUMENT.value(), s"Disputed: $reason", Seq.empty),
v2 = GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Disputed.Reject(reason).asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Disputed.Reject(reason).asGrpcStatusFromContext
)
@deprecated
def outOfQuota(reason: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): com.google.rpc.status.Status =
errorCodesVersionSwitcher.choose(
v1 = RpcStatus.of(Code.ABORTED.value(), s"Resources exhausted: $reason", Seq.empty),
v2 = GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.OutOfQuota.Reject(reason).asGrpcStatusFromContext
),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.OutOfQuota.Reject(reason).asGrpcStatusFromContext
)
}
}
}
object ErrorFactories {
val SelfServiceErrorCodeFactories: ErrorFactories = ErrorFactories(
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = true)
)
def apply(errorCodesVersionSwitcher: ErrorCodesVersionSwitcher): ErrorFactories =
new ErrorFactories(errorCodesVersionSwitcher)
def apply(useSelfServiceErrorCodes: Boolean): ErrorFactories =
new ErrorFactories(
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = useSelfServiceErrorCodes)
)
def apply() = new ErrorFactories
private[daml] lazy val definiteAnswers = Map(
true -> AnyProto.pack[ErrorInfo](

View File

@ -24,18 +24,18 @@ class FieldValidations private (errorFactories: ErrorFactories) {
case None => Right(None)
case Some(`ledgerId`) => Right(Some(ledgerId))
case Some(mismatching) =>
Left(ledgerIdMismatch(ledgerId, mismatching, definiteAnswer = Some(false)))
Left(ledgerIdMismatch(ledgerId, mismatching))
}
def requireNonEmptyString(s: String, fieldName: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, String] =
Either.cond(s.nonEmpty, s, missingField(fieldName, definiteAnswer = Some(false)))
Either.cond(s.nonEmpty, s, missingField(fieldName))
def requireIdentifier(s: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Ref.Name] =
Ref.Name.fromString(s).left.map(invalidArgument(definiteAnswer = Some(false)))
Ref.Name.fromString(s).left.map(invalidArgument)
private def requireNonEmptyParsedId[T](parser: String => Either[String, T])(
s: String,
@ -44,9 +44,9 @@ class FieldValidations private (errorFactories: ErrorFactories) {
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, T] =
if (s.isEmpty)
Left(missingField(fieldName, definiteAnswer = Some(false)))
Left(missingField(fieldName))
else
parser(s).left.map(invalidField(fieldName, _, definiteAnswer = Some(false)))
parser(s).left.map(invalidField(fieldName, _))
def requireName(
s: String,
@ -67,7 +67,7 @@ class FieldValidations private (errorFactories: ErrorFactories) {
def requireParty(s: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Ref.Party] =
Ref.Party.fromString(s).left.map(invalidArgument(definiteAnswer = Some(false)))
Ref.Party.fromString(s).left.map(invalidArgument)
def requireParties(parties: Set[String])(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
@ -107,7 +107,7 @@ class FieldValidations private (errorFactories: ErrorFactories) {
def requireLedgerString(s: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Ref.LedgerString] =
Ref.LedgerString.fromString(s).left.map(invalidArgument(definiteAnswer = Some(false)))
Ref.LedgerString.fromString(s).left.map(invalidArgument)
def validateSubmissionId(s: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
@ -117,7 +117,7 @@ class FieldValidations private (errorFactories: ErrorFactories) {
.fromString(nonEmptyString)
.map(domain.SubmissionId(_))
.left
.map(invalidField("submission_id", _, definiteAnswer = Some(false)))
.map(invalidField("submission_id", _))
}
def requireContractId(
@ -126,8 +126,8 @@ class FieldValidations private (errorFactories: ErrorFactories) {
)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, ContractId] =
if (s.isEmpty) Left(missingField(fieldName, definiteAnswer = Some(false)))
else ContractId.fromString(s).left.map(invalidField(fieldName, _, definiteAnswer = Some(false)))
if (s.isEmpty) Left(missingField(fieldName))
else ContractId.fromString(s).left.map(invalidField(fieldName, _))
def requireDottedName(
s: String,
@ -135,7 +135,7 @@ class FieldValidations private (errorFactories: ErrorFactories) {
)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, Ref.DottedName] =
Ref.DottedName.fromString(s).left.map(invalidField(fieldName, _, definiteAnswer = Some(false)))
Ref.DottedName.fromString(s).left.map(invalidField(fieldName, _))
def requireNonEmpty[M[_] <: Iterable[_], T](
s: M[T],
@ -144,13 +144,13 @@ class FieldValidations private (errorFactories: ErrorFactories) {
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, M[T]] =
if (s.nonEmpty) Right(s)
else Left(missingField(fieldName, definiteAnswer = Some(false)))
else Left(missingField(fieldName))
def requirePresence[T](option: Option[T], fieldName: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): Either[StatusRuntimeException, T] =
option.fold[Either[StatusRuntimeException, T]](
Left(missingField(fieldName, definiteAnswer = Some(false)))
Left(missingField(fieldName))
)(Right(_))
def validateIdentifier(identifier: Identifier)(implicit

View File

@ -30,39 +30,6 @@ trait ValidatorTestUtils extends Matchers with Inside with OptionValues { self:
protected val transactionId = "42"
protected val ledgerEnd = domain.LedgerOffset.Absolute(Ref.LedgerString.assertFromString("1000"))
/** Fixture that facilitates testing validators with and without self-service error codes.
*
* @param testedFactory Creates an instance of a validator to be tested.
* Accepts a boolean to decide if self-service error codes should be enabled.
*/
class ValidatorFixture[T](testedFactory: Boolean => T) {
def testRequestFailure(
testedRequest: T => Either[StatusRuntimeException, _],
expectedCodeV1: Code,
expectedDescriptionV1: String,
expectedCodeV2: Code,
expectedDescriptionV2: String,
metadataV2: Map[String, String] = Map.empty,
): Assertion = {
requestMustFailWith(
request = testedRequest(testedFactory(false)),
code = expectedCodeV1,
description = expectedDescriptionV1,
metadata = Map.empty[String, String],
)
requestMustFailWith(
request = testedRequest(testedFactory(true)),
code = expectedCodeV2,
description = expectedDescriptionV2,
metadataV2,
)
}
def tested(enabledSelfServiceErrorCodes: Boolean): T = {
testedFactory(enabledSelfServiceErrorCodes)
}
}
protected def hasExpectedFilters(req: transaction.GetTransactionsRequest) = {
val filtersByParty = req.filter.filtersByParty
filtersByParty should have size 1

View File

@ -3,7 +3,7 @@
package com.daml.ledger.api
import com.daml.error.{ErrorCodesVersionSwitcher, NoLogging}
import com.daml.error.NoLogging
import com.daml.ledger.api.v1.value.Value.Sum
import com.daml.ledger.api.v1.{value => api}
import com.daml.ledger.api.validation.{ValidatorTestUtils, ValueValidator}
@ -28,8 +28,7 @@ class ValueConversionRoundTripTest
private val constructor: String = "constructor"
private val errorCodesVersionSwitcher = mock[ErrorCodesVersionSwitcher]
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val fieldValidations = FieldValidations(errorFactories)
private val valueValidator = new ValueValidator(errorFactories, fieldValidations)

View File

@ -3,7 +3,7 @@
package com.daml.ledger.api.validation
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher, NoLogging}
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
import com.daml.ledger.api.domain
import com.daml.ledger.api.messages.command.completion.CompletionStreamRequest
import com.daml.ledger.api.v1.command_completion_service.{
@ -37,21 +37,11 @@ class CompletionServiceRequestValidatorTest
private val endReq = CompletionEndRequest(expectedLedgerId)
private val errorCodesVersionSwitcher_mock = mock[ErrorCodesVersionSwitcher]
private val validator = new CompletionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowAllParties,
errorCodesVersionSwitcher = errorCodesVersionSwitcher_mock,
)
val fixture = new ValidatorFixture((selfServiceErrorCodesEnabled: Boolean) => {
new CompletionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowAllParties,
errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(selfServiceErrorCodesEnabled),
)
})
"CompletionRequestValidation" when {
"validating gRPC completion requests" should {
@ -61,36 +51,32 @@ class CompletionServiceRequestValidatorTest
validator.validateGrpcCompletionStreamRequest(grpcCompletionReq.withLedgerId(""))
) { case Right(req) =>
req shouldBe completionReq.copy(ledgerId = None)
verifyZeroInteractions(errorCodesVersionSwitcher_mock)
}
}
"return the correct error on missing application ID" in {
fixture.testRequestFailure(
testedRequest = _.validateGrpcCompletionStreamRequest(
requestMustFailWith(
request = validator.validateGrpcCompletionStreamRequest(
grpcCompletionReq.withApplicationId("")
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: application_id",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: application_id",
metadata = Map.empty,
)
}
"return the correct error on unknown begin boundary" in {
fixture.testRequestFailure(
testedRequest = _.validateGrpcCompletionStreamRequest(
requestMustFailWith(
request = validator.validateGrpcCompletionStreamRequest(
grpcCompletionReq.withOffset(
LedgerOffset(LedgerOffset.Value.Boundary(LedgerBoundary.Unrecognized(7)))
)
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 =
"Invalid argument: Unknown ledger boundary value '7' in field offset.boundary",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown ledger boundary value '7' in field offset.boundary",
metadata = Map.empty,
)
}
@ -99,7 +85,6 @@ class CompletionServiceRequestValidatorTest
validator.validateGrpcCompletionStreamRequest(grpcCompletionReq)
) { case Right(req) =>
req shouldBe completionReq
verifyZeroInteractions(errorCodesVersionSwitcher_mock)
}
}
}
@ -111,28 +96,26 @@ class CompletionServiceRequestValidatorTest
validator.validateCompletionStreamRequest(completionReq.copy(ledgerId = None), ledgerEnd)
) { case Right(req) =>
req shouldBe completionReq.copy(ledgerId = None)
verifyZeroInteractions(errorCodesVersionSwitcher_mock)
}
}
"return the correct error on missing party" in {
fixture.testRequestFailure(
testedRequest = _.validateCompletionStreamRequest(
requestMustFailWith(
request = validator.validateCompletionStreamRequest(
completionReq.copy(parties = Set.empty),
ledgerEnd,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: parties",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: parties",
metadata = Map.empty,
)
}
"return the correct error when offset is after ledger end" in {
fixture.testRequestFailure(
testedRequest = _.validateCompletionStreamRequest(
requestMustFailWith(
request = validator.validateCompletionStreamRequest(
completionReq.copy(offset =
Some(
domain.LedgerOffset.Absolute(
@ -142,11 +125,10 @@ class CompletionServiceRequestValidatorTest
),
ledgerEnd,
),
expectedCodeV1 = OUT_OF_RANGE,
expectedDescriptionV1 = "Begin offset (1001) is after ledger end (1000)",
expectedCodeV2 = OUT_OF_RANGE,
expectedDescriptionV2 =
code = OUT_OF_RANGE,
description =
"OFFSET_AFTER_LEDGER_END(12,0): Begin offset (1001) is after ledger end (1000)",
metadata = Map.empty,
)
}
@ -161,7 +143,6 @@ class CompletionServiceRequestValidatorTest
req.applicationId shouldEqual expectedApplicationId
req.parties shouldEqual Set(party)
req.offset shouldEqual None
verifyZeroInteractions(errorCodesVersionSwitcher_mock)
}
}
}
@ -169,14 +150,13 @@ class CompletionServiceRequestValidatorTest
"validating completions end requests" should {
"fail on ledger ID mismatch" in {
fixture.testRequestFailure(
testedRequest = _.validateCompletionEndRequest(endReq.withLedgerId("mismatchedLedgerId")),
expectedCodeV1 = NOT_FOUND,
expectedDescriptionV1 =
"Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
expectedCodeV2 = NOT_FOUND,
expectedDescriptionV2 =
requestMustFailWith(
request =
validator.validateCompletionEndRequest(endReq.withLedgerId("mismatchedLedgerId")),
code = NOT_FOUND,
description =
"LEDGER_ID_MISMATCH(11,0): Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
metadata = Map.empty,
)
}
@ -184,49 +164,40 @@ class CompletionServiceRequestValidatorTest
inside(
validator.validateCompletionEndRequest(endReq)
) { case Right(_) =>
verifyZeroInteractions(errorCodesVersionSwitcher_mock)
succeed
}
}
}
"applying party name checks" should {
val knowsPartyOnlyFixture = new ValidatorFixture(enabled => {
new CompletionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowPartySet(Set(party)),
new ErrorCodesVersionSwitcher(enabled),
)
})
val partyRestrictiveValidator = new CompletionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowPartySet(Set(party)),
)
val unknownParties = List("party", "Alice", "Bob").map(Ref.Party.assertFromString).toSet
val knownParties = List("party").map(Ref.Party.assertFromString)
"reject completion requests for unknown parties" in {
knowsPartyOnlyFixture.testRequestFailure(
testedRequest = _.validateCompletionStreamRequest(
requestMustFailWith(
request = partyRestrictiveValidator.validateCompletionStreamRequest(
completionReq.copy(parties = unknownParties),
ledgerEnd,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid argument: Unknown parties: [Alice, Bob]",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown parties: [Alice, Bob]",
metadata = Map.empty,
)
}
"accept transaction requests for known parties" in {
knowsPartyOnlyFixture
.tested(enabledSelfServiceErrorCodes = true)
.validateGrpcCompletionStreamRequest(
grpcCompletionReq.withParties(knownParties)
) shouldBe a[Right[_, _]]
knowsPartyOnlyFixture
.tested(enabledSelfServiceErrorCodes = false)
.validateGrpcCompletionStreamRequest(
grpcCompletionReq.withParties(knownParties)
) shouldBe a[Right[_, _]]
partyRestrictiveValidator.validateGrpcCompletionStreamRequest(
grpcCompletionReq.withParties(knownParties)
) shouldBe a[Right[_, _]]
partyRestrictiveValidator.validateGrpcCompletionStreamRequest(
grpcCompletionReq.withParties(knownParties)
) shouldBe a[Right[_, _]]
}
}
}

View File

@ -3,7 +3,7 @@
package com.daml.ledger.api.validation
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher, NoLogging}
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
import com.daml.ledger.api.DomainMocks
import com.daml.ledger.api.v1.value.Identifier
import com.daml.platform.server.api.validation.{ErrorFactories, FieldValidations}
@ -16,10 +16,7 @@ class IdentifierValidatorTest extends AsyncWordSpec with ValidatorTestUtils with
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger = NoLogging
private val errorFactories_mock = mock[ErrorFactories]
private val fieldValidations = FieldValidations(errorFactories_mock)
private val fixture = new ValidatorFixture(enabled =>
FieldValidations(ErrorFactories(new ErrorCodesVersionSwitcher(enabled)))
)
private val fieldValidations = FieldValidations(ErrorFactories())
object api {
val identifier = Identifier("package", moduleName = "module", entityName = "entity")
@ -33,24 +30,23 @@ class IdentifierValidatorTest extends AsyncWordSpec with ValidatorTestUtils with
}
"not allow missing package ids" in {
fixture.testRequestFailure(
_.validateIdentifier(api.identifier.withPackageId("")),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = """Missing field: package_id""",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
fieldValidations.validateIdentifier(api.identifier.withPackageId("")),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: package_id",
metadata = Map.empty,
)
}
"not allow missing names" in {
fixture.testRequestFailure(
_.validateIdentifier(api.identifier.withModuleName("").withEntityName("")),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid field module_name: Expected a non-empty string",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request =
fieldValidations.validateIdentifier(api.identifier.withModuleName("").withEntityName("")),
code = INVALID_ARGUMENT,
description =
"INVALID_FIELD(8,0): The submitted command has a field with invalid value: Invalid field module_name: Expected a non-empty string",
metadata = Map.empty,
)
}
}

View File

@ -6,7 +6,7 @@ package com.daml.ledger.api.validation
import java.time.{Instant, Duration => JDuration}
import com.daml.api.util.{DurationConversion, TimestampConversion}
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher, NoLogging}
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
import com.daml.ledger.api.DomainMocks.{applicationId, commandId, submissionId, workflowId}
import com.daml.ledger.api.domain.{LedgerId, Commands => ApiCommands}
import com.daml.ledger.api.v1.commands.Commands.{DeduplicationPeriod => DeduplicationPeriodProto}
@ -21,7 +21,7 @@ import com.daml.lf.value.{Value => Lf}
import com.daml.platform.server.api.validation.{ErrorFactories, FieldValidations}
import com.google.protobuf.duration.Duration
import com.google.protobuf.empty.Empty
import io.grpc.Status.Code.{INVALID_ARGUMENT, NOT_FOUND, UNAVAILABLE}
import io.grpc.Status.Code.{INVALID_ARGUMENT, NOT_FOUND}
import org.mockito.MockitoSugar
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
@ -132,24 +132,10 @@ class SubmitRequestValidatorTest
private def unexpectedError = sys.error("unexpected error")
private val errorCodesVersionSwitcher_mock = mock[ErrorCodesVersionSwitcher]
private val commandValidatorFixture = new ValidatorFixture(selfServiceErrorCodesEnabled =>
new CommandsValidator(ledgerId, new ErrorCodesVersionSwitcher(selfServiceErrorCodesEnabled))
)
private val valueValidatorFixture = new ValidatorFixture(selfServiceErrorCodesEnabled => {
val errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(selfServiceErrorCodesEnabled)
val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
new ValueValidator(
errorFactories,
FieldValidations(errorFactories),
)
})
private val testedCommandValidator =
new CommandsValidator(ledgerId, errorCodesVersionSwitcher_mock)
new CommandsValidator(ledgerId)
private val testedValueValidator = {
val errorFactories = ErrorFactories(errorCodesVersionSwitcher_mock)
val errorFactories = ErrorFactories()
new ValueValidator(
errorFactories,
FieldValidations(errorFactories),
@ -168,18 +154,17 @@ class SubmitRequestValidatorTest
}
"reject requests with empty commands" in {
commandValidatorFixture.testRequestFailure(
_.validateCommands(
requestMustFailWith(
request = testedCommandValidator.validateCommands(
api.commands.withCommands(Seq.empty),
internal.ledgerTime,
internal.submittedAt,
Some(internal.maxDeduplicationDuration),
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: commands",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: commands",
metadata = Map.empty,
)
}
@ -194,7 +179,6 @@ class SubmitRequestValidatorTest
}
"tolerate a missing workflowId" in {
testedCommandValidator.validateCommands(
api.commands.withWorkflowId(""),
internal.ledgerTime,
@ -209,53 +193,47 @@ class SubmitRequestValidatorTest
}
"not allow missing applicationId" in {
commandValidatorFixture.testRequestFailure(
_.validateCommands(
requestMustFailWith(
request = testedCommandValidator.validateCommands(
api.commands.withApplicationId(""),
internal.ledgerTime,
internal.submittedAt,
Some(internal.maxDeduplicationDuration),
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: application_id",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: application_id",
metadata = Map.empty,
)
}
"not allow missing commandId" in {
commandValidatorFixture.testRequestFailure(
_.validateCommands(
requestMustFailWith(
request = testedCommandValidator.validateCommands(
api.commands.withCommandId(""),
internal.ledgerTime,
internal.submittedAt,
Some(internal.maxDeduplicationDuration),
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: command_id",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: command_id",
metadata = Map.empty,
)
}
"not allow missing submitter" in {
commandValidatorFixture.testRequestFailure(
_.validateCommands(
requestMustFailWith(
request = testedCommandValidator.validateCommands(
api.commands.withParty(""),
internal.ledgerTime,
internal.submittedAt,
Some(internal.maxDeduplicationDuration),
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = """Missing field: party or act_as""",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: party or act_as",
metadata = Map.empty,
)
}
@ -369,18 +347,17 @@ class SubmitRequestValidatorTest
DeduplicationPeriodProto.DeduplicationDuration(Duration.of(-1, 0)),
)
) { deduplication =>
commandValidatorFixture.testRequestFailure(
_.validateCommands(
requestMustFailWith(
testedCommandValidator.validateCommands(
api.commands.copy(deduplicationPeriod = deduplication),
internal.ledgerTime,
internal.submittedAt,
Some(internal.maxDeduplicationDuration),
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid field deduplication_period: Duration must be positive",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_FIELD(8,0): The submitted command has a field with invalid value: Invalid field deduplication_period: Duration must be positive",
metadata = Map.empty,
)
}
}
@ -430,14 +407,13 @@ class SubmitRequestValidatorTest
}
"not allow missing ledger configuration" in {
commandValidatorFixture.testRequestFailure(
_.validateCommands(api.commands, internal.ledgerTime, internal.submittedAt, None),
expectedCodeV1 = UNAVAILABLE,
expectedDescriptionV1 = "The ledger configuration is not available.",
expectedCodeV2 = NOT_FOUND,
expectedDescriptionV2 =
requestMustFailWith(
request = testedCommandValidator
.validateCommands(api.commands, internal.ledgerTime, internal.submittedAt, None),
code = NOT_FOUND,
description =
"LEDGER_CONFIGURATION_NOT_FOUND(11,0): The ledger configuration could not be retrieved.",
metadata = Map.empty,
)
}
}
@ -462,13 +438,12 @@ class SubmitRequestValidatorTest
}
"reject non valid party" in {
valueValidatorFixture.testRequestFailure(
_.validateValue(DomainMocks.values.invalidApiParty),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = DomainMocks.values.invalidPartyMsg,
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(DomainMocks.values.invalidApiParty),
code = INVALID_ARGUMENT,
description =
"""INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: non expected character 0x40 in Daml-LF Party "p@rty"""",
metadata = Map.empty,
)
}
}
@ -518,13 +493,12 @@ class SubmitRequestValidatorTest
forEvery(absoluteValues) { (absoluteValue, _) =>
val s = sign + absoluteValue
val input = Value(Sum.Numeric(s))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = s"""Invalid argument: Could not read Numeric string "$s"""",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
s"""INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Could not read Numeric string "$s"""",
metadata = Map.empty,
)
}
}
@ -550,13 +524,12 @@ class SubmitRequestValidatorTest
forEvery(absoluteValues) { absoluteValue =>
val s = sign + absoluteValue
val input = Value(Sum.Numeric(s))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = s"""Invalid argument: Could not read Numeric string "$s"""",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
s"""INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Could not read Numeric string "$s"""",
metadata = Map.empty,
)
}
}
@ -604,13 +577,12 @@ class SubmitRequestValidatorTest
forEvery(testCases) { long =>
val input = Value(Sum.Timestamp(long))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = s"Invalid argument: out of bound Timestamp $long",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
s"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: out of bound Timestamp $long",
metadata = Map.empty,
)
}
}
@ -643,13 +615,12 @@ class SubmitRequestValidatorTest
forEvery(testCases) { int =>
val input = Value(Sum.Date(int))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = s"Invalid argument: out of bound Date $int",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
s"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: out of bound Date $int",
metadata = Map.empty,
)
}
}
@ -703,13 +674,12 @@ class SubmitRequestValidatorTest
"not allow missing record values" in {
val record =
Value(Sum.Record(Record(Some(api.identifier), Seq(RecordField(api.label, None)))))
valueValidatorFixture.testRequestFailure(
_.validateValue(record),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: value",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(record),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: value",
metadata = Map.empty,
)
}
@ -738,25 +708,23 @@ class SubmitRequestValidatorTest
"not allow missing constructor" in {
val variant = Value(Sum.Variant(Variant(None, "", Some(Value(api.int64)))))
valueValidatorFixture.testRequestFailure(
_.validateValue(variant),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: constructor",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(variant),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: constructor",
metadata = Map.empty,
)
}
"not allow missing values" in {
val variant = Value(Sum.Variant(Variant(None, api.constructor, None)))
valueValidatorFixture.testRequestFailure(
_.validateValue(variant),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: value",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(variant),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: value",
metadata = Map.empty,
)
}
@ -784,13 +752,12 @@ class SubmitRequestValidatorTest
ApiList(Seq(DomainMocks.values.validApiParty, DomainMocks.values.invalidApiParty))
)
)
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = DomainMocks.values.invalidPartyMsg,
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
"""INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: non expected character 0x40 in Daml-LF Party "p@rty"""",
metadata = Map.empty,
)
}
}
@ -811,13 +778,12 @@ class SubmitRequestValidatorTest
"reject optional containing invalid values" in {
val input = Value(Sum.Optional(ApiOptional(Some(DomainMocks.values.invalidApiParty))))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = DomainMocks.values.invalidPartyMsg,
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
"""INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: non expected character 0x40 in Daml-LF Party "p@rty"""",
metadata = Map.empty,
)
}
}
@ -856,14 +822,12 @@ class SubmitRequestValidatorTest
ApiMap.Entry(k, Some(Value(Sum.Int64(v))))
}
val input = Value(Sum.Map(ApiMap(apiEntries.toSeq)))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 =
s"Invalid argument: key ${Utf8.sha256("1")} duplicated when trying to build map",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: key 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b duplicated when trying to build map",
metadata = Map.empty,
)
}
@ -874,13 +838,12 @@ class SubmitRequestValidatorTest
ApiMap.Entry("2", Some(DomainMocks.values.invalidApiParty)),
)
val input = Value(Sum.Map(ApiMap(apiEntries)))
valueValidatorFixture.testRequestFailure(
_.validateValue(input),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = DomainMocks.values.invalidPartyMsg,
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = testedValueValidator.validateValue(input),
code = INVALID_ARGUMENT,
description =
"""INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: non expected character 0x40 in Daml-LF Party "p@rty"""",
metadata = Map.empty,
)
}
}

View File

@ -3,7 +3,7 @@
package com.daml.ledger.api.validation
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher, NoLogging}
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
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
@ -68,27 +68,18 @@ class TransactionServiceRequestValidatorTest
private val txByIdReq =
GetTransactionByIdRequest(expectedLedgerId, transactionId, Seq(party))
private val errorCodesVersionSwitcher_mock = mock[ErrorCodesVersionSwitcher]
private val testedValidator = new TransactionServiceRequestValidator(
private val validator = new TransactionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowAllParties,
ErrorFactories(errorCodesVersionSwitcher_mock),
ErrorFactories(),
)
private val fixture = new ValidatorFixture((selfServiceErrorCodesEnabled: Boolean) => {
new TransactionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowAllParties,
ErrorFactories(new ErrorCodesVersionSwitcher(selfServiceErrorCodesEnabled)),
)
})
"TransactionRequestValidation" when {
"validating regular requests" should {
"accept requests with empty ledger ID" in {
inside(testedValidator.validate(txReq.withLedgerId(""), ledgerEnd)) { case Right(req) =>
inside(validator.validate(txReq.withLedgerId(""), ledgerEnd)) { case Right(req) =>
req.ledgerId shouldEqual None
req.startExclusive shouldEqual domain.LedgerOffset.LedgerBegin
req.endInclusive shouldEqual Some(domain.LedgerOffset.Absolute(absoluteOffset))
@ -101,131 +92,120 @@ class TransactionServiceRequestValidatorTest
}
"return the correct error on missing filter" in {
fixture.testRequestFailure(
_.validate(txReq.update(_.optionalFilter := None), ledgerEnd),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: filter",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
validator.validate(txReq.update(_.optionalFilter := None), ledgerEnd),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: filter",
metadata = Map.empty,
)
}
"return the correct error on empty filter" in {
fixture.testRequestFailure(
_.validate(
requestMustFailWith(
request = validator.validate(
txReq.update(_.filter.filtersByParty := Map.empty),
ledgerEnd,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid argument: filtersByParty cannot be empty",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: filtersByParty cannot be empty",
metadata = Map.empty,
)
}
"return the correct error on missing begin" in {
fixture.testRequestFailure(
_.validate(txReq.update(_.optionalBegin := None), ledgerEnd),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: begin",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validate(txReq.update(_.optionalBegin := None), ledgerEnd),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: begin",
metadata = Map.empty,
)
}
"return the correct error on empty begin " in {
fixture.testRequestFailure(
_.validate(txReq.update(_.begin := LedgerOffset()), ledgerEnd),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: begin.(boundary|value)",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validate(txReq.update(_.begin := LedgerOffset()), ledgerEnd),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: begin.(boundary|value)",
metadata = Map.empty,
)
}
"return the correct error on empty end " in {
fixture.testRequestFailure(
_.validate(txReq.withEnd(LedgerOffset()), ledgerEnd),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: end.(boundary|value)",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validate(txReq.withEnd(LedgerOffset()), ledgerEnd),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: end.(boundary|value)",
metadata = Map.empty,
)
}
"return the correct error on unknown begin boundary" in {
fixture.testRequestFailure(
_.validate(
requestMustFailWith(
request = validator.validate(
txReq.withBegin(
LedgerOffset(LedgerOffset.Value.Boundary(LedgerBoundary.Unrecognized(7)))
),
ledgerEnd,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 =
"Invalid argument: Unknown ledger boundary value '7' in field begin.boundary",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown ledger boundary value '7' in field begin.boundary",
metadata = Map.empty,
)
}
"return the correct error on unknown end boundary" in {
fixture.testRequestFailure(
_.validate(
requestMustFailWith(
request = validator.validate(
txReq.withEnd(
LedgerOffset(LedgerOffset.Value.Boundary(LedgerBoundary.Unrecognized(7)))
),
ledgerEnd,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 =
"Invalid argument: Unknown ledger boundary value '7' in field end.boundary",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown ledger boundary value '7' in field end.boundary",
metadata = Map.empty,
)
}
"return the correct error when begin offset is after ledger end" in {
fixture.testRequestFailure(
_.validate(
requestMustFailWith(
request = validator.validate(
txReq.withBegin(
LedgerOffset(LedgerOffset.Value.Absolute((ledgerEnd.value.toInt + 1).toString))
),
ledgerEnd,
),
expectedCodeV1 = OUT_OF_RANGE,
expectedDescriptionV1 = "Begin offset (1001) is after ledger end (1000)",
expectedCodeV2 = OUT_OF_RANGE,
expectedDescriptionV2 =
code = OUT_OF_RANGE,
description =
"OFFSET_AFTER_LEDGER_END(12,0): Begin offset (1001) is after ledger end (1000)",
metadata = Map.empty,
)
}
"return the correct error when end offset is after ledger end" in {
fixture.testRequestFailure(
_.validate(
requestMustFailWith(
request = validator.validate(
txReq.withEnd(
LedgerOffset(LedgerOffset.Value.Absolute((ledgerEnd.value.toInt + 1).toString))
),
ledgerEnd,
),
expectedCodeV1 = OUT_OF_RANGE,
expectedDescriptionV1 = "End offset (1001) is after ledger end (1000)",
expectedCodeV2 = OUT_OF_RANGE,
expectedDescriptionV2 =
code = OUT_OF_RANGE,
description =
"OFFSET_AFTER_LEDGER_END(12,0): End offset (1001) is after ledger end (1000)",
metadata = Map.empty,
)
}
"tolerate missing end" in {
inside(testedValidator.validate(txReq.update(_.optionalEnd := None), ledgerEnd)) {
inside(validator.validate(txReq.update(_.optionalEnd := None), ledgerEnd)) {
case Right(req) =>
req.ledgerId shouldEqual Some(expectedLedgerId)
req.startExclusive shouldEqual domain.LedgerOffset.LedgerBegin
@ -239,7 +219,7 @@ class TransactionServiceRequestValidatorTest
"tolerate empty filters_inclusive" in {
inside(
testedValidator.validate(
validator.validate(
txReq.update(_.filter.filtersByParty.modify(_.map { case (p, f) =>
p -> f.update(_.inclusive := InclusiveFilters(Nil))
})),
@ -261,7 +241,7 @@ class TransactionServiceRequestValidatorTest
"tolerate missing filters_inclusive" in {
inside(
testedValidator.validate(
validator.validate(
txReq.update(_.filter.filtersByParty.modify(_.map { case (p, f) =>
p -> f.update(_.optionalInclusive := None)
})),
@ -282,7 +262,7 @@ class TransactionServiceRequestValidatorTest
}
"tolerate all fields filled out" in {
inside(testedValidator.validate(txReq, ledgerEnd)) { case Right(req) =>
inside(validator.validate(txReq, ledgerEnd)) { case Right(req) =>
req.ledgerId shouldEqual Some(expectedLedgerId)
req.startExclusive shouldEqual domain.LedgerOffset.LedgerBegin
req.endInclusive shouldEqual Some(domain.LedgerOffset.Absolute(absoluteOffset))
@ -295,7 +275,7 @@ class TransactionServiceRequestValidatorTest
"validating tree requests" should {
"tolerate missing filters_inclusive" in {
inside(testedValidator.validateTree(txTreeReq, ledgerEnd)) { case Right(req) =>
inside(validator.validateTree(txTreeReq, ledgerEnd)) { case Right(req) =>
req.ledgerId shouldEqual Some(expectedLedgerId)
req.startExclusive shouldEqual domain.LedgerOffset.LedgerBegin
req.endInclusive shouldEqual Some(domain.LedgerOffset.Absolute(absoluteOffset))
@ -306,51 +286,47 @@ class TransactionServiceRequestValidatorTest
}
"not tolerate having filters_inclusive" in {
fixture.testRequestFailure(
_.validateTree(
requestMustFailWith(
request = validator.validateTree(
txTreeReq.update(_.filter.filtersByParty.modify(_.map { case (p, f) =>
p -> f.update(_.optionalInclusive := Some(InclusiveFilters()))
})),
ledgerEnd,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 =
"Invalid argument: party attempted subscription for templates []. Template filtration is not supported on GetTransactionTrees RPC. To get filtered data, use the GetTransactions RPC.",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: party attempted subscription for templates []. Template filtration is not supported on GetTransactionTrees RPC. To get filtered data, use the GetTransactions RPC.",
metadata = Map.empty,
)
}
"return the correct error when begin offset is after ledger end" in {
fixture.testRequestFailure(
_.validateTree(
requestMustFailWith(
request = validator.validateTree(
txTreeReq.withBegin(
LedgerOffset(LedgerOffset.Value.Absolute((ledgerEnd.value.toInt + 1).toString))
),
ledgerEnd,
),
expectedCodeV1 = OUT_OF_RANGE,
expectedDescriptionV1 = "Begin offset (1001) is after ledger end (1000)",
expectedCodeV2 = OUT_OF_RANGE,
expectedDescriptionV2 =
code = OUT_OF_RANGE,
description =
"OFFSET_AFTER_LEDGER_END(12,0): Begin offset (1001) is after ledger end (1000)",
metadata = Map.empty,
)
}
"return the correct error when end offset is after ledger end" in {
fixture.testRequestFailure(
_.validateTree(
requestMustFailWith(
request = validator.validateTree(
txTreeReq.withEnd(
LedgerOffset(LedgerOffset.Value.Absolute((ledgerEnd.value.toInt + 1).toString))
),
ledgerEnd,
),
expectedCodeV1 = OUT_OF_RANGE,
expectedDescriptionV1 = "End offset (1001) is after ledger end (1000)",
expectedCodeV2 = OUT_OF_RANGE,
expectedDescriptionV2 =
code = OUT_OF_RANGE,
description =
"OFFSET_AFTER_LEDGER_END(12,0): End offset (1001) is after ledger end (1000)",
metadata = Map.empty,
)
}
}
@ -358,19 +334,17 @@ class TransactionServiceRequestValidatorTest
"validating ledger end requests" should {
"fail on ledger ID mismatch" in {
fixture.testRequestFailure(
_.validateLedgerEnd(endReq.withLedgerId("mismatchedLedgerId")),
expectedCodeV1 = NOT_FOUND,
expectedDescriptionV1 =
"Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
expectedCodeV2 = NOT_FOUND,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validateLedgerEnd(endReq.withLedgerId("mismatchedLedgerId")),
code = NOT_FOUND,
description =
"LEDGER_ID_MISMATCH(11,0): Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
metadata = Map.empty,
)
}
"succeed validating a correct request" in {
inside(testedValidator.validateLedgerEnd(endReq)) { case Right(_) =>
inside(validator.validateLedgerEnd(endReq)) { case Right(_) =>
succeed
}
}
@ -379,41 +353,37 @@ class TransactionServiceRequestValidatorTest
"validating transaction by id requests" should {
"fail on ledger ID mismatch" in {
fixture.testRequestFailure(
_.validateTransactionById(txByIdReq.withLedgerId("mismatchedLedgerId")),
expectedCodeV1 = NOT_FOUND,
expectedDescriptionV1 =
"Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
expectedCodeV2 = NOT_FOUND,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validateTransactionById(txByIdReq.withLedgerId("mismatchedLedgerId")),
code = NOT_FOUND,
description =
"LEDGER_ID_MISMATCH(11,0): Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
metadata = Map.empty,
)
}
"fail on empty transactionId" in {
fixture.testRequestFailure(
_.validateTransactionById(txByIdReq.withTransactionId("")),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: transaction_id",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validateTransactionById(txByIdReq.withTransactionId("")),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: transaction_id",
metadata = Map.empty,
)
}
"fail on empty requesting parties" in {
fixture.testRequestFailure(
_.validateTransactionById(txByIdReq.withRequestingParties(Nil)),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: requesting_parties",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validateTransactionById(txByIdReq.withRequestingParties(Nil)),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: requesting_parties",
metadata = Map.empty,
)
}
"return passed ledger ID" in {
inside(testedValidator.validateTransactionById(txByIdReq)) { case Right(out) =>
inside(validator.validateTransactionById(txByIdReq)) { case Right(out) =>
out should have(Symbol("ledgerId")(Some(expectedLedgerId)))
}
}
@ -423,42 +393,39 @@ class TransactionServiceRequestValidatorTest
"validating transaction by event id requests" should {
"fail on ledger ID mismatch" in {
fixture.testRequestFailure(
_.validateTransactionByEventId(txByEvIdReq.withLedgerId("mismatchedLedgerId")),
expectedCodeV1 = NOT_FOUND,
expectedDescriptionV1 =
"Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
expectedCodeV2 = NOT_FOUND,
expectedDescriptionV2 =
requestMustFailWith(
request =
validator.validateTransactionByEventId(txByEvIdReq.withLedgerId("mismatchedLedgerId")),
code = NOT_FOUND,
description =
"LEDGER_ID_MISMATCH(11,0): Ledger ID 'mismatchedLedgerId' not found. Actual Ledger ID is 'expectedLedgerId'.",
metadata = Map.empty,
)
}
"fail on empty eventId" in {
fixture.testRequestFailure(
_.validateTransactionByEventId(txByEvIdReq.withEventId("")),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: event_id",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validateTransactionByEventId(txByEvIdReq.withEventId("")),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: event_id",
metadata = Map.empty,
)
}
"fail on empty requesting parties" in {
fixture.testRequestFailure(
_.validateTransactionByEventId(txByEvIdReq.withRequestingParties(Nil)),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Missing field: requesting_parties",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = validator.validateTransactionByEventId(txByEvIdReq.withRequestingParties(Nil)),
code = INVALID_ARGUMENT,
description =
"MISSING_FIELD(8,0): The submitted command is missing a mandatory field: requesting_parties",
metadata = Map.empty,
)
}
"return passed ledger ID" in {
inside(
testedValidator.validateTransactionByEventId(txByEvIdReq)
validator.validateTransactionByEventId(txByEvIdReq)
) { case Right(out) =>
out should have(Symbol("ledgerId")(Some(expectedLedgerId)))
}
@ -468,13 +435,11 @@ class TransactionServiceRequestValidatorTest
"applying party name checks" should {
val knowsPartyOnlyFixture = new ValidatorFixture((selfServiceErrorCodesEnabled: Boolean) => {
new TransactionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowPartySet(Set(party)),
ErrorFactories(new ErrorCodesVersionSwitcher(selfServiceErrorCodesEnabled)),
)
})
val partyRestrictiveValidator = new TransactionServiceRequestValidator(
domain.LedgerId(expectedLedgerId),
PartyNameChecker.AllowPartySet(Set(party)),
ErrorFactories(),
)
val partyWithUnknowns = List("party", "Alice", "Bob")
val filterWithUnknown =
@ -483,85 +448,75 @@ class TransactionServiceRequestValidatorTest
TransactionFilter(Map(party -> Filters.defaultInstance))
"reject transaction requests for unknown parties" in {
knowsPartyOnlyFixture.testRequestFailure(
_.validate(txReq.withFilter(filterWithUnknown), ledgerEnd),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid argument: Unknown parties: [Alice, Bob]",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request =
partyRestrictiveValidator.validate(txReq.withFilter(filterWithUnknown), ledgerEnd),
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown parties: [Alice, Bob]",
metadata = Map.empty,
)
}
"reject transaction tree requests for unknown parties" in {
knowsPartyOnlyFixture.testRequestFailure(
_.validateTree(txTreeReq.withFilter(filterWithUnknown), ledgerEnd),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid argument: Unknown parties: [Alice, Bob]",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
requestMustFailWith(
request = partyRestrictiveValidator
.validateTree(txTreeReq.withFilter(filterWithUnknown), ledgerEnd),
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown parties: [Alice, Bob]",
metadata = Map.empty,
)
}
"reject transaction by id requests for unknown parties" in {
knowsPartyOnlyFixture.testRequestFailure(
_.validateTransactionById(
requestMustFailWith(
request = partyRestrictiveValidator.validateTransactionById(
txByIdReq.withRequestingParties(partyWithUnknowns)
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid argument: Unknown parties: [Alice, Bob]",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown parties: [Alice, Bob]",
metadata = Map.empty,
)
}
"reject transaction by event id requests for unknown parties" in {
knowsPartyOnlyFixture.testRequestFailure(
_.validateTransactionById(
requestMustFailWith(
request = partyRestrictiveValidator.validateTransactionById(
txByIdReq.withRequestingParties(partyWithUnknowns)
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 = "Invalid argument: Unknown parties: [Alice, Bob]",
expectedCodeV2 = INVALID_ARGUMENT,
expectedDescriptionV2 =
code = INVALID_ARGUMENT,
description =
"INVALID_ARGUMENT(8,0): The submitted command has invalid arguments: Unknown parties: [Alice, Bob]",
metadata = Map.empty,
)
}
"accept transaction requests for known parties" in {
knowsPartyOnlyFixture
.tested(true)
.validate(
txReq.withFilter(filterWithKnown),
ledgerEnd,
) shouldBe a[Right[_, _]]
partyRestrictiveValidator.validate(
txReq.withFilter(filterWithKnown),
ledgerEnd,
) shouldBe a[Right[_, _]]
}
"accept transaction tree requests for known parties" in {
knowsPartyOnlyFixture
.tested(true)
.validateTree(
txTreeReq.withFilter(filterWithKnown),
ledgerEnd,
) shouldBe a[Right[_, _]]
partyRestrictiveValidator.validateTree(
txTreeReq.withFilter(filterWithKnown),
ledgerEnd,
) shouldBe a[Right[_, _]]
}
"accept transaction by id requests for known parties" in {
knowsPartyOnlyFixture
.tested(true)
.validateTransactionById(
txByIdReq.withRequestingParties(List("party"))
) shouldBe a[Right[_, _]]
partyRestrictiveValidator.validateTransactionById(
txByIdReq.withRequestingParties(List("party"))
) shouldBe a[Right[_, _]]
}
"accept transaction by event id requests for known parties" in {
knowsPartyOnlyFixture
.tested(true)
.validateTransactionById(
txByIdReq.withRequestingParties(List("party"))
) shouldBe a[Right[_, _]]
partyRestrictiveValidator.validateTransactionById(
txByIdReq.withRequestingParties(List("party"))
) shouldBe a[Right[_, _]]
}
}
}

View File

@ -3,7 +3,6 @@
package com.daml.platform.server.api.services.grpc
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.testing.utils.MockMessages._
import com.daml.ledger.api.v1.command_service.CommandServiceGrpc.CommandService
@ -53,7 +52,6 @@ class GrpcCommandServiceSpec
val grpcCommandService = new GrpcCommandService(
mockCommandService,
ledgerId = LedgerId(ledgerId),
errorCodesVersionSwitcher = mock[ErrorCodesVersionSwitcher],
currentLedgerTime = () => Instant.EPOCH,
currentUtcTime = () => Instant.EPOCH,
maxDeduplicationTime = () => Some(Duration.ZERO),

View File

@ -5,7 +5,6 @@ package com.daml.platform.server.api.services.grpc
import java.time.{Duration, Instant}
import com.codahale.metrics.MetricRegistry
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.messages.command.submission.SubmitRequest
import com.daml.ledger.api.testing.utils.MockMessages._
@ -30,7 +29,6 @@ class GrpcCommandSubmissionServiceSpec
with Matchers
with ArgumentMatchersSugar {
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val errorCodesVersionSwitcher_mock = mock[ErrorCodesVersionSwitcher]
private val generatedSubmissionId = "generated-submission-id"
import GrpcCommandSubmissionServiceSpec._
@ -102,7 +100,6 @@ class GrpcCommandSubmissionServiceSpec
maxDeduplicationTime = () => Some(Duration.ZERO),
submissionIdGenerator = () => Ref.SubmissionId.assertFromString(generatedSubmissionId),
metrics = new Metrics(new MetricRegistry),
errorCodesVersionSwitcher = errorCodesVersionSwitcher_mock,
)
}

View File

@ -3,7 +3,6 @@
package com.daml.platform.server.api.services.grpc
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.grpc.GrpcException
import com.daml.grpc.adapter.server.rs.MockServerCallStreamObserver
import com.daml.ledger.api.health._
@ -32,7 +31,7 @@ final class GrpcHealthServiceSpec
"HealthService" should {
"report SERVING if there are no health checks" in {
val service = new GrpcHealthService(new HealthChecks, errorCodesVersionSwitcherMock)
val service = new GrpcHealthService(new HealthChecks)
for {
response <- service.check(HealthCheckRequest())
@ -43,8 +42,7 @@ final class GrpcHealthServiceSpec
"report SERVING if there is one healthy check" in {
val service = new GrpcHealthService(
new HealthChecks("component" -> healthyComponent),
errorCodesVersionSwitcherMock,
new HealthChecks("component" -> healthyComponent)
)
for {
@ -56,8 +54,7 @@ final class GrpcHealthServiceSpec
"report NOT_SERVING if there is one unhealthy check" in {
val service = new GrpcHealthService(
new HealthChecks("component" -> unhealthyComponent),
errorCodesVersionSwitcherMock,
new HealthChecks("component" -> unhealthyComponent)
)
for {
@ -73,8 +70,7 @@ final class GrpcHealthServiceSpec
"component A" -> healthyComponent,
"component B" -> healthyComponent,
"component C" -> healthyComponent,
),
errorCodesVersionSwitcherMock,
)
)
service.check(HealthCheckRequest())
@ -91,8 +87,7 @@ final class GrpcHealthServiceSpec
"component A" -> healthyComponent,
"component B" -> unhealthyComponent,
"component C" -> healthyComponent,
),
errorCodesVersionSwitcherMock,
)
)
for {
@ -104,8 +99,7 @@ final class GrpcHealthServiceSpec
"report SERVING when querying a single, healthy component" in {
val service = new GrpcHealthService(
new HealthChecks("component" -> healthyComponent),
errorCodesVersionSwitcherMock,
new HealthChecks("component" -> healthyComponent)
)
for {
@ -117,8 +111,7 @@ final class GrpcHealthServiceSpec
"report NOT_SERVING when querying a single, unhealthy component" in {
val service = new GrpcHealthService(
new HealthChecks("component" -> unhealthyComponent),
errorCodesVersionSwitcherMock,
new HealthChecks("component" -> unhealthyComponent)
)
for {
@ -134,8 +127,7 @@ final class GrpcHealthServiceSpec
"component A" -> healthyComponent,
"component B" -> healthyComponent,
"component C" -> unhealthyComponent,
),
errorCodesVersionSwitcherMock,
)
)
for {
@ -151,8 +143,7 @@ final class GrpcHealthServiceSpec
"component A" -> unhealthyComponent,
"component B" -> healthyComponent,
"component C" -> healthyComponent,
),
errorCodesVersionSwitcherMock,
)
)
for {
@ -174,7 +165,6 @@ final class GrpcHealthServiceSpec
"component B" -> componentWithHealthBackedBy(() => componentBHealth),
"component C" -> componentWithHealthBackedBy(() => componentCHealth),
),
errorCodesVersionSwitcherMock,
maximumWatchFrequency = 1.millisecond,
)
@ -240,7 +230,6 @@ final class GrpcHealthServiceSpec
"component B" -> componentWithHealthBackedBy(() => componentBHealth),
"component C" -> componentWithHealthBackedBy(() => componentCHealth),
),
errorCodesVersionSwitcherMock,
maximumWatchFrequency = 1.millisecond,
)
@ -292,64 +281,31 @@ final class GrpcHealthServiceSpec
}
}
"fail gracefully when a non-existent component is checked (returns V1 error codes)" in {
failOnNonExistentCheckedComponent(usesSelfServiceErrorCodes = false)
}
"fail gracefully when a non-existent component is checked (returns V2 error codes)" in {
failOnNonExistentCheckedComponent(usesSelfServiceErrorCodes = true)
}
"fail gracefully when a non-existent component is watched (returns V1 error codes)" in {
failOnNonExistentWatchedComponent(usesSelfServiceErrorCodes = false)
}
"fail gracefully when a non-existent component is watched (returns V2 error codes)" in {
failOnNonExistentWatchedComponent(usesSelfServiceErrorCodes = true)
}
private def failOnNonExistentCheckedComponent(usesSelfServiceErrorCodes: Boolean) = {
"fail gracefully when a non-existent component is checked" in {
val service = new GrpcHealthService(
new HealthChecks("component" -> unhealthyComponent),
new ErrorCodesVersionSwitcher(usesSelfServiceErrorCodes),
new HealthChecks("component" -> unhealthyComponent)
)
for {
throwable <- service.check(HealthCheckRequest("another component")).failed
} yield assertErrorCode(usesSelfServiceErrorCodes, throwable)
service.check(HealthCheckRequest("another component")).failed.map(assertErrorCode)
}
private def failOnNonExistentWatchedComponent(usesSelfServiceErrorCodes: Boolean) = {
"fail gracefully when a non-existent component is watched" in {
val responseObserver = new MockServerCallStreamObserver[HealthCheckResponse]
val service = new GrpcHealthService(
new HealthChecks("component" -> unhealthyComponent),
new ErrorCodesVersionSwitcher(usesSelfServiceErrorCodes),
new HealthChecks("component" -> unhealthyComponent)
)
service.watch(HealthCheckRequest("another component"), responseObserver)
responseObserver.demandResponse()
for {
throwable <- responseObserver.completionFuture.failed
} yield assertErrorCode(usesSelfServiceErrorCodes, throwable)
responseObserver.completionFuture.failed.map(assertErrorCode)
}
private def assertErrorCode(usesSelfServiceErrorCodes: Boolean, throwable: Throwable) =
private def assertErrorCode(throwable: Throwable) =
throwable match {
case GrpcException.NOT_FOUND() if !usesSelfServiceErrorCodes => succeed
case GrpcException.INVALID_ARGUMENT() if usesSelfServiceErrorCodes => succeed
case GrpcException.INVALID_ARGUMENT() => succeed
case ex => fail(s"Expected a NOT_FOUND error, but got $ex")
}
// On the happy flow, this parameter is not used.
// Negative tests returning errors should explicitly instantiate it
private def errorCodesVersionSwitcherMock: ErrorCodesVersionSwitcher =
new ErrorCodesVersionSwitcher(false) {
override def choose[X](v1: => X, v2: => X): X = {
val _ = (v1, v2)
fail("Should not be called")
}
}
}
object GrpcHealthServiceSpec {

View File

@ -10,7 +10,7 @@ import com.daml.error.definitions.LedgerApiErrors.RequestValidation.InvalidDedup
import com.daml.error.{ContextualizedErrorLogger, NoLogging}
import com.daml.ledger.api.DeduplicationPeriod.DeduplicationDuration
import com.daml.ledger.api.validation.ValidatorTestUtils
import io.grpc.Status.Code.{FAILED_PRECONDITION, INVALID_ARGUMENT}
import io.grpc.Status.Code.FAILED_PRECONDITION
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
@ -23,28 +23,22 @@ class DeduplicationPeriodValidatorSpec
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger = NoLogging
private val maxDeduplicationDuration = time.Duration.ofSeconds(5)
private val deduplicationValidatorFixture = new ValidatorFixture(selfServiceErrorCodesEnabled =>
new DeduplicationPeriodValidator(ErrorFactories(selfServiceErrorCodesEnabled))
)
private val validator = new DeduplicationPeriodValidator(ErrorFactories())
"not allow deduplication duration exceeding maximum deduplication duration" in {
val durationSecondsExceedingMax = maxDeduplicationDuration.plusSeconds(1).getSeconds
deduplicationValidatorFixture.testRequestFailure(
_.validate(
requestMustFailWith(
request = validator.validate(
DeduplicationDuration(
Duration.ofSeconds(durationSecondsExceedingMax)
),
maxDeduplicationDuration,
),
expectedCodeV1 = INVALID_ARGUMENT,
expectedDescriptionV1 =
s"Invalid field deduplication_period: The given deduplication duration of ${java.time.Duration
.ofSeconds(durationSecondsExceedingMax)} exceeds the maximum deduplication time of ${maxDeduplicationDuration}",
expectedCodeV2 = FAILED_PRECONDITION,
expectedDescriptionV2 =
code = FAILED_PRECONDITION,
description =
s"INVALID_DEDUPLICATION_PERIOD(9,0): The submitted command had an invalid deduplication period: The given deduplication duration of ${java.time.Duration
.ofSeconds(durationSecondsExceedingMax)} exceeds the maximum deduplication time of ${maxDeduplicationDuration}",
metadataV2 = Map(
metadata = Map(
ValidMaxDeduplicationFieldKey -> maxDeduplicationDuration.toString
),
)

View File

@ -64,18 +64,16 @@ object Assertions {
)
}
//TODO error codes daml 2.0: remove this two-level function
/** Asserts GRPC error codes depending on the self-service error codes feature in the Ledger API. */
def assertGrpcError(
t: Throwable,
selfServiceErrorCode: ErrorCode,
errorCode: ErrorCode,
exceptionMessageSubstring: Option[String],
checkDefiniteAnswerMetadata: Boolean = false,
additionalErrorAssertions: Throwable => Unit = _ => (),
): Unit =
assertGrpcErrorRegex(
t,
selfServiceErrorCode,
errorCode,
exceptionMessageSubstring
.map(msgSubstring => Pattern.compile(Pattern.quote(msgSubstring))),
checkDefiniteAnswerMetadata,
@ -90,7 +88,7 @@ object Assertions {
@tailrec
def assertGrpcErrorRegex(
t: Throwable,
selfServiceErrorCode: ErrorCode, //TODO error codes daml 2.0: rename to errorCode
errorCode: ErrorCode,
optPattern: Option[Pattern],
checkDefiniteAnswerMetadata: Boolean = false,
additionalErrorAssertions: Throwable => Unit = _ => (),
@ -99,14 +97,14 @@ object Assertions {
case RetryStrategy.FailedRetryException(cause) =>
assertGrpcErrorRegex(
cause,
selfServiceErrorCode,
errorCode,
optPattern,
checkDefiniteAnswerMetadata,
additionalErrorAssertions,
)
case exception: StatusRuntimeException =>
optPattern.foreach(assertMatches(exception.getMessage, _))
assertSelfServiceErrorCode(exception, selfServiceErrorCode)
assertErrorCode(exception, errorCode)
if (checkDefiniteAnswerMetadata) assertDefiniteAnswer(exception)
additionalErrorAssertions(exception)
case _ =>
@ -156,8 +154,7 @@ object Assertions {
}
}
//TODO error codes daml 2.0: rename this
def assertSelfServiceErrorCode(
def assertErrorCode(
statusRuntimeException: StatusRuntimeException,
expectedErrorCode: ErrorCode,
): Unit = {

View File

@ -669,17 +669,17 @@ final class CommandDeduplicationIT(
private def submitRequestAndAssertSyncFailure(
ledger: ParticipantTestContext,
request: SubmitRequest,
code: Code,
selfServiceErrorCode: ErrorCode,
grpcCode: Code,
errorCode: ErrorCode,
additionalErrorAssertions: Throwable => Unit = _ => (),
)(implicit ec: ExecutionContext): Future[Unit] =
ledger
.submit(request)
.mustFail(s"Request expected to fail with code $code")
.mustFail(s"Request expected to fail with code $grpcCode")
.map(
assertGrpcError(
_,
selfServiceErrorCode,
errorCode,
exceptionMessageSubstring = None,
checkDefiniteAnswerMetadata = true,
additionalErrorAssertions,
@ -698,7 +698,7 @@ final class CommandDeduplicationIT(
.map(
assertGrpcError(
_,
selfServiceErrorCode = LedgerApiErrors.ConsistencyErrors.DuplicateCommand,
errorCode = LedgerApiErrors.ConsistencyErrors.DuplicateCommand,
exceptionMessageSubstring = None,
checkDefiniteAnswerMetadata = true,
assertDeduplicatedSubmissionIdAndOffsetOnError(

View File

@ -200,7 +200,7 @@ class CommandDeduplicationPeriodValidationIT extends LedgerTestSuite {
failure,
// Canton returns INVALID_DEDUPLICATION_PERIOD with earliest_offset metadata
// KV returns PARTICIPANT_PRUNED_DATA_ACCESSED with earliest_offset metadata
selfServiceErrorCode =
errorCode =
if (isOffsetNativelySupported)
LedgerApiErrors.RequestValidation.InvalidDeduplicationPeriodField
else LedgerApiErrors.RequestValidation.ParticipantPrunedDataAccessed,

View File

@ -9,7 +9,7 @@ import com.daml.ledger.api.refinements.ApiTypes.Party
import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions.{
assertGrpcError,
assertSelfServiceErrorCode,
assertErrorCode,
fail,
}
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
@ -128,7 +128,7 @@ final class ContractIdIT extends LedgerTestSuite {
} yield result match {
case Failure(exception: StatusRuntimeException)
if Try(
assertSelfServiceErrorCode(
assertErrorCode(
statusRuntimeException = exception,
expectedErrorCode = LedgerApiErrors.ConsistencyErrors.ContractNotFound,
)

View File

@ -58,7 +58,7 @@ final class UserManagementServiceIT extends LedgerTestSuite {
def assertTooManyUserRightsError(t: Throwable): Unit = {
assertGrpcError(
t = t,
selfServiceErrorCode = LedgerApiErrors.AdminServices.TooManyUserRights,
errorCode = LedgerApiErrors.AdminServices.TooManyUserRights,
exceptionMessageSubstring = None,
)
}
@ -128,7 +128,7 @@ final class UserManagementServiceIT extends LedgerTestSuite {
.mustFail(context = problem)
} yield assertGrpcError(
t = throwable,
selfServiceErrorCode = expectedErrorCode,
errorCode = expectedErrorCode,
exceptionMessageSubstring = None,
)
}
@ -358,12 +358,12 @@ final class UserManagementServiceIT extends LedgerTestSuite {
assertGrpcError(
t = onBadTokenError,
selfServiceErrorCode = LedgerApiErrors.RequestValidation.InvalidArgument,
errorCode = LedgerApiErrors.RequestValidation.InvalidArgument,
exceptionMessageSubstring = None,
)
assertGrpcError(
t = onNegativePageSizeError,
selfServiceErrorCode = LedgerApiErrors.RequestValidation.InvalidArgument,
errorCode = LedgerApiErrors.RequestValidation.InvalidArgument,
exceptionMessageSubstring = None,
)
assert(
@ -525,7 +525,7 @@ final class UserManagementServiceIT extends LedgerTestSuite {
private def assertUserNotFound(t: Throwable): Unit = {
assertGrpcError(
t = t,
selfServiceErrorCode = LedgerApiErrors.AdminServices.UserNotFound,
errorCode = LedgerApiErrors.AdminServices.UserNotFound,
exceptionMessageSubstring = None,
)
}
@ -535,7 +535,7 @@ final class UserManagementServiceIT extends LedgerTestSuite {
): Unit = {
assertGrpcError(
t = t,
selfServiceErrorCode = LedgerApiErrors.AdminServices.UserAlreadyExists,
errorCode = LedgerApiErrors.AdminServices.UserAlreadyExists,
exceptionMessageSubstring = None,
)
}

View File

@ -55,6 +55,6 @@ private[memory] class InMemoryLedgerFactory(dispatcher: Dispatcher[Index], state
state = state,
engine = engine,
committerExecutionContext = materializer.executionContext,
).map(writer => new KeyValueReadWriteFactory(config, metrics, reader, writer))
).map(writer => new KeyValueReadWriteFactory(metrics, reader, writer))
}
}

View File

@ -68,7 +68,6 @@ class InMemoryLedgerReaderWriterIntegrationSpec
KeyValueParticipantStateReader(
reader = reader,
metrics = metrics,
enableSelfServiceErrorCodes = true,
),
new KeyValueParticipantStateWriter(
writer = writer,

View File

@ -50,7 +50,7 @@ object SqlLedgerFactory extends LedgerFactory[ExtraConfig] {
metrics = metrics.daml.kvutils.submission.validator.stateValueCache,
),
).map(ledgerReaderWriter =>
new KeyValueReadWriteFactory(config, metrics, ledgerReaderWriter, ledgerReaderWriter)
new KeyValueReadWriteFactory(metrics, ledgerReaderWriter, ledgerReaderWriter)
)
}
}

View File

@ -45,7 +45,6 @@ abstract class SqlLedgerReaderWriterIntegrationSpecBase(implementationName: Stri
val reader = KeyValueParticipantStateReader(
reader = readerWriter,
metrics = metrics,
enableSelfServiceErrorCodes = true,
)
val writer = new KeyValueParticipantStateWriter(
readerWriter,

View File

@ -40,7 +40,6 @@ final case class Config[Extra](
configurationLoadTimeout: Duration,
commandConfig: CommandConfiguration,
enableInMemoryFanOutForLedgerApi: Boolean,
enableSelfServiceErrorCodes: Boolean,
eventsPageSize: Int,
eventsProcessingParallelism: Int,
extra: Extra,
@ -87,7 +86,6 @@ object Config {
configurationLoadTimeout = Duration.ofSeconds(10),
commandConfig = CommandConfiguration.default,
enableInMemoryFanOutForLedgerApi = false,
enableSelfServiceErrorCodes = true,
eventsPageSize = IndexConfiguration.DefaultEventsPageSize,
eventsProcessingParallelism = IndexConfiguration.DefaultEventsProcessingParallelism,
extra = extra,

View File

@ -82,7 +82,6 @@ trait ConfigProvider[ExtraConfig] {
maxTransactionsInMemoryFanOutBufferSize =
participantConfig.maxTransactionsInMemoryFanOutBufferSize,
enableInMemoryFanOutForLedgerApi = config.enableInMemoryFanOutForLedgerApi,
enableSelfServiceErrorCodes = config.enableSelfServiceErrorCodes,
userManagementConfig = config.userManagementConfig,
)

View File

@ -66,17 +66,6 @@ final class ConfigSpec
)
behavior of "Runner"
it should "enable self-service error codes by default" in {
val actual = configParser(
Seq(
dumpIndexMetadataCommand,
"some-jdbc-url",
)
)
actual.value.enableSelfServiceErrorCodes shouldBe true
}
it should "succeed when server's private key is encrypted and secret-url is provided" in {
val actual = configParser(
Seq(

View File

@ -176,7 +176,6 @@ da_scala_library(
"//ledger-api/rs-grpc-bridge",
"//ledger-api/sample-service",
"//ledger-api/testing-utils",
"//ledger/error",
"//ledger/ledger-api-client",
"//ledger/ledger-api-common",
"//ledger/ledger-api-domain",

View File

@ -42,6 +42,5 @@ case class ApiServerConfig(
maxContractKeyStateCacheSize: Long,
maxTransactionsInMemoryFanOutBufferSize: Long,
enableInMemoryFanOutForLedgerApi: Boolean,
enableSelfServiceErrorCodes: Boolean,
userManagementConfig: UserManagementConfig,
)

View File

@ -5,7 +5,6 @@ package com.daml.platform.apiserver
import akka.stream.Materializer
import com.daml.api.util.TimeProvider
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.auth.Authorizer
import com.daml.ledger.api.auth.services._
@ -86,7 +85,6 @@ private[daml] object ApiServices {
healthChecks: HealthChecks,
seedService: SeedService,
managementServiceTimeout: Duration,
enableSelfServiceErrorCodes: Boolean,
checkOverloaded: TelemetryContext => Option[state.SubmissionResult],
ledgerFeatures: LedgerFeatures,
userManagementConfig: UserManagementConfig,
@ -114,9 +112,6 @@ private[daml] object ApiServices {
servicesExecutionContext = servicesExecutionContext,
)
private val errorsVersionsSwitcher =
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = enableSelfServiceErrorCodes)
override def acquire()(implicit context: ResourceContext): Resource[ApiServices] = {
logger.info(engine.info.toString)
for {
@ -148,30 +143,28 @@ private[daml] object ApiServices {
checkOverloaded: TelemetryContext => Option[state.SubmissionResult],
)(implicit executionContext: ExecutionContext): List[BindableService] = {
val apiTransactionService =
ApiTransactionService.create(ledgerId, transactionsService, metrics, errorsVersionsSwitcher)
ApiTransactionService.create(ledgerId, transactionsService, metrics)
val apiLedgerIdentityService =
ApiLedgerIdentityService.create(() => identityService.getLedgerId(), errorsVersionsSwitcher)
ApiLedgerIdentityService.create(() => identityService.getLedgerId())
val apiVersionService =
ApiVersionService.create(
enableSelfServiceErrorCodes,
ledgerFeatures,
userManagementConfig = userManagementConfig,
)
val apiPackageService =
ApiPackageService.create(ledgerId, packagesService, errorsVersionsSwitcher)
ApiPackageService.create(ledgerId, packagesService)
val apiConfigurationService =
ApiLedgerConfigurationService.create(ledgerId, configurationService, errorsVersionsSwitcher)
ApiLedgerConfigurationService.create(ledgerId, configurationService)
val (completionService, grpcCompletionService) =
ApiCommandCompletionService.create(
ledgerId,
completionsService,
metrics,
errorsVersionsSwitcher,
)
val apiActiveContractsService =
@ -179,13 +172,12 @@ private[daml] object ApiServices {
ledgerId,
activeContractsService,
metrics,
errorsVersionsSwitcher,
)
val apiTimeServiceOpt =
optTimeServiceBackend.map(tsb =>
new TimeServiceAuthorization(
ApiTimeService.create(ledgerId, tsb, errorsVersionsSwitcher),
ApiTimeService.create(ledgerId, tsb),
authorizer,
)
)
@ -200,14 +192,13 @@ private[daml] object ApiServices {
val apiReflectionService = ProtoReflectionService.newInstance()
val apiHealthService = new GrpcHealthService(healthChecks, errorsVersionsSwitcher)
val apiHealthService = new GrpcHealthService(healthChecks)
val maybeApiUserManagementService: Option[UserManagementServiceAuthorization] =
if (userManagementConfig.enabled) {
val apiUserManagementService =
new ApiUserManagementService(
userManagementStore,
errorsVersionsSwitcher,
maxUsersPageSize = userManagementConfig.maxUsersPageSize,
)
val authorized =
@ -218,7 +209,7 @@ private[daml] object ApiServices {
}
val apiMeteringReportService =
new ApiMeteringReportService(participantId, meteringStore, errorsVersionsSwitcher)
new ApiMeteringReportService(participantId, meteringStore)
apiTimeServiceOpt.toList :::
writeServiceBackedApiServices :::
@ -274,7 +265,6 @@ private[daml] object ApiServices {
partyConfig.implicitPartyAllocation
),
metrics,
errorsVersionsSwitcher,
)
// Note: the command service uses the command submission, command completion, and transaction
@ -298,7 +288,6 @@ private[daml] object ApiServices {
timeProvider = timeProvider,
ledgerConfigurationSubscription = ledgerConfigurationSubscription,
metrics = metrics,
errorsVersionsSwitcher,
)
val apiPartyManagementService = ApiPartyManagementService.createApiService(
@ -306,7 +295,6 @@ private[daml] object ApiServices {
transactionsService,
writeService,
managementServiceTimeout,
errorsVersionsSwitcher,
)
val apiPackageManagementService = ApiPackageManagementService.createApiService(
@ -315,21 +303,18 @@ private[daml] object ApiServices {
writeService,
managementServiceTimeout,
engine,
errorsVersionsSwitcher,
)
val apiConfigManagementService = ApiConfigManagementService.createApiService(
configManagementService,
writeService,
timeProvider,
errorsVersionsSwitcher,
)
val apiParticipantPruningService =
ApiParticipantPruningService.createApiService(
indexService,
writeService,
errorsVersionsSwitcher,
)
List(

View File

@ -9,7 +9,6 @@ import akka.actor.ActorSystem
import akka.stream.Materializer
import com.daml.api.util.TimeProvider
import com.daml.buildinfo.BuildInfo
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.auth.interceptor.AuthorizationInterceptor
import com.daml.ledger.api.auth.{AuthService, Authorizer}
import com.daml.ledger.api.health.HealthChecks
@ -75,14 +74,10 @@ object StandaloneApiServer {
}
}
val errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(
config.enableSelfServiceErrorCodes
)
val authorizer = new Authorizer(
Clock.systemUTC.instant _,
ledgerId,
participantId,
errorCodesVersionSwitcher,
userManagementStore,
servicesExecutionContext,
userRightsCheckIntervalInSeconds = userManagementConfig.cacheExpiryAfterWriteInSeconds,
@ -113,7 +108,6 @@ object StandaloneApiServer {
healthChecks = healthChecksWithIndexService,
seedService = SeedService(config.seeding),
managementServiceTimeout = config.managementServiceTimeout,
enableSelfServiceErrorCodes = config.enableSelfServiceErrorCodes,
checkOverloaded = checkOverloaded,
userManagementStore = userManagementStore,
ledgerFeatures = ledgerFeatures,
@ -130,7 +124,6 @@ object StandaloneApiServer {
authService,
Option.when(config.userManagementConfig.enabled)(userManagementStore),
servicesExecutionContext,
errorCodesVersionSwitcher,
) :: otherInterceptors,
servicesExecutionContext,
metrics,

View File

@ -92,7 +92,6 @@ object StandaloneIndexService {
maxContractKeyStateCacheSize = config.maxContractKeyStateCacheSize,
maxTransactionsInMemoryFanOutBufferSize = config.maxTransactionsInMemoryFanOutBufferSize,
enableInMemoryFanOutForLedgerApi = config.enableInMemoryFanOutForLedgerApi,
enableSelfServiceErrorCodes = config.enableSelfServiceErrorCodes,
)
.map(index => new SpannedIndexService(new TimedIndexService(index, metrics)))
} yield indexService

View File

@ -6,11 +6,7 @@ package com.daml.platform.apiserver.services
import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.active_contracts_service.ActiveContractsServiceGrpc.ActiveContractsService
@ -73,15 +69,14 @@ private[apiserver] object ApiActiveContractsService {
ledgerId: LedgerId,
backend: ACSBackend,
metrics: Metrics,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
mat: Materializer,
esf: ExecutionSequencerFactory,
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): ActiveContractsService with GrpcApiService = {
val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
val field = FieldValidations(ErrorFactories(errorCodesVersionSwitcher))
val errorFactories = ErrorFactories()
val field = FieldValidations(ErrorFactories())
val service = new ApiActiveContractsService(
backend = backend,
metrics = metrics,

View File

@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicLong
import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.error.{DamlContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.DamlContextualizedErrorLogger
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.domain.{LedgerId, LedgerOffset}
import com.daml.ledger.api.messages.command.completion.CompletionStreamRequest
@ -89,7 +89,6 @@ private[apiserver] object ApiCommandCompletionService {
ledgerId: LedgerId,
completionsService: IndexCompletionsService,
metrics: Metrics,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
materializer: Materializer,
esf: ExecutionSequencerFactory,
@ -99,7 +98,6 @@ private[apiserver] object ApiCommandCompletionService {
val validator = new CompletionServiceRequestValidator(
ledgerId,
PartyNameChecker.AllowAllParties,
errorCodesVersionSwitcher,
)
val impl: CommandCompletionService =
new ApiCommandCompletionService(completionsService, validator, metrics)

View File

@ -10,11 +10,7 @@ import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.{Flow, Keep, Source}
import com.daml.api.util.TimeProvider
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.SubmissionIdGenerator
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.messages.command.completion.CompletionStreamRequest
@ -61,7 +57,6 @@ import scala.util.Try
private[apiserver] final class ApiCommandService private[services] (
transactionServices: TransactionServices,
submissionTracker: Tracker,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
executionContext: ExecutionContext,
loggingContext: LoggingContext,
@ -70,7 +65,7 @@ private[apiserver] final class ApiCommandService private[services] (
private val logger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
@volatile private var running = true
@ -169,7 +164,7 @@ private[apiserver] final class ApiCommandService private[services] (
} else {
Future
.failed(
errorFactories.serviceNotRunning("Command Service")(definiteAnswer = Some(false))
errorFactories.serviceNotRunning("Command Service")
)
}
@ -215,13 +210,12 @@ private[apiserver] object ApiCommandService {
timeProvider: TimeProvider,
ledgerConfigurationSubscription: LedgerConfigurationSubscription,
metrics: Metrics,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
materializer: Materializer,
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): CommandServiceGrpc.CommandService with GrpcApiService = {
val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
val errorFactories = ErrorFactories()
val ledgerOffsetValidator = new LedgerOffsetValidator(errorFactories)
val submissionTracker = new TrackerMap.SelfCleaning(
configuration.trackerRetentionPeriod,
@ -237,10 +231,8 @@ private[apiserver] object ApiCommandService {
trackerCleanupInterval,
)
new GrpcCommandService(
service =
new ApiCommandService(transactionServices, submissionTracker, errorCodesVersionSwitcher),
service = new ApiCommandService(transactionServices, submissionTracker),
ledgerId = configuration.ledgerId,
errorCodesVersionSwitcher = errorCodesVersionSwitcher,
currentLedgerTime = () => timeProvider.getCurrentTime,
currentUtcTime = () => Instant.now,
maxDeduplicationTime = () =>

View File

@ -7,7 +7,6 @@ import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.api.util.DurationConversion._
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.ledger_configuration_service._
@ -61,14 +60,13 @@ private[apiserver] object ApiLedgerConfigurationService {
def create(
ledgerId: LedgerId,
configurationService: IndexConfigurationService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
esf: ExecutionSequencerFactory,
materializer: Materializer,
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): LedgerConfigurationServiceGrpc.LedgerConfigurationService with GrpcApiService = {
val fieldValidations = FieldValidations(ErrorFactories(errorCodesVersionSwitcher))
val fieldValidations = FieldValidations(ErrorFactories())
new LedgerConfigurationServiceValidation(
service = new ApiLedgerConfigurationService(configurationService),
ledgerId = ledgerId,

View File

@ -3,11 +3,7 @@
package com.daml.platform.apiserver.services
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.ledger_identity_service.LedgerIdentityServiceGrpc.{
LedgerIdentityService => GrpcLedgerIdentityService
@ -27,8 +23,7 @@ import scala.annotation.nowarn
import scala.concurrent.{ExecutionContext, Future}
private[apiserver] final class ApiLedgerIdentityService private (
getLedgerId: () => Future[LedgerId],
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
getLedgerId: () => Future[LedgerId]
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends GrpcLedgerIdentityService
with GrpcApiService {
@ -36,7 +31,7 @@ private[apiserver] final class ApiLedgerIdentityService private (
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
@volatile var closed = false
@ -46,7 +41,7 @@ private[apiserver] final class ApiLedgerIdentityService private (
): Future[GetLedgerIdentityResponse] = {
logger.info(s"Received request for ledger identity: $request")
if (closed)
Future.failed(errorFactories.serviceNotRunning("Ledger Identity Service")(None))
Future.failed(errorFactories.serviceNotRunning("Ledger Identity Service"))
else
getLedgerId()
.map(ledgerId => GetLedgerIdentityResponse(ledgerId.unwrap))
@ -63,12 +58,11 @@ private[apiserver] final class ApiLedgerIdentityService private (
private[apiserver] object ApiLedgerIdentityService {
def create(
getLedgerId: () => Future[LedgerId],
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
getLedgerId: () => Future[LedgerId]
)(implicit
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): ApiLedgerIdentityService with BindableService = {
new ApiLedgerIdentityService(getLedgerId, errorCodesVersionSwitcher)
new ApiLedgerIdentityService(getLedgerId)
}
}

View File

@ -4,7 +4,7 @@
package com.daml.platform.apiserver.services
import com.daml.daml_lf_dev.DamlLf.{Archive, HashFunction}
import com.daml.error.{DamlContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.error.DamlContextualizedErrorLogger
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.package_service.PackageServiceGrpc.PackageService
import com.daml.ledger.api.v1.package_service.{HashFunction => APIHashFunction, _}
@ -24,15 +24,14 @@ import io.grpc.{BindableService, ServerServiceDefinition}
import scala.concurrent.{ExecutionContext, Future}
private[apiserver] final class ApiPackageService private (
backend: IndexPackagesService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
backend: IndexPackagesService
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends PackageService
with GrpcApiService {
private implicit val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
override def bindService(): ServerServiceDefinition =
PackageServiceGrpc.bindService(this, executionContext)
@ -97,7 +96,7 @@ private[apiserver] final class ApiPackageService private (
ValidationLogger.logFailure(
request,
errorFactories
.invalidArgument(Some(true))(s"Invalid package id: $errorMessage")(
.invalidArgument(s"Invalid package id: $errorMessage")(
createContextualizedErrorLogger
),
)
@ -127,16 +126,14 @@ private[platform] object ApiPackageService {
def create(
ledgerId: LedgerId,
backend: IndexPackagesService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): PackageService with GrpcApiService = {
val service = new ApiPackageService(
backend = backend,
errorCodesVersionSwitcher = errorCodesVersionSwitcher,
backend = backend
)
val fieldValidations = FieldValidations(ErrorFactories(errorCodesVersionSwitcher))
val fieldValidations = FieldValidations(ErrorFactories())
new PackageServiceValidation(
service = service,
ledgerId = ledgerId,

View File

@ -9,12 +9,7 @@ import java.util.UUID
import com.daml.api.util.TimeProvider
import com.daml.error.ErrorCode.LoggingApiException
import com.daml.error.definitions.{ErrorCauseExport, RejectionGenerators}
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCause,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger, ErrorCause}
import com.daml.ledger.api.domain.{LedgerId, SubmissionId, Commands => ApiCommands}
import com.daml.ledger.api.messages.command.submission.SubmitRequest
import com.daml.ledger.api.SubmissionIdGenerator
@ -23,8 +18,6 @@ import com.daml.ledger.participant.state.index.v2._
import com.daml.ledger.participant.state.{v2 => state}
import com.daml.lf.crypto
import com.daml.lf.data.Ref
import com.daml.lf.engine.{Error => LfError}
import com.daml.lf.interpretation.{Error => InterpretationError}
import com.daml.lf.transaction.SubmittedTransaction
import com.daml.logging.LoggingContext.withEnrichedLoggingContext
import com.daml.logging.{ContextualizedLogger, LoggingContext}
@ -40,9 +33,7 @@ import com.daml.platform.services.time.TimeProviderType
import com.daml.scalautil.future.FutureConversion.CompletionStageConversionOps
import com.daml.telemetry.TelemetryContext
import com.daml.timer.Delayed
import io.grpc.{Status, StatusRuntimeException}
import scala.annotation.nowarn
import scala.jdk.FutureConverters.CompletionStageOps
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}
@ -61,7 +52,6 @@ private[apiserver] object ApiSubmissionService {
checkOverloaded: TelemetryContext => Option[state.SubmissionResult],
configuration: ApiSubmissionService.Configuration,
metrics: Metrics,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
executionContext: ExecutionContext,
loggingContext: LoggingContext,
@ -78,7 +68,6 @@ private[apiserver] object ApiSubmissionService {
checkOverloaded,
configuration,
metrics,
errorCodesVersionSwitcher,
),
ledgerId = ledgerId,
currentLedgerTime = () => timeProvider.getCurrentTime,
@ -87,7 +76,6 @@ private[apiserver] object ApiSubmissionService {
ledgerConfigurationSubscription.latestConfiguration().map(_.maxDeduplicationTime),
submissionIdGenerator = SubmissionIdGenerator.Random,
metrics = metrics,
errorCodesVersionSwitcher = errorCodesVersionSwitcher,
)
final case class Configuration(
@ -107,13 +95,12 @@ private[apiserver] final class ApiSubmissionService private[services] (
checkOverloaded: TelemetryContext => Option[state.SubmissionResult],
configuration: ApiSubmissionService.Configuration,
metrics: Metrics,
val errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends CommandSubmissionService
with AutoCloseable {
private val logger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
override def submit(
request: SubmitRequest
@ -136,9 +123,7 @@ private[apiserver] final class ApiSubmissionService private[services] (
.transform(handleSubmissionResult)
case None =>
Future.failed(
errorFactories.missingLedgerConfig(Status.Code.UNAVAILABLE)(definiteAnswer =
Some(false)
)
errorFactories.missingLedgerConfig()
)
}
evaluatedCommand.andThen(logger.logErrorsOnCall[Unit])
@ -278,41 +263,12 @@ private[apiserver] final class ApiSubmissionService private[services] (
.toScalaUnwrapped
}
/** This method encodes logic related to legacy error codes (V1).
* Cf. self-service error codes (V2) in //ledger/error
*/
@nowarn("msg=deprecated")
private def toStatusExceptionV1(
errorCause: ErrorCause
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger): StatusRuntimeException = {
// Explicitly instantiate here a V1-enabled ErrorFactories to support the legacy error code dispatching logic.
val v1ErrorFactories = ErrorFactories(new ErrorCodesVersionSwitcher(false))
errorCause match {
case cause @ ErrorCause.DamlLf(error) =>
error match {
case LfError.Interpretation(
LfError.Interpretation.DamlException(
InterpretationError.ContractNotFound(_) |
InterpretationError.DuplicateContractKey(_)
),
_,
) | LfError.Validation(LfError.Validation.ReplayMismatch(_)) =>
v1ErrorFactories.aborted(cause.explain, definiteAnswer = Some(false))
case _ =>
v1ErrorFactories.invalidArgument(definiteAnswer = Some(false))(cause.explain)
}
case cause: ErrorCause.LedgerTime =>
v1ErrorFactories.aborted(cause.explain, definiteAnswer = Some(false))
}
}
private def failedOnCommandExecution(
error: ErrorCause
)(implicit contextualizedErrorLogger: ContextualizedErrorLogger): Future[CommandExecutionResult] =
errorCodesVersionSwitcher.chooseAsFailedFuture(
v1 = toStatusExceptionV1(error),
v2 = RejectionGenerators
.commandExecutorError(cause = ErrorCauseExport.fromErrorCause(error)),
Future.failed(
RejectionGenerators
.commandExecutorError(cause = ErrorCauseExport.fromErrorCause(error))
)
override def close(): Unit = ()

View File

@ -7,11 +7,7 @@ import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.api.util.TimestampConversion._
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.testing.time_service.TimeServiceGrpc.TimeService
@ -33,7 +29,6 @@ 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,
@ -46,7 +41,7 @@ private[apiserver] final class ApiTimeService private (
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val fieldValidations = FieldValidations(errorFactories)
private val dispatcher = SignalDispatcher[Instant]()
@ -97,7 +92,7 @@ private[apiserver] final class ApiTimeService private (
if (success) Right(requestedTime)
else
Left(
invalidArgument(None)(
invalidArgument(
s"current_time mismatch. Provided: $expectedTime. Actual: ${backend.getCurrentTime}"
)
)
@ -115,7 +110,7 @@ private[apiserver] final class ApiTimeService private (
Right(())
else
Left(
invalidArgument(None)(
invalidArgument(
s"new_time [$requestedTime] is before current_time [$expectedTime]. Setting time backwards is not allowed."
)
)
@ -153,12 +148,11 @@ private[apiserver] object ApiTimeService {
def create(
ledgerId: LedgerId,
backend: TimeServiceBackend,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
mat: Materializer,
esf: ExecutionSequencerFactory,
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): TimeService with GrpcApiService =
new ApiTimeService(ledgerId, backend, errorCodesVersionSwitcher)
new ApiTimeService(ledgerId, backend)
}

View File

@ -3,11 +3,7 @@
package com.daml.platform.apiserver.services
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.v1.experimental_features.{
ExperimentalFeatures,
ExperimentalOptionalLedgerId,
@ -36,7 +32,6 @@ import scala.util.Try
import scala.util.control.NonFatal
private[apiserver] final class ApiVersionService private (
enableSelfServiceErrorCodes: Boolean,
ledgerFeatures: LedgerFeatures,
userManagementConfig: UserManagementConfig,
)(implicit
@ -45,9 +40,8 @@ private[apiserver] final class ApiVersionService private (
) extends VersionService
with GrpcApiService {
private val errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes)
private val logger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
@ -73,10 +67,9 @@ private[apiserver] final class ApiVersionService private (
),
experimental = Some(
ExperimentalFeatures.of(
selfServiceErrorCodes =
Option.when(enableSelfServiceErrorCodes)(ExperimentalSelfServiceErrorCodes()): @nowarn(
"cat=deprecation&origin=com\\.daml\\.ledger\\.api\\.v1\\.experimental_features\\..*"
),
selfServiceErrorCodes = Some(ExperimentalSelfServiceErrorCodes()): @nowarn(
"cat=deprecation&origin=com\\.daml\\.ledger\\.api\\.v1\\.experimental_features\\..*"
),
staticTime = Some(ExperimentalStaticTime(supported = ledgerFeatures.staticTime)),
commandDeduplication = Some(ledgerFeatures.commandDeduplicationFeatures),
optionalLedgerId = Some(ExperimentalOptionalLedgerId()),
@ -122,12 +115,10 @@ private[apiserver] final class ApiVersionService private (
private[apiserver] object ApiVersionService {
def create(
enableSelfServiceErrorCodes: Boolean,
ledgerFeatures: LedgerFeatures,
userManagementConfig: UserManagementConfig,
)(implicit loggingContext: LoggingContext, ec: ExecutionContext): ApiVersionService =
new ApiVersionService(
enableSelfServiceErrorCodes,
ledgerFeatures,
userManagementConfig = userManagementConfig,
)

View File

@ -7,11 +7,7 @@ import java.time.{Duration => JDuration}
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.api.util.{DurationConversion, TimeProvider, TimestampConversion}
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.domain
import com.daml.ledger.api.domain.{ConfigurationEntry, LedgerOffset}
import com.daml.ledger.api.v1.admin.config_management_service.ConfigManagementServiceGrpc.ConfigManagementService
@ -28,7 +24,7 @@ import com.daml.platform.apiserver.services.logging
import com.daml.platform.server.api.ValidationLogger
import com.daml.platform.server.api.validation.{ErrorFactories, FieldValidations}
import com.daml.telemetry.{DefaultTelemetry, TelemetryContext}
import io.grpc.{ServerServiceDefinition, Status, StatusRuntimeException}
import io.grpc.{ServerServiceDefinition, StatusRuntimeException}
import scala.jdk.FutureConverters.CompletionStageOps
import scala.concurrent.duration.{Duration, FiniteDuration}
@ -40,7 +36,6 @@ private[apiserver] final class ApiConfigManagementService private (
writeService: state.WriteConfigService,
timeProvider: TimeProvider,
submissionIdGenerator: String => Ref.SubmissionId,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
materializer: Materializer,
executionContext: ExecutionContext,
@ -48,7 +43,7 @@ private[apiserver] final class ApiConfigManagementService private (
) extends ConfigManagementService
with GrpcApiService {
private implicit val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val fieldValidations = FieldValidations(errorFactories)
import errorFactories._
@ -67,7 +62,7 @@ private[apiserver] final class ApiConfigManagementService private (
Future.successful(configurationToResponse(configuration))
case None =>
Future.failed(
missingLedgerConfig(Status.Code.NOT_FOUND)(Some(true))(
missingLedgerConfig()(
new DamlContextualizedErrorLogger(logger, loggingContext, None)
)
)
@ -116,7 +111,7 @@ private[apiserver] final class ApiConfigManagementService private (
logger.warn(
"Could not get the current time model. The index does not yet have any ledger configuration."
)
Future.failed(missingLedgerConfig(Status.Code.UNAVAILABLE)(None))
Future.failed(missingLedgerConfig())
}
(ledgerEndBeforeRequest, currentConfig) = configuration
@ -127,7 +122,7 @@ private[apiserver] final class ApiConfigManagementService private (
Future.failed(
ValidationLogger.logFailure(
request,
invalidArgument(None)(
invalidArgument(
s"Mismatching configuration generation, expected $expectedGeneration, received ${request.configurationGeneration}"
),
)
@ -188,7 +183,7 @@ private[apiserver] final class ApiConfigManagementService private (
minSkew = DurationConversion.fromProto(pMinSkew),
maxSkew = DurationConversion.fromProto(pMaxSkew),
) match {
case Failure(err) => Left(invalidArgument(None)(err.toString))
case Failure(err) => Left(invalidArgument(err.toString))
case Success(ok) => Right(ok)
}
// TODO(JM): The maximum record time should be constrained, probably by the current active time model?
@ -201,7 +196,7 @@ private[apiserver] final class ApiConfigManagementService private (
}
maximumRecordTime <- Time.Timestamp
.fromInstant(mrtInstant)
.fold(err => Left(invalidArgument(None)(err)), Right(_))
.fold(err => Left(invalidArgument(err)), Right(_))
} yield SetTimeModelParameters(newTimeModel, maximumRecordTime, timeToLive)
}
@ -213,7 +208,6 @@ private[apiserver] object ApiConfigManagementService {
readBackend: IndexConfigManagementService,
writeBackend: state.WriteConfigService,
timeProvider: TimeProvider,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
submissionIdGenerator: String => Ref.SubmissionId = augmentSubmissionId,
)(implicit
materializer: Materializer,
@ -225,7 +219,6 @@ private[apiserver] object ApiConfigManagementService {
writeBackend,
timeProvider,
submissionIdGenerator,
errorCodesVersionSwitcher,
)
private final class SynchronousResponseStrategy(
@ -269,7 +262,7 @@ private[apiserver] object ApiConfigManagementService {
submissionId: Ref.SubmissionId
): PartialFunction[ConfigurationEntry, StatusRuntimeException] = {
case domain.ConfigurationEntry.Rejected(`submissionId`, reason, _) =>
errorFactories.configurationEntryRejected(reason, None)(
errorFactories.configurationEntryRejected(reason)(
new DamlContextualizedErrorLogger(logger, loggingContext, Some(submissionId))
)
}

View File

@ -3,11 +3,7 @@
package com.daml.platform.apiserver.services.admin
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.v1.admin.metering_report_service.MeteringReportServiceGrpc.MeteringReportService
import com.daml.ledger.api.v1.admin.metering_report_service._
import com.daml.ledger.participant.state.index.v2.MeteringStore
@ -29,7 +25,6 @@ import scala.util.chaining.scalaUtilChainingOps
private[apiserver] final class ApiMeteringReportService(
participantId: Ref.ParticipantId,
store: MeteringStore,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
clock: () => ProtoTimestamp = () => toProtoTimestamp(Timestamp.now()),
)(implicit
executionContext: ExecutionContext,
@ -42,7 +37,7 @@ private[apiserver] final class ApiMeteringReportService(
new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val generator = new MeteringReportGenerator(participantId)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
override def bindService(): ServerServiceDefinition =
MeteringReportServiceGrpc.bindService(this, executionContext)
@ -73,7 +68,7 @@ private[apiserver] final class ApiMeteringReportService(
case Right(f) => f
case Left(error) =>
Future.failed(
ValidationLogger.logFailure(request, errorFactories.invalidArgument(None)(error))
ValidationLogger.logFailure(request, errorFactories.invalidArgument(error))
)
}
}

View File

@ -11,11 +11,7 @@ import com.daml.api.util.TimestampConversion
import com.daml.daml_lf_dev.DamlLf.Archive
import com.daml.error.definitions.LoggingPackageServiceError
import com.daml.error.definitions.PackageServiceError.Validation
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.domain.{LedgerOffset, PackageEntry}
import com.daml.ledger.api.v1.admin.package_management_service.PackageManagementServiceGrpc.PackageManagementService
import com.daml.ledger.api.v1.admin.package_management_service._
@ -52,7 +48,6 @@ private[apiserver] final class ApiPackageManagementService private (
engine: Engine,
darReader: GenDarReader[Archive],
submissionIdGenerator: String => Ref.SubmissionId,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
materializer: Materializer,
executionContext: ExecutionContext,
@ -62,7 +57,7 @@ private[apiserver] final class ApiPackageManagementService private (
private implicit val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val synchronousResponse = new SynchronousResponse(
new SynchronousResponseStrategy(
transactionsService,
@ -160,7 +155,6 @@ private[apiserver] object ApiPackageManagementService {
writeBackend: state.WritePackagesService,
managementServiceTimeout: Duration,
engine: Engine,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
darReader: GenDarReader[Archive] = DarParser,
submissionIdGenerator: String => Ref.SubmissionId = augmentSubmissionId,
)(implicit
@ -176,7 +170,6 @@ private[apiserver] object ApiPackageManagementService {
engine,
darReader,
submissionIdGenerator,
errorCodesVersionSwitcher,
)
private final class SynchronousResponseStrategy(
@ -213,7 +206,7 @@ private[apiserver] object ApiPackageManagementService {
submissionId: Ref.SubmissionId
): PartialFunction[PackageEntry, StatusRuntimeException] = {
case PackageEntry.PackageUploadRejected(`submissionId`, _, reason) =>
errorFactories.packageUploadRejected(reason, definiteAnswer = None)(
errorFactories.packageUploadRejected(reason)(
new DamlContextualizedErrorLogger(logger, loggingContext, Some(submissionId))
)
}

View File

@ -3,11 +3,7 @@
package com.daml.platform.apiserver.services.admin
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import java.util.UUID
import com.daml.ledger.api.v1.admin.participant_pruning_service.{
@ -34,13 +30,12 @@ import scala.concurrent.{ExecutionContext, Future}
final class ApiParticipantPruningService private (
readBackend: IndexParticipantPruningService with LedgerEndService,
writeBackend: state.WriteParticipantPruningService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends ParticipantPruningServiceGrpc.ParticipantPruningService
with GrpcApiService {
private implicit val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
import errorFactories._
@ -56,7 +51,7 @@ final class ApiParticipantPruningService private (
)
.left
.map(err =>
invalidArgument(None)(s"submission_id $err")(
invalidArgument(s"submission_id $err")(
contextualizedErrorLogger(request.submissionId)
)
)
@ -143,7 +138,7 @@ final class ApiParticipantPruningService private (
Either.cond(
offset.nonEmpty,
offset,
invalidArgument(None)("prune_up_to not specified"),
invalidArgument("prune_up_to not specified"),
)
private def checkOffsetIsHexadecimal(
@ -154,7 +149,7 @@ final class ApiParticipantPruningService private (
.toEither
.left
.map(t =>
nonHexOffset(None)(
nonHexOffset(
fieldName = "prune_up_to",
offsetValue = pruneUpToString,
message =
@ -177,7 +172,7 @@ final class ApiParticipantPruningService private (
Future.failed(
// TODO error codes: Relax the constraint (pruneUpToString <= ledgerEnd.value)
// and use offsetAfterLedgerEnd
offsetOutOfRange(None)(
offsetOutOfRange(
s"prune_up_to needs to be before ledger end ${ledgerEnd.value}"
)
)
@ -193,11 +188,10 @@ object ApiParticipantPruningService {
def createApiService(
readBackend: IndexParticipantPruningService with LedgerEndService,
writeBackend: state.WriteParticipantPruningService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): ParticipantPruningServiceGrpc.ParticipantPruningService with GrpcApiService =
new ApiParticipantPruningService(readBackend, writeBackend, errorCodesVersionSwitcher)
new ApiParticipantPruningService(readBackend, writeBackend)
}

View File

@ -7,11 +7,7 @@ import java.time.Duration
import java.util.UUID
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.domain.{LedgerOffset, PartyEntry}
import com.daml.ledger.api.v1.admin.party_management_service.PartyManagementServiceGrpc.PartyManagementService
import com.daml.ledger.api.v1.admin.party_management_service._
@ -40,7 +36,6 @@ private[apiserver] final class ApiPartyManagementService private (
transactionService: IndexTransactionsService,
writeService: state.WritePartyService,
managementServiceTimeout: Duration,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
submissionIdGenerator: Option[Ref.Party] => Ref.SubmissionId,
)(implicit
materializer: Materializer,
@ -53,7 +48,7 @@ private[apiserver] final class ApiPartyManagementService private (
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private val synchronousResponse = new SynchronousResponse(
new SynchronousResponseStrategy(
transactionService,
@ -115,7 +110,7 @@ private[apiserver] final class ApiPartyManagementService private (
error =>
Future.failed(
ValidationLogger
.logFailure(request, errorFactories.invalidArgument(None)(error))
.logFailure(request, errorFactories.invalidArgument(error))
),
party => Future.successful(Some(party)),
)
@ -159,7 +154,6 @@ private[apiserver] object ApiPartyManagementService {
transactionsService: IndexTransactionsService,
writeBackend: state.WritePartyService,
managementServiceTimeout: Duration,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
submissionIdGenerator: Option[Ref.Party] => Ref.SubmissionId = CreateSubmissionId.withPrefix,
)(implicit
materializer: Materializer,
@ -171,7 +165,6 @@ private[apiserver] object ApiPartyManagementService {
transactionsService,
writeBackend,
managementServiceTimeout,
errorCodesVersionSwitcher,
submissionIdGenerator,
)
@ -225,7 +218,7 @@ private[apiserver] object ApiPartyManagementService {
submissionId: Ref.SubmissionId
): PartialFunction[PartyEntry, StatusRuntimeException] = {
case PartyEntry.AllocationRejected(`submissionId`, reason) =>
errorFactories.invalidArgument(None)(reason)(
errorFactories.invalidArgument(reason)(
new DamlContextualizedErrorLogger(logger, loggingContext, Some(submissionId))
)
}

View File

@ -7,11 +7,7 @@ import java.nio.charset.StandardCharsets
import java.util.Base64
import com.daml.error.definitions.LedgerApiErrors
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.api.domain._
import com.daml.ledger.api.v1.admin.user_management_service.{CreateUserResponse, GetUserResponse}
import com.daml.ledger.api.v1.admin.{user_management_service => proto}
@ -32,7 +28,6 @@ import scala.util.Try
private[apiserver] final class ApiUserManagementService(
userManagementStore: UserManagementStore,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
maxUsersPageSize: Int,
)(implicit
executionContext: ExecutionContext,
@ -43,7 +38,7 @@ private[apiserver] final class ApiUserManagementService(
import ApiUserManagementService._
private implicit val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
private implicit val contextualizedErrorLogger: ContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
private val fieldValidations = FieldValidations(errorFactories)

View File

@ -6,11 +6,7 @@ package com.daml.platform.apiserver.services.transaction
import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.error.{
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.daml.ledger.api.domain.{
Filters,
@ -48,7 +44,6 @@ private[apiserver] object ApiTransactionService {
ledgerId: LedgerId,
transactionsService: IndexTransactionsService,
metrics: Metrics,
errorsVersionsSwitcher: ErrorCodesVersionSwitcher,
)(implicit
ec: ExecutionContext,
mat: Materializer,
@ -56,22 +51,21 @@ private[apiserver] object ApiTransactionService {
loggingContext: LoggingContext,
): GrpcTransactionService with BindableService =
new GrpcTransactionService(
new ApiTransactionService(transactionsService, metrics, errorsVersionsSwitcher),
new ApiTransactionService(transactionsService, metrics),
ledgerId,
PartyNameChecker.AllowAllParties,
ErrorFactories(errorsVersionsSwitcher),
ErrorFactories(),
)
}
private[apiserver] final class ApiTransactionService private (
transactionsService: IndexTransactionsService,
metrics: Metrics,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends TransactionService {
private val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)
private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)
private val errorFactories = ErrorFactories()
import errorFactories.transactionNotFound
import errorFactories.invalidArgumentWasNotFound
@ -146,7 +140,7 @@ private[apiserver] final class ApiTransactionService private (
}
.getOrElse {
Future.failed {
invalidArgumentWasNotFound(None)(s"invalid eventId: ${request.eventId}")
invalidArgumentWasNotFound(s"invalid eventId: ${request.eventId}")
}
}
.andThen(logger.logErrorsOnCall[GetTransactionResponse])
@ -189,7 +183,7 @@ private[apiserver] final class ApiTransactionService private (
}
.getOrElse {
val msg = s"eventId: ${request.eventId}"
Future.failed(invalidArgumentWasNotFound(None)(msg))
Future.failed(invalidArgumentWasNotFound(msg))
}
.andThen(logger.logErrorsOnCall[GetFlatTransactionResponse])
}

View File

@ -4,7 +4,6 @@
package com.daml.platform.index
import akka.stream.Materializer
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.participant.state.index.v2.IndexService
import com.daml.ledger.resources.ResourceOwner
@ -37,7 +36,6 @@ private[platform] object JdbcIndex {
maxContractKeyStateCacheSize: Long,
maxTransactionsInMemoryFanOutBufferSize: Long,
enableInMemoryFanOutForLedgerApi: Boolean,
enableSelfServiceErrorCodes: Boolean,
)(implicit mat: Materializer, loggingContext: LoggingContext): ResourceOwner[IndexService] =
new ReadOnlySqlLedger.Owner(
dbSupport = dbSupport,
@ -58,12 +56,12 @@ private[platform] object JdbcIndex {
enableInMemoryFanOutForLedgerApi = enableInMemoryFanOutForLedgerApi,
participantId = participantId,
maxTransactionsInMemoryFanOutBufferSize = maxTransactionsInMemoryFanOutBufferSize,
errorFactories = ErrorFactories(new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes)),
errorFactories = ErrorFactories(),
).map { ledger =>
new LedgerBackedIndexService(
MeteredReadOnlyLedger(ledger, metrics),
participantId,
errorFactories = ErrorFactories(new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes)),
errorFactories = ErrorFactories(),
)
}
}

View File

@ -141,7 +141,7 @@ private[platform] final class LedgerBackedIndexService(
Source.empty
case Some(end) if begin > end =>
Source.failed(
errorFactories.offsetOutOfRange(None)(
errorFactories.offsetOutOfRange(
s"End offset ${end.toApiString} is before Begin offset ${begin.toApiString}."
)(new DamlContextualizedErrorLogger(logger, loggingContext, None))
)

View File

@ -411,7 +411,7 @@ private class JdbcLedgerDao(
conn,
)
) {
throw errorFactories.offsetOutOfRange(None)(
throw errorFactories.offsetOutOfRange(
"Pruning offset for all divulged contracts needs to be after the migration offset"
)(new DamlContextualizedErrorLogger(logger, loggingContext, None))
}

View File

@ -4,7 +4,6 @@
package com.daml.platform.store.dao
import com.codahale.metrics.MetricRegistry
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.domain.{LedgerId, ParticipantId}
import com.daml.ledger.api.testing.utils.AkkaBeforeAndAfterAll
import com.daml.ledger.resources.{Resource, ResourceContext, ResourceOwner}
@ -109,9 +108,7 @@ private[dao] trait JdbcLedgerDaoBackend extends AkkaBeforeAndAfterAll {
// `dbDispatcher` and `ledgerDao` depend on the `postgresFixture` which is in turn initialized `beforeAll`
private var resource: Resource[LedgerDao] = _
private val errorFactories = ErrorFactories(
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = true)
)
private val errorFactories = ErrorFactories()
override protected def beforeAll(): Unit = {
super.beforeAll()

View File

@ -7,7 +7,6 @@ import akka.NotUsed
import akka.stream.Materializer
import akka.stream.scaladsl.Source
import com.daml.api.util.TimeProvider
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.grpc.{GrpcException, GrpcStatus}
import com.daml.ledger.api.domain.{ConfigurationEntry, LedgerOffset}
import com.daml.ledger.api.testing.utils.AkkaBeforeAndAfterAll
@ -53,8 +52,6 @@ class ApiConfigManagementServiceSpec
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val useSelfServiceErrorCodes = mock[ErrorCodesVersionSwitcher]
"ApiConfigManagementService" should {
"get the time model" in {
val indexedTimeModel = LedgerTimeModel(
@ -76,7 +73,6 @@ class ApiConfigManagementServiceSpec
),
writeService,
TimeProvider.UTC,
useSelfServiceErrorCodes,
)
apiConfigManagementService
@ -84,17 +80,25 @@ class ApiConfigManagementServiceSpec
.map { response =>
response.timeModel should be(Some(expectedTimeModel))
verifyZeroInteractions(writeService)
verifyZeroInteractions(useSelfServiceErrorCodes)
succeed
}
}
"return a `NOT_FOUND` error if a time model is not found (V1 error codes)" in {
testReturnANotFoundErrorIfTimeModelNotFound(false)
}
"return a `NOT_FOUND` error if a time model is not found" in {
val writeService = mock[WriteConfigService]
val apiConfigManagementService = ApiConfigManagementService.createApiService(
EmptyIndexConfigManagementService,
writeService,
TimeProvider.UTC,
)
"return a `NOT_FOUND` error if a time model is not found (V2 self-service error codes)" in {
testReturnANotFoundErrorIfTimeModelNotFound(true)
apiConfigManagementService
.getTimeModel(GetTimeModelRequest.defaultInstance)
.transform(Success.apply)
.map { response =>
response should matchPattern { case Failure(GrpcException(GrpcStatus.NOT_FOUND(), _)) =>
}
}
}
"set a new time model" in {
@ -133,7 +137,6 @@ class ApiConfigManagementServiceSpec
indexService,
writeService,
timeProvider,
useSelfServiceErrorCodes,
)
apiConfigManagementService
@ -154,17 +157,44 @@ class ApiConfigManagementServiceSpec
.map { response =>
response.configurationGeneration should be(expectedGeneration)
currentConfiguration() should be(Some(expectedConfiguration))
verifyZeroInteractions(useSelfServiceErrorCodes)
succeed
}
}
"refuse to set a new time model if none is indexed (V1 error codes)" in {
testRefuseToSetANewTimeModelIfNoneIsIndexedYet(false)
}
"refuse to set a new time model if none is indexed" in {
val initialGeneration = 0L
"refuse to set a new time model if none is indexed (V2 self-service error codes)" in {
testRefuseToSetANewTimeModelIfNoneIsIndexedYet(true)
val timeProvider = TimeProvider.UTC
val maximumRecordTime = timeProvider.getCurrentTime.plusSeconds(60)
val writeService = mock[WriteService]
val apiConfigManagementService = ApiConfigManagementService.createApiService(
EmptyIndexConfigManagementService,
writeService,
timeProvider,
)
apiConfigManagementService
.setTimeModel(
SetTimeModelRequest.of(
"a submission ID",
maximumRecordTime = Some(Timestamp.of(maximumRecordTime.getEpochSecond, 0)),
configurationGeneration = initialGeneration,
newTimeModel = Some(
TimeModel(
avgTransactionLatency = Some(DurationProto.of(10, 0)),
minSkew = Some(DurationProto.of(20, 0)),
maxSkew = Some(DurationProto.of(40, 0)),
)
),
)
)
.transform(Success.apply)
.map { response =>
verifyZeroInteractions(writeService)
response should matchPattern { case Failure(GrpcException(GrpcStatus.NOT_FOUND(), _)) =>
}
}
}
"propagate trace context" in {
@ -172,7 +202,6 @@ class ApiConfigManagementServiceSpec
new FakeStreamingIndexConfigManagementService(someConfigurationEntries),
TestWriteConfigService,
TimeProvider.UTC,
useSelfServiceErrorCodes,
_ => Ref.SubmissionId.assertFromString("aSubmission"),
)
@ -186,73 +215,10 @@ class ApiConfigManagementServiceSpec
}
.map { _ =>
spanExporter.finishedSpanAttributes should contain(anApplicationIdSpanAttribute)
verifyZeroInteractions(useSelfServiceErrorCodes)
succeed
}
}
}
private def testReturnANotFoundErrorIfTimeModelNotFound(useSelfServiceErrorCodes: Boolean) = {
val errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(useSelfServiceErrorCodes)
val writeService = mock[WriteConfigService]
val apiConfigManagementService = ApiConfigManagementService.createApiService(
EmptyIndexConfigManagementService,
writeService,
TimeProvider.UTC,
errorCodesVersionSwitcher,
)
apiConfigManagementService
.getTimeModel(GetTimeModelRequest.defaultInstance)
.transform(Success.apply)
.map { response =>
response should matchPattern { case Failure(GrpcException(GrpcStatus.NOT_FOUND(), _)) =>
}
}
}
private def testRefuseToSetANewTimeModelIfNoneIsIndexedYet(useSelfServiceErrorCodes: Boolean) = {
val errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(useSelfServiceErrorCodes)
val initialGeneration = 0L
val timeProvider = TimeProvider.UTC
val maximumRecordTime = timeProvider.getCurrentTime.plusSeconds(60)
val writeService = mock[WriteService]
val apiConfigManagementService = ApiConfigManagementService.createApiService(
EmptyIndexConfigManagementService,
writeService,
timeProvider,
errorCodesVersionSwitcher,
)
apiConfigManagementService
.setTimeModel(
SetTimeModelRequest.of(
"a submission ID",
maximumRecordTime = Some(Timestamp.of(maximumRecordTime.getEpochSecond, 0)),
configurationGeneration = initialGeneration,
newTimeModel = Some(
TimeModel(
avgTransactionLatency = Some(DurationProto.of(10, 0)),
minSkew = Some(DurationProto.of(20, 0)),
maxSkew = Some(DurationProto.of(40, 0)),
)
),
)
)
.transform(Success.apply)
.map { response =>
verifyZeroInteractions(writeService)
if (useSelfServiceErrorCodes) {
response should matchPattern { case Failure(GrpcException(GrpcStatus.NOT_FOUND(), _)) =>
}
} else {
response should matchPattern { case Failure(GrpcException(GrpcStatus.UNAVAILABLE(), _)) =>
}
}
}
}
}
object ApiConfigManagementServiceSpec {

View File

@ -9,7 +9,6 @@ import java.util.zip.ZipInputStream
import akka.stream.scaladsl.Source
import com.daml.daml_lf_dev.DamlLf
import com.daml.daml_lf_dev.DamlLf.Archive
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.domain.LedgerOffset.Absolute
import com.daml.ledger.api.domain.PackageEntry
import com.daml.ledger.api.testing.utils.AkkaBeforeAndAfterAll
@ -49,7 +48,6 @@ class ApiPackageManagementServiceSpec
import ApiPackageManagementServiceSpec._
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val errorCodesVersionSwitcher: ErrorCodesVersionSwitcher = mock[ErrorCodesVersionSwitcher]
"ApiPackageManagementService $suffix" should {
"propagate trace context" in {
@ -65,7 +63,6 @@ class ApiPackageManagementServiceSpec
}
.map { _ =>
spanExporter.finishedSpanAttributes should contain(anApplicationIdSpanAttribute)
verifyZeroInteractions(errorCodesVersionSwitcher)
succeed
}
}
@ -99,7 +96,6 @@ class ApiPackageManagementServiceSpec
TestWritePackagesService,
Duration.ZERO,
mockEngine,
errorCodesVersionSwitcher,
mockDarReader,
_ => Ref.SubmissionId.assertFromString("aSubmission"),
)

View File

@ -6,7 +6,6 @@ package com.daml.platform.apiserver.services.admin
import java.time.Duration
import java.util.concurrent.{CompletableFuture, CompletionStage}
import akka.stream.scaladsl.Source
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.domain.LedgerOffset.Absolute
import com.daml.ledger.api.domain.{PartyDetails, PartyEntry}
import com.daml.ledger.api.testing.utils.AkkaBeforeAndAfterAll
@ -36,7 +35,6 @@ class ApiPartyManagementServiceSpec
with AkkaBeforeAndAfterAll {
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val errorCodesVersionSwitcher: ErrorCodesVersionSwitcher = mock[ErrorCodesVersionSwitcher]
"ApiPartyManagementService" should {
"propagate trace context" in {
@ -62,7 +60,6 @@ class ApiPartyManagementServiceSpec
mockIndexTransactionsService,
TestWritePartyService,
Duration.ZERO,
errorCodesVersionSwitcher,
_ => Ref.SubmissionId.assertFromString("aSubmission"),
)
@ -76,7 +73,6 @@ class ApiPartyManagementServiceSpec
}
.map { _ =>
spanExporter.finishedSpanAttributes should contain(anApplicationIdSpanAttribute)
verifyZeroInteractions(errorCodesVersionSwitcher)
succeed
}
}

View File

@ -3,7 +3,6 @@
package com.daml.platform.apiserver.services
import com.daml.error.ErrorCodesVersionSwitcher
import java.time.{Duration, Instant}
import java.util.UUID
import java.util.concurrent.TimeUnit
@ -40,8 +39,6 @@ class ApiCommandServiceSpec
private implicit val resourceContext: ResourceContext = ResourceContext(executionContext)
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val errorCodesVersionSwitcher = mock[ErrorCodesVersionSwitcher]
s"the command service" should {
val completionSuccess = CompletionResponse.CompletionSuccess(
Completion(
@ -71,7 +68,6 @@ class ApiCommandServiceSpec
new ApiCommandService(
UnimplementedTransactionServices,
submissionTracker,
errorCodesVersionSwitcher,
)
).use { stub =>
val request = SubmitAndWaitRequest.of(Some(commands))
@ -81,7 +77,6 @@ class ApiCommandServiceSpec
verify(submissionTracker).track(
eqTo(CommandSubmission(commands))
)(any[ExecutionContext], any[LoggingContext])
verifyZeroInteractions(errorCodesVersionSwitcher)
succeed
}
}
@ -111,7 +106,6 @@ class ApiCommandServiceSpec
new ApiCommandService(
UnimplementedTransactionServices,
submissionTracker,
errorCodesVersionSwitcher,
),
deadlineTicker,
).use { stub =>
@ -124,55 +118,42 @@ class ApiCommandServiceSpec
verify(submissionTracker).track(
eqTo(CommandSubmission(commands, timeout = Some(Duration.ofSeconds(30))))
)(any[ExecutionContext], any[LoggingContext])
verifyZeroInteractions(errorCodesVersionSwitcher)
succeed
}
}
}
"time out if the tracker times out" in {
def testTrackerTimeout(switcher: ErrorCodesVersionSwitcher, expectedStatusCode: Code) = {
val commands = someCommands()
val submissionTracker = mock[Tracker]
when(
submissionTracker.track(any[CommandSubmission])(
any[ExecutionContext],
any[LoggingContext],
)
).thenReturn(
Future.successful(
Left(
CompletionResponse.QueueCompletionFailure(
CompletionResponse.TimeoutResponse("command ID")
)
val commands = someCommands()
val submissionTracker = mock[Tracker]
when(
submissionTracker.track(any[CommandSubmission])(
any[ExecutionContext],
any[LoggingContext],
)
).thenReturn(
Future.successful(
Left(
CompletionResponse.QueueCompletionFailure(
CompletionResponse.TimeoutResponse("command ID")
)
)
)
)
openChannel(
new ApiCommandService(
UnimplementedTransactionServices,
submissionTracker,
switcher,
)
).use { stub =>
val request = SubmitAndWaitRequest.of(Some(commands))
stub.submitAndWaitForTransactionId(request).failed.map {
case RpcProtoExtractors.Exception(RpcProtoExtractors.Status(`expectedStatusCode`)) =>
succeed
case unexpected => fail(s"Unexpected exception", unexpected)
}
openChannel(
new ApiCommandService(
UnimplementedTransactionServices,
submissionTracker,
)
).use { stub =>
val request = SubmitAndWaitRequest.of(Some(commands))
stub.submitAndWaitForTransactionId(request).failed.map {
case RpcProtoExtractors.Exception(RpcProtoExtractors.Status(Code.DEADLINE_EXCEEDED)) =>
succeed
case unexpected => fail(s"Unexpected exception", unexpected)
}
}
testTrackerTimeout(
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = false),
Code.ABORTED,
)
testTrackerTimeout(
new ErrorCodesVersionSwitcher(enableSelfServiceErrorCodes = true),
Code.DEADLINE_EXCEEDED,
)
}
"close the supplied tracker when closed" in {
@ -180,11 +161,9 @@ class ApiCommandServiceSpec
val service = new ApiCommandService(
UnimplementedTransactionServices,
submissionTracker,
errorCodesVersionSwitcher,
)
verifyZeroInteractions(submissionTracker)
verifyZeroInteractions(errorCodesVersionSwitcher)
service.close()
verify(submissionTracker).close()

View File

@ -4,7 +4,7 @@
package com.daml.platform.apiserver.services
import com.codahale.metrics.MetricRegistry
import com.daml.error.{ErrorCause, ErrorCodesVersionSwitcher}
import com.daml.error.ErrorCause
import com.daml.ledger.api.domain.{CommandId, Commands, LedgerId, PartyDetails}
import com.daml.ledger.api.messages.command.submission.SubmitRequest
import com.daml.ledger.api.{DeduplicationPeriod, DomainMocks}
@ -35,7 +35,7 @@ import io.grpc.{Status, StatusRuntimeException}
import org.mockito.{ArgumentMatchersSugar, MockitoSugar}
import org.scalatest.flatspec.AsyncFlatSpec
import org.scalatest.matchers.should.Matchers
import org.scalatest.{Assertion, Inside}
import org.scalatest.Inside
import java.time.Duration
import java.util.concurrent.CompletableFuture.completedFuture
import java.util.concurrent.atomic.AtomicInteger
@ -221,16 +221,6 @@ class ApiSubmissionServiceSpec
behavior of "submit"
it should "return proper gRPC status codes for DamlLf errors" in {
testProperGrpcStatusCodesForDamlLfErrors(useSelfServiceErrorCodes = false)
}
it should "return proper gRPC status codes for DamlLf errors (self service error codes a.k.a v2 error codes)" in {
testProperGrpcStatusCodesForDamlLfErrors(useSelfServiceErrorCodes = true)
}
private def testProperGrpcStatusCodesForDamlLfErrors(
useSelfServiceErrorCodes: Boolean
): Future[Assertion] = {
// given
val partyManagementService = mock[IndexPartyManagementService]
val writeService = mock[WriteService]
@ -245,7 +235,7 @@ class ApiSubmissionServiceSpec
),
None,
)
) -> ((Status.ABORTED, Status.NOT_FOUND)),
) -> Status.NOT_FOUND,
ErrorCause.DamlLf(
LfError.Interpretation(
LfError.Interpretation.DamlException(
@ -255,12 +245,12 @@ class ApiSubmissionServiceSpec
),
None,
)
) -> ((Status.ABORTED, Status.ALREADY_EXISTS)),
) -> Status.ALREADY_EXISTS,
ErrorCause.DamlLf(
LfError.Validation(
LfError.Validation.ReplayMismatch(ReplayMismatch(null, null))
)
) -> ((Status.ABORTED, Status.INTERNAL)),
) -> Status.INTERNAL,
ErrorCause.DamlLf(
LfError.Preprocessing(
LfError.Preprocessing.Lookup(
@ -270,7 +260,7 @@ class ApiSubmissionServiceSpec
)
)
)
) -> ((Status.INVALID_ARGUMENT, Status.INVALID_ARGUMENT)),
) -> Status.INVALID_ARGUMENT,
ErrorCause.DamlLf(
LfError.Interpretation(
LfError.Interpretation.DamlException(
@ -281,22 +271,15 @@ class ApiSubmissionServiceSpec
),
None,
)
) -> ((Status.INVALID_ARGUMENT, Status.INVALID_ARGUMENT)),
ErrorCause.LedgerTime(0) -> ((Status.ABORTED, Status.ABORTED)),
).map { case (key, (statusV1, statusV2)) =>
if (useSelfServiceErrorCodes) {
(key, statusV2)
} else {
(key, statusV1)
}
}
) -> Status.INVALID_ARGUMENT,
ErrorCause.LedgerTime(0) -> Status.ABORTED,
)
val service = newSubmissionService(
writeService,
partyManagementService,
implicitPartyAllocation = true,
commandExecutor = mockCommandExecutor,
useSelfServiceErrorCodes = useSelfServiceErrorCodes,
)
// when
@ -379,7 +362,6 @@ object ApiSubmissionServiceSpec {
partyManagementService: IndexPartyManagementService,
implicitPartyAllocation: Boolean,
commandExecutor: CommandExecutor = null,
useSelfServiceErrorCodes: Boolean = true,
checkOverloaded: TelemetryContext => Option[SubmissionResult] = _ => None,
)(implicit
executionContext: ExecutionContext,
@ -400,9 +382,6 @@ object ApiSubmissionServiceSpec {
commandExecutor = commandExecutor,
configuration = ApiSubmissionService.Configuration(implicitPartyAllocation),
metrics = new Metrics(new MetricRegistry),
errorCodesVersionSwitcher = new ErrorCodesVersionSwitcher(
enableSelfServiceErrorCodes = useSelfServiceErrorCodes
),
checkOverloaded = checkOverloaded,
)
}

View File

@ -3,7 +3,6 @@
package com.daml.platform.apiserver.services.admin
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.v1.admin.metering_report_service.{
ApplicationMeteringReport,
GetMeteringReportRequest,
@ -31,8 +30,6 @@ class ApiMeteringReportServiceSpec extends AsyncWordSpec with Matchers with Mock
private val someParticipantId = Ref.ParticipantId.assertFromString("test-participant")
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val switcher = new ErrorCodesVersionSwitcher(true)
private val appIdA = Ref.ApplicationId.assertFromString("AppA")
private val appIdB = Ref.ApplicationId.assertFromString("AppB")
@ -91,7 +88,7 @@ class ApiMeteringReportServiceSpec extends AsyncWordSpec with Matchers with Mock
val expectedGenTime = toProtoTimestamp(Timestamp.now().addMicros(-1000))
val underTest =
new ApiMeteringReportService(someParticipantId, store, switcher, () => expectedGenTime)
new ApiMeteringReportService(someParticipantId, store, () => expectedGenTime)
val from = Timestamp(10000)
@ -115,7 +112,7 @@ class ApiMeteringReportServiceSpec extends AsyncWordSpec with Matchers with Mock
val expectedGenTime = toProtoTimestamp(Timestamp.now().addMicros(-1000))
val underTest =
new ApiMeteringReportService(someParticipantId, store, switcher, () => expectedGenTime)
new ApiMeteringReportService(someParticipantId, store, () => expectedGenTime)
val from = Timestamp(10000)
val to = Timestamp(20000)
@ -138,7 +135,7 @@ class ApiMeteringReportServiceSpec extends AsyncWordSpec with Matchers with Mock
}
"fail if the from timestamp is unset" in {
val underTest = new ApiMeteringReportService(someParticipantId, mock[MeteringStore], switcher)
val underTest = new ApiMeteringReportService(someParticipantId, mock[MeteringStore])
val request = GetMeteringReportRequest.defaultInstance
underTest.getMeteringReport(request).failed.map { _ => succeed }
}

View File

@ -70,58 +70,13 @@ class QueueBackedTrackerSpec
"input is submitted, and the queue is backpressuring" should {
"return a RESOURCE_EXHAUSTED error" in {
def testIt(useSelfServiceErrorCodes: Boolean, expectedStatusCode: com.google.rpc.Code) = {
val tracker = new QueueBackedTracker(
queue,
Future.successful(Done),
ErrorFactories(useSelfServiceErrorCodes),
)
tracker.track(input(1))
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
CompletionResponse
.QueueSubmitFailure(RpcProtoExtractors.Status(`expectedStatusCode`))
) =>
}
}
}
testIt(useSelfServiceErrorCodes = false, com.google.rpc.Code.RESOURCE_EXHAUSTED)
testIt(useSelfServiceErrorCodes = true, com.google.rpc.Code.ABORTED)
}
}
"input is submitted, and the queue has been completed" should {
"return an UNAVAILABLE error with self-service error codes enabled" in {
val tracker = new QueueBackedTracker(
queue,
Future.successful(Done),
ErrorFactories(useSelfServiceErrorCodes = true),
)
queue.complete()
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
CompletionResponse
.QueueSubmitFailure(RpcProtoExtractors.Status(com.google.rpc.Code.UNAVAILABLE))
) =>
}
}
}
"return an ABORTED error with self-service error codes disabled" in {
val tracker = new QueueBackedTracker(
queue,
Future.successful(Done),
ErrorFactories(useSelfServiceErrorCodes = false),
ErrorFactories(),
)
queue.complete()
tracker.track(input(1))
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
@ -133,12 +88,31 @@ class QueueBackedTrackerSpec
}
}
"input is submitted, and the queue has failed" should {
"return an INTERNAL error with self-service error codes enabled" in {
"input is submitted, and the queue has been completed" should {
"return an UNAVAILABLE error" in {
val tracker = new QueueBackedTracker(
queue,
Future.successful(Done),
ErrorFactories(useSelfServiceErrorCodes = true),
ErrorFactories(),
)
queue.complete()
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
CompletionResponse
.QueueSubmitFailure(RpcProtoExtractors.Status(com.google.rpc.Code.UNAVAILABLE))
) =>
}
}
}
}
"input is submitted, and the queue has failed" should {
"return an INTERNAL error" in {
val tracker = new QueueBackedTracker(
queue,
Future.successful(Done),
ErrorFactories(),
)
queue.fail(TestingException("The queue fails with this error."))
@ -151,58 +125,35 @@ class QueueBackedTrackerSpec
}
}
}
"return an ABORTED error with self-service error codes disabled" in {
val tracker = new QueueBackedTracker(
queue,
Future.successful(Done),
ErrorFactories(useSelfServiceErrorCodes = false),
)
queue.fail(TestingException("The queue fails with this error."))
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
CompletionResponse
.QueueSubmitFailure(RpcProtoExtractors.Status(com.google.rpc.Code.ABORTED))
) =>
}
}
}
}
"input is submitted, and the offer method has thrown an exception" should {
"return an INTERNAL error" in {
def testIt(useSelfServiceErrorCodes: Boolean) = {
val fakeQueue = new BoundedSourceQueue[QueueInput] {
override def offer(elem: QueueInput): QueueOfferResult =
throw new IllegalArgumentException("test")
val fakeQueue = new BoundedSourceQueue[QueueInput] {
override def offer(elem: QueueInput): QueueOfferResult =
throw new IllegalArgumentException("test")
override def size(): Int = 0
override def size(): Int = 0
override def complete(): Unit = ()
override def complete(): Unit = ()
override def fail(ex: Throwable): Unit = ()
}
val tracker = new QueueBackedTracker(
fakeQueue,
Future.successful(Done),
ErrorFactories(useSelfServiceErrorCodes),
)
override def fail(ex: Throwable): Unit = ()
}
val tracker = new QueueBackedTracker(
fakeQueue,
Future.successful(Done),
ErrorFactories(),
)
tracker.track(input(1))
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
CompletionResponse
.QueueSubmitFailure(RpcProtoExtractors.Status(com.google.rpc.Code.INTERNAL))
) =>
}
tracker.track(input(1))
tracker.track(input(2)).map { completion =>
completion should matchPattern {
case Left(
CompletionResponse
.QueueSubmitFailure(RpcProtoExtractors.Status(com.google.rpc.Code.INTERNAL))
) =>
}
}
testIt(useSelfServiceErrorCodes = false)
testIt(useSelfServiceErrorCodes = true)
}
}
}

View File

@ -51,7 +51,6 @@ da_scala_library(
"//ledger/ledger-api-health",
"//ledger/ledger-configuration",
"//ledger/ledger-configuration/protobuf:ledger_configuration_proto_java",
"//ledger/ledger-grpc",
"//ledger/ledger-offset",
"//ledger/ledger-resources",
"//ledger/metrics",

View File

@ -33,7 +33,6 @@ da_scala_library(
"//daml-lf/transaction",
"//language-support/scala/bindings",
"//ledger/caching",
"//ledger/error",
"//ledger/ledger-api-auth",
"//ledger/ledger-api-common",
"//ledger/ledger-api-health",

View File

@ -47,7 +47,6 @@ trait ReadWriteServiceFactory {
}
class KeyValueReadWriteFactory(
config: Config[_],
metrics: Metrics,
ledgerReader: LedgerReader,
ledgerWriter: LedgerWriter,
@ -56,7 +55,6 @@ class KeyValueReadWriteFactory(
KeyValueParticipantStateReader(
ledgerReader,
metrics,
config.enableSelfServiceErrorCodes,
)
}
@ -70,7 +68,6 @@ class KeyValueReadWriteFactory(
class KeyValueDeduplicationSupportFactory(
delegate: ReadWriteServiceFactory,
config: Config[_],
completionsService: IndexCompletionsService,
)(implicit materializer: Materializer, ec: ExecutionContext)
extends ReadWriteServiceFactory {
@ -78,7 +75,7 @@ class KeyValueDeduplicationSupportFactory(
override def writeService(): WriteService = {
val writeServiceDelegate = delegate.writeService()
val errorFactories = ErrorFactories(config.enableSelfServiceErrorCodes)
val errorFactories = ErrorFactories()
new WriteServiceWithDeduplicationSupport(
writeServiceDelegate,
new DeduplicationPeriodSupport(

View File

@ -9,7 +9,6 @@ import akka.stream.Materializer
import com.codahale.metrics.InstrumentedExecutorService
import com.daml.api.util.TimeProvider
import com.daml.buildinfo.BuildInfo
import com.daml.error.ErrorCodesVersionSwitcher
import com.daml.ledger.api.auth.{
AuthServiceJWT,
AuthServiceNone,
@ -65,9 +64,7 @@ final class Runner[T <: ReadWriteService, Extra](
val config = configProvider.manipulateConfig(originalConfig)
config.mode match {
case Mode.DumpIndexMetadata(jdbcUrls) =>
val errorFactories = ErrorFactories(
new ErrorCodesVersionSwitcher(originalConfig.enableSelfServiceErrorCodes)
)
val errorFactories = ErrorFactories()
DumpIndexMetadata(jdbcUrls, errorFactories, name)
sys.exit(0)
case Mode.Run =>
@ -230,7 +227,6 @@ final class Runner[T <: ReadWriteService, Extra](
).acquire()
factory = new KeyValueDeduplicationSupportFactory(
ledgerFactory,
config,
indexService,
)(implicitly, servicesExecutionContext)
writeService = new TimedWriteService(factory.writeService(), metrics)

View File

@ -6,7 +6,7 @@ package com.daml.ledger.participant.state.kvutils
import java.io.StringWriter
import java.time.{Duration, Instant}
import com.daml.error.{ContextualizedErrorLogger, ValueSwitch}
import com.daml.error.ContextualizedErrorLogger
import com.daml.ledger.api.DeduplicationPeriod
import com.daml.ledger.offset.Offset
import com.daml.ledger.participant.state.kvutils.committer.transaction.Rejection
@ -465,62 +465,61 @@ object Conversions {
@nowarn("msg=deprecated")
def decodeTransactionRejectionEntry(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
entry: DamlTransactionRejectionEntry
)(implicit loggingContext: ContextualizedErrorLogger): FinalReason =
FinalReason(entry.getReasonCase match {
case DamlTransactionRejectionEntry.ReasonCase.INVALID_LEDGER_TIME =>
val rejection = entry.getInvalidLedgerTime
invalidLedgerTimeStatus(entry, rejection, errorVersionSwitch)
invalidLedgerTimeStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.DISPUTED =>
val rejection = entry.getDisputed
disputedStatus(entry, rejection, errorVersionSwitch)
disputedStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.SUBMITTER_CANNOT_ACT_VIA_PARTICIPANT =>
val rejection = entry.getSubmitterCannotActViaParticipant
submitterCannotActViaParticipantStatus(entry, rejection, errorVersionSwitch)
submitterCannotActViaParticipantStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.INCONSISTENT =>
val rejection = entry.getInconsistent
inconsistentStatus(entry, rejection, errorVersionSwitch)
inconsistentStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.RESOURCES_EXHAUSTED =>
val rejection = entry.getResourcesExhausted
resourceExhaustedStatus(entry, rejection, errorVersionSwitch)
resourceExhaustedStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.DUPLICATE_COMMAND =>
duplicateCommandStatus(entry, errorVersionSwitch)
duplicateCommandStatus(entry)
case DamlTransactionRejectionEntry.ReasonCase.PARTY_NOT_KNOWN_ON_LEDGER =>
val rejection = entry.getPartyNotKnownOnLedger
partyNotKnownOnLedgerStatus(entry, rejection, errorVersionSwitch)
partyNotKnownOnLedgerStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.VALIDATION_FAILURE =>
val rejection = entry.getValidationFailure
validationFailureStatus(entry, rejection, errorVersionSwitch)
validationFailureStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.INTERNALLY_DUPLICATE_KEYS =>
internallyDuplicateKeysStatus(entry, errorVersionSwitch)
internallyDuplicateKeysStatus()
case DamlTransactionRejectionEntry.ReasonCase.INTERNALLY_INCONSISTENT_KEYS =>
internallyInconsistentKeysStatus(entry, errorVersionSwitch)
internallyInconsistentKeysStatus()
case DamlTransactionRejectionEntry.ReasonCase.EXTERNALLY_INCONSISTENT_CONTRACTS =>
externallyInconsistentContractsStatus(entry, errorVersionSwitch)
externallyInconsistentContractsStatus()
case DamlTransactionRejectionEntry.ReasonCase.EXTERNALLY_DUPLICATE_KEYS =>
externallyDuplicateKeysStatus(entry, errorVersionSwitch)
externallyDuplicateKeysStatus()
case DamlTransactionRejectionEntry.ReasonCase.EXTERNALLY_INCONSISTENT_KEYS =>
externallyInconsistentKeysStatus(entry, errorVersionSwitch)
externallyInconsistentKeysStatus()
case DamlTransactionRejectionEntry.ReasonCase.MISSING_INPUT_STATE =>
val rejection = entry.getMissingInputState
missingInputStateStatus(entry, rejection, errorVersionSwitch)
missingInputStateStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.RECORD_TIME_OUT_OF_RANGE =>
val rejection = entry.getRecordTimeOutOfRange
recordTimeOutOfRangeStatus(entry, rejection, errorVersionSwitch)
recordTimeOutOfRangeStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.CAUSAL_MONOTONICITY_VIOLATED =>
causalMonotonicityViolatedStatus(entry, errorVersionSwitch)
causalMonotonicityViolatedStatus()
case DamlTransactionRejectionEntry.ReasonCase.SUBMITTING_PARTY_NOT_KNOWN_ON_LEDGER =>
val rejection = entry.getSubmittingPartyNotKnownOnLedger
submittingPartyNotKnownOnLedgerStatus(entry, rejection, errorVersionSwitch)
submittingPartyNotKnownOnLedgerStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.PARTIES_NOT_KNOWN_ON_LEDGER =>
val rejection = entry.getPartiesNotKnownOnLedger
partiesNotKnownOnLedgerStatus(entry, rejection, errorVersionSwitch)
partiesNotKnownOnLedgerStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.INVALID_PARTICIPANT_STATE =>
val rejection = entry.getInvalidParticipantState
invalidParticipantStateStatus(entry, rejection, errorVersionSwitch)
invalidParticipantStateStatus(rejection)
case DamlTransactionRejectionEntry.ReasonCase.REASON_NOT_SET =>
rejectionReasonNotSetStatus(entry, errorVersionSwitch)
rejectionReasonNotSetStatus()
})
def objectToJsonString(obj: Object): String = {

View File

@ -3,7 +3,7 @@
package com.daml.ledger.participant.state.kvutils
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger, ValueSwitch}
import com.daml.error.{ContextualizedErrorLogger, DamlContextualizedErrorLogger}
import com.daml.ledger.configuration.Configuration
import com.daml.ledger.participant.state.kvutils.Conversions._
import com.daml.ledger.participant.state.kvutils.store.events.PackageUpload.DamlPackageUploadRejectionEntry
@ -52,7 +52,6 @@ object KeyValueConsumption {
*
* @param entryId: The log entry identifier.
* @param entry: The log entry.
* @param errorVersionSwitch: Decides between v1 and v2 (self-service) errors.
* @return [[Update]]s constructed from log entry.
*/
// TODO(BH): add participantId to ensure participant id matches in DamlLogEntry
@ -60,7 +59,6 @@ object KeyValueConsumption {
def logEntryToUpdate(
entryId: DamlLogEntryId,
entry: DamlLogEntry,
errorVersionSwitch: ValueSwitch,
recordTimeForUpdate: Option[Timestamp] = None,
)(loggingContext: LoggingContext): List[Update] = {
implicit val logContext: LoggingContext = loggingContext
@ -218,7 +216,6 @@ object KeyValueConsumption {
transactionRejectionEntryToUpdate(
recordTime,
rejectionEntry,
errorVersionSwitch,
)(contextualizedErrorLogger(loggingContext, rejectionEntry))
)
@ -226,7 +223,6 @@ object KeyValueConsumption {
outOfTimeBoundsEntryToUpdate(
recordTime,
entry.getOutOfTimeBoundsEntry,
errorVersionSwitch,
).toList
case DamlLogEntry.PayloadCase.TIME_UPDATE_ENTRY =>
@ -251,9 +247,8 @@ object KeyValueConsumption {
private def transactionRejectionEntryToUpdate(
recordTime: Timestamp,
rejEntry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Update = {
val reason = Conversions.decodeTransactionRejectionEntry(rejEntry, errorVersionSwitch)
val reason = Conversions.decodeTransactionRejectionEntry(rejEntry)
Update.CommandRejected(
recordTime = recordTime,
completionInfo = parseCompletionInfo(recordTime, rejEntry.getSubmitterInfo),
@ -350,7 +345,6 @@ object KeyValueConsumption {
private[kvutils] def outOfTimeBoundsEntryToUpdate(
recordTime: Timestamp,
outOfTimeBoundsEntry: DamlOutOfTimeBoundsEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: LoggingContext): Option[Update] = {
val timeBounds = parseTimeBounds(outOfTimeBoundsEntry)
val deduplicated = timeBounds.deduplicateUntil.exists(recordTime <= _)
@ -366,7 +360,6 @@ object KeyValueConsumption {
duplicateCommandsRejectionUpdate(
recordTime,
rejectionEntry,
errorVersionSwitch,
None, // Not available for historical entries
)(contextualizedErrorLogger(loggingContext, rejectionEntry))
)
@ -383,7 +376,6 @@ object KeyValueConsumption {
timeBounds.tooEarlyUntil,
timeBounds.tooLateFrom,
rejectionEntry,
errorVersionSwitch,
)(contextualizedErrorLogger(loggingContext, rejectionEntry))
)

View File

@ -5,7 +5,6 @@ package com.daml.ledger.participant.state.kvutils.api
import akka.NotUsed
import akka.stream.scaladsl.Source
import com.daml.error.ValueSwitch
import com.daml.ledger.api.health.HealthStatus
import com.daml.ledger.configuration.LedgerInitialConditions
import com.daml.ledger.offset.Offset
@ -29,11 +28,9 @@ import com.daml.metrics.{Metrics, Timed}
class KeyValueParticipantStateReader private[api] (
reader: LedgerReader,
metrics: Metrics,
enableSelfServiceErrorCodes: Boolean,
logEntryToUpdate: (
DamlLogEntryId,
DamlLogEntry,
ValueSwitch,
Option[Timestamp],
) => LoggingContext => List[Update],
timeUpdatesProvider: TimeUpdatesProvider,
@ -42,8 +39,6 @@ class KeyValueParticipantStateReader private[api] (
import KeyValueParticipantStateReader._
private val errorVersionSwitch = new ValueSwitch(enableSelfServiceErrorCodes)
override def ledgerInitialConditions(): Source[LedgerInitialConditions, NotUsed] =
Source.single(createLedgerInitialConditions())
@ -65,7 +60,6 @@ class KeyValueParticipantStateReader private[api] (
logEntryToUpdate(
logEntryId,
logEntry,
errorVersionSwitch,
timeUpdatesProvider(),
)(loggingContext)
val updatesWithOffsets =
@ -102,14 +96,12 @@ object KeyValueParticipantStateReader {
def apply(
reader: LedgerReader,
metrics: Metrics,
enableSelfServiceErrorCodes: Boolean,
timeUpdatesProvider: TimeUpdatesProvider = TimeUpdatesProvider.ReasonableDefault,
failOnUnexpectedEvent: Boolean = true,
): KeyValueParticipantStateReader =
new KeyValueParticipantStateReader(
reader,
metrics,
enableSelfServiceErrorCodes,
KeyValueConsumption.logEntryToUpdate,
timeUpdatesProvider,
failOnUnexpectedEvent,

View File

@ -115,12 +115,11 @@ object WriteServiceWithDeduplicationSupport {
def apply(
delegate: WriteService,
indexCompletionService: IndexCompletionsService,
enableSelfServiceErrorCodes: Boolean,
)(implicit
materializer: Materializer,
ec: ExecutionContext,
): WriteServiceWithDeduplicationSupport = {
val errorFactories = ErrorFactories(enableSelfServiceErrorCodes)
val errorFactories = ErrorFactories()
new WriteServiceWithDeduplicationSupport(
delegate,
new DeduplicationPeriodSupport(

View File

@ -60,9 +60,7 @@ class DeduplicationPeriodSupport(
)
Left(
errorFactories.invalidDeduplicationPeriod(
"deduplication_period",
s"Cannot convert deduplication offset to duration because there is no completion at given offset $offset.",
Some(false),
None,
)
)

View File

@ -5,11 +5,9 @@ package com.daml.ledger.participant.state.kvutils.updates
import com.daml.error.definitions.LedgerApiErrors
import java.io.StringWriter
import java.time.Instant
import com.daml.error.{ContextualizedErrorLogger, ValueSwitch}
import com.daml.error.ContextualizedErrorLogger
import com.daml.grpc.GrpcStatus
import com.daml.ledger.grpc.GrpcStatuses
import com.daml.ledger.participant.state.kvutils.Conversions.parseCompletionInfo
import com.daml.ledger.participant.state.kvutils.committer.transaction.Rejection.{
ExternallyInconsistentTransaction,
@ -20,27 +18,19 @@ import com.daml.ledger.participant.state.kvutils.store.events._
import com.daml.ledger.participant.state.v2.Update
import com.daml.ledger.participant.state.v2.Update.CommandRejected.FinalReason
import com.daml.lf.data.Time.Timestamp
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.protobuf.ProtocolStringList
import com.google.protobuf.any.{Any => AnyProto}
import com.google.rpc.code.Code
import com.google.rpc.error_details.ErrorInfo
import com.google.rpc.status.Status
import scala.annotation.nowarn
import scala.jdk.CollectionConverters._
/** Utilities for converting between rejection log entries and updates and/or gRPC statuses.
*/
private[kvutils] object TransactionRejections {
@nowarn("msg=deprecated")
def invalidRecordTimeRejectionUpdate(
recordTime: Timestamp,
tooEarlyUntil: Option[Timestamp],
tooLateFrom: Option[Timestamp],
rejectionEntry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Update.CommandRejected = {
Update.CommandRejected(
recordTime = recordTime,
@ -49,28 +39,22 @@ private[kvutils] object TransactionRejections {
rejectionEntry.getSubmitterInfo,
),
reasonTemplate = FinalReason(
errorVersionSwitch.choose(
V1.invalidRecordTimeRejectionStatus(
rejectionEntry,
invalidRecordTimeReason(recordTime, tooEarlyUntil, tooEarlyUntil),
Code.ABORTED,
),
V2.invalidRecordTimeRejectionStatus(
rejectionEntry,
recordTime,
tooEarlyUntil,
tooLateFrom,
),
)
KVErrors.Time.InvalidRecordTime
.Reject(
rejectionEntry.getDefiniteAnswer,
invalidRecordTimeReason(recordTime, tooEarlyUntil, tooLateFrom),
recordTime.toInstant,
tooEarlyUntil.map(_.toInstant),
tooLateFrom.map(_.toInstant),
)
.asStatus
),
)
}
@nowarn("msg=deprecated")
def duplicateCommandsRejectionUpdate(
recordTime: Timestamp,
rejectionEntry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
existingCommandSubmissionId: Option[String],
)(implicit loggingContext: ContextualizedErrorLogger): Update.CommandRejected = {
val definiteAnswer = rejectionEntry.getDefiniteAnswer
@ -81,98 +65,57 @@ private[kvutils] object TransactionRejections {
rejectionEntry.getSubmitterInfo,
),
reasonTemplate = FinalReason(
errorVersionSwitch.choose(
V1.duplicateCommandsRejectionStatus(definiteAnswer, Code.ALREADY_EXISTS),
V2.duplicateCommandsRejectionStatus(definiteAnswer, existingCommandSubmissionId),
)
duplicateCommandsRejectionStatus(definiteAnswer, existingCommandSubmissionId)
),
)
}
@nowarn("msg=deprecated")
def rejectionReasonNotSetStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(entry, Code.UNKNOWN, "No reason set for rejection"),
V2.rejectionReasonNotSetStatus(),
)
def rejectionReasonNotSetStatus()(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Internal.RejectionReasonNotSet
.Reject()
.asStatus
@nowarn("msg=deprecated")
def invalidParticipantStateStatus(
entry: DamlTransactionRejectionEntry,
rejection: InvalidParticipantState,
errorVersionSwitch: ValueSwitch,
rejection: InvalidParticipantState
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
val metadata = rejection.getMetadataMap.asScala.toMap
errorVersionSwitch.choose(
V1.status(
entry,
Code.INVALID_ARGUMENT,
s"Disputed: $details",
metadata,
),
V2.invalidParticipantStateStatus(details, metadata),
)
KVErrors.Internal.InvalidParticipantState
.Reject(details, metadata)
.asStatus
}
@nowarn("msg=deprecated")
def partiesNotKnownOnLedgerStatus(
entry: DamlTransactionRejectionEntry,
rejection: PartiesNotKnownOnLedger,
errorVersionSwitch: ValueSwitch,
rejection: PartiesNotKnownOnLedger
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val parties = rejection.getPartiesList
errorVersionSwitch.choose(
V1.status(
entry,
Code.INVALID_ARGUMENT,
s"Party not known on ledger: Parties not known on ledger ${parties.asScala.mkString("[", ",", "]")}",
Map("parties" -> toJsonString(parties)),
),
V2.partiesNotKnownOnLedgerStatus(parties.asScala.toSeq),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.Reject(parties.asScala.toSet)
.asGrpcStatusFromContext
)
}
@nowarn("msg=deprecated")
def submittingPartyNotKnownOnLedgerStatus(
entry: DamlTransactionRejectionEntry,
rejection: SubmittingPartyNotKnownOnLedger,
errorVersionSwitch: ValueSwitch,
rejection: SubmittingPartyNotKnownOnLedger
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val submitter = rejection.getSubmitterParty
errorVersionSwitch.choose(
V1.status(
entry,
Code.INVALID_ARGUMENT,
s"Party not known on ledger: Submitting party '$submitter' not known",
Map("submitter_party" -> submitter),
),
V2.submittingPartyNotKnownOnLedgerStatus(submitter),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.SubmittingPartyNotKnownOnLedger
.Reject(submitter)
.asGrpcStatusFromContext
)
}
@nowarn("msg=deprecated")
def causalMonotonicityViolatedStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
"Invalid ledger time: Causal monotonicity violated",
),
V2.causalMonotonicityViolatedStatus(),
)
def causalMonotonicityViolatedStatus()(implicit
loggingContext: ContextualizedErrorLogger
): Status =
KVErrors.Time.CausalMonotonicityViolated
.Reject()
.asStatus
@nowarn("msg=deprecated")
def recordTimeOutOfRangeStatus(
entry: DamlTransactionRejectionEntry,
rejection: RecordTimeOutOfRange,
errorVersionSwitch: ValueSwitch,
rejection: RecordTimeOutOfRange
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val minRecordTime = Instant
.ofEpochSecond(
@ -184,530 +127,183 @@ private[kvutils] object TransactionRejections {
rejection.getMaximumRecordTime.getSeconds,
rejection.getMaximumRecordTime.getNanos.toLong,
)
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Invalid ledger time: Record time is outside of valid range [${rejection.getMinimumRecordTime}, ${rejection.getMaximumRecordTime}]",
Map(
"minimum_record_time" -> minRecordTime.toString,
"maximum_record_time" -> maxRecordTime.toString,
),
),
V2.recordTimeOutOfRangeStatus(minRecordTime, maxRecordTime),
)
KVErrors.Time.RecordTimeOutOfRange
.Reject(minRecordTime, maxRecordTime)
.asStatus
}
@nowarn("msg=deprecated")
def externallyDuplicateKeysStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Inconsistent: ${ExternallyInconsistentTransaction.DuplicateKeys.description}",
),
V2.externallyDuplicateKeysStatus(),
def externallyDuplicateKeysStatus()(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.DuplicateContractKey
.Reject(ExternallyInconsistentTransaction.DuplicateKeys.description)
.asGrpcStatusFromContext
)
@nowarn("msg=deprecated")
def externallyInconsistentKeysStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Inconsistent: ${ExternallyInconsistentTransaction.InconsistentKeys.description}",
),
V2.externallyInconsistentKeysStatus(),
def externallyInconsistentKeysStatus()(implicit
loggingContext: ContextualizedErrorLogger
): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InconsistentContractKey
.Reject(ExternallyInconsistentTransaction.InconsistentKeys.description)
.asGrpcStatusFromContext
)
@nowarn("msg=deprecated")
def externallyInconsistentContractsStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Inconsistent: ${ExternallyInconsistentTransaction.InconsistentContracts.description}",
),
V2.externallyInconsistentContractsStatus(),
def externallyInconsistentContractsStatus()(implicit
loggingContext: ContextualizedErrorLogger
): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InconsistentContracts
.Reject(ExternallyInconsistentTransaction.InconsistentContracts.description)
.asGrpcStatusFromContext
)
@nowarn("msg=deprecated")
def missingInputStateStatus(
entry: DamlTransactionRejectionEntry,
rejection: MissingInputState,
errorVersionSwitch: ValueSwitch,
rejection: MissingInputState
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val key = rejection.getKey.toString
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Inconsistent: Missing input state for key $key",
Map("key" -> key),
),
V2.missingInputStateStatus(key),
)
KVErrors.Internal.MissingInputState
.Reject(key)
.asStatus
}
@nowarn("msg=deprecated")
def internallyInconsistentKeysStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(
entry,
Code.INVALID_ARGUMENT,
s"Disputed: ${InternallyInconsistentTransaction.InconsistentKeys.description}",
),
V2.internallyInconsistentKeysStatus(), // Code.INTERNAL as it should have been caught by the participant
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Internal.InternallyInconsistentKeys
.Reject(InternallyInconsistentTransaction.InconsistentKeys.description)
.asGrpcStatusFromContext
)
@nowarn("msg=deprecated")
def internallyDuplicateKeysStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
errorVersionSwitch.choose(
V1.status(
entry,
Code.INVALID_ARGUMENT,
s"Disputed: ${InternallyInconsistentTransaction.DuplicateKeys.description}",
),
V2.internallyDuplicateKeysStatus(), // Code.INTERNAL as it should have been caught by the participant
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Internal.InternallyDuplicateKeys
.Reject(InternallyInconsistentTransaction.DuplicateKeys.description)
.asGrpcStatusFromContext
)
@nowarn("msg=deprecated")
def validationFailureStatus(
entry: DamlTransactionRejectionEntry,
rejection: ValidationFailure,
errorVersionSwitch: ValueSwitch,
rejection: ValidationFailure
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
errorVersionSwitch.choose(
V1.disputedStatus(entry, details, Code.INVALID_ARGUMENT),
V2.validationFailureStatus(details),
)
KVErrors.Consistency.ValidationFailure
.Reject(details)
.asStatus
}
@nowarn("msg=deprecated")
def duplicateCommandStatus(
entry: DamlTransactionRejectionEntry,
errorVersionSwitch: ValueSwitch,
entry: DamlTransactionRejectionEntry
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val rejectionReason = entry.getDuplicateCommand
errorVersionSwitch.choose(
V1.status(
entry,
Code.ALREADY_EXISTS,
"Duplicate commands",
),
V2.duplicateCommandsRejectionStatus(existingCommandSubmissionId =
Some(rejectionReason.getSubmissionId).filter(_.nonEmpty)
),
duplicateCommandsRejectionStatus(existingCommandSubmissionId =
Some(rejectionReason.getSubmissionId).filter(_.nonEmpty)
)
}
@nowarn("msg=deprecated")
def submitterCannotActViaParticipantStatus(
entry: DamlTransactionRejectionEntry,
rejection: SubmitterCannotActViaParticipant,
errorVersionSwitch: ValueSwitch,
rejection: SubmitterCannotActViaParticipant
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val submitter = rejection.getSubmitterParty
val participantId = rejection.getParticipantId
val details = rejection.getDetails
errorVersionSwitch.choose(
V1.status(
entry,
Code.PERMISSION_DENIED,
s"Submitter cannot act via participant: $details",
Map(
"submitter_party" -> submitter,
"participant_id" -> participantId,
),
),
V2.submitterCannotActViaParticipantStatus(details, submitter, participantId),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.SubmitterCannotActViaParticipant
.RejectWithSubmitterAndParticipantId(
details,
submitter,
participantId,
)
.asGrpcStatusFromContext
)
}
@nowarn("msg=deprecated")
def invalidLedgerTimeStatus(
entry: DamlTransactionRejectionEntry,
rejection: InvalidLedgerTime,
errorVersionSwitch: ValueSwitch,
rejection: InvalidLedgerTime
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
val ledgerTime = rejection.getLedgerTime
val ledgerTimeLowerBound = rejection.getLowerBound
val ledgerTimeUpperBound = rejection.getUpperBound
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Invalid ledger time: $details",
Map(
"ledger_time" -> ledgerTime.toString,
"lower_bound" -> ledgerTimeLowerBound.toString,
"upper_bound" -> ledgerTimeUpperBound.toString,
),
),
V2.invalidLedgerTimeStatus(
details,
ledgerTime = Instant
.ofEpochSecond(
ledgerTime.getSeconds,
ledgerTime.getNanos.toLong,
),
ledgerTimeLowerBound = Instant
.ofEpochSecond(
ledgerTimeLowerBound.getSeconds,
ledgerTimeLowerBound.getNanos.toLong,
),
ledgerTimeUpperBound = Instant
.ofEpochSecond(
ledgerTimeUpperBound.getSeconds,
ledgerTimeUpperBound.getNanos.toLong,
),
),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InvalidLedgerTime
.RejectEnriched(
cause = details,
ledger_time = Instant
.ofEpochSecond(
ledgerTime.getSeconds,
ledgerTime.getNanos.toLong,
),
ledger_time_lower_bound = Instant
.ofEpochSecond(
ledgerTimeLowerBound.getSeconds,
ledgerTimeLowerBound.getNanos.toLong,
),
ledger_time_upper_bound = Instant
.ofEpochSecond(
ledgerTimeUpperBound.getSeconds,
ledgerTimeUpperBound.getNanos.toLong,
),
)
.asGrpcStatusFromContext
)
}
@nowarn("msg=deprecated")
def resourceExhaustedStatus(
entry: DamlTransactionRejectionEntry,
rejection: ResourcesExhausted,
errorVersionSwitch: ValueSwitch,
rejection: ResourcesExhausted
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Resources exhausted: $details",
),
V2.resourceExhaustedStatus(details),
)
KVErrors.Resources.ResourceExhausted
.Reject(details)
.asStatus
}
@deprecated
def partyNotKnownOnLedgerStatus(
entry: DamlTransactionRejectionEntry,
rejection: PartyNotKnownOnLedger,
errorVersionSwitch: ValueSwitch,
rejection: PartyNotKnownOnLedger
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
errorVersionSwitch.choose(
V1.status(
entry,
Code.INVALID_ARGUMENT,
s"Party not known on ledger: $details",
),
V2.partyNotKnownOnLedgerStatus(details),
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.RejectDeprecated(details)
.asGrpcStatusFromContext
)
}
@deprecated
def inconsistentStatus(
entry: DamlTransactionRejectionEntry,
rejection: Inconsistent,
errorVersionSwitch: ValueSwitch,
rejection: Inconsistent
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
errorVersionSwitch.choose(
V1.status(
entry,
Code.ABORTED,
s"Inconsistent: $details",
),
V2.inconsistentStatus(details),
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.Inconsistent
.Reject(details)
.asGrpcStatusFromContext
)
}
@deprecated
def disputedStatus(
entry: DamlTransactionRejectionEntry,
rejection: Disputed,
errorVersionSwitch: ValueSwitch,
rejection: Disputed
)(implicit loggingContext: ContextualizedErrorLogger): Status = {
val details = rejection.getDetails
errorVersionSwitch.choose(
V1.disputedStatus(entry, details, Code.INVALID_ARGUMENT),
V2.disputedStatus(details),
)
}
private object V2 {
def externallyDuplicateKeysStatus()(implicit
loggingContext: ContextualizedErrorLogger
): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.DuplicateContractKey
.Reject(ExternallyInconsistentTransaction.DuplicateKeys.description)
.asGrpcStatusFromContext
)
def externallyInconsistentKeysStatus(
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InconsistentContractKey
.Reject(ExternallyInconsistentTransaction.InconsistentKeys.description)
.asGrpcStatusFromContext
)
def externallyInconsistentContractsStatus(
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InconsistentContracts
.Reject(ExternallyInconsistentTransaction.InconsistentContracts.description)
.asGrpcStatusFromContext
)
def submitterCannotActViaParticipantStatus(
details: String,
submitter: String,
participantId: String,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.SubmitterCannotActViaParticipant
.RejectWithSubmitterAndParticipantId(
details,
submitter,
participantId,
)
.asGrpcStatusFromContext
)
def recordTimeOutOfRangeStatus(
minimumRecordTime: Instant,
maximumRecordTime: Instant,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Time.RecordTimeOutOfRange
.Reject(minimumRecordTime, maximumRecordTime)
.asStatus
def invalidRecordTimeRejectionStatus(
rejectionEntry: DamlTransactionRejectionEntry,
recordTime: Timestamp,
tooEarlyUntil: Option[Timestamp],
tooLateFrom: Option[Timestamp],
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Time.InvalidRecordTime
.Reject(
rejectionEntry.getDefiniteAnswer,
invalidRecordTimeReason(recordTime, tooEarlyUntil, tooLateFrom),
recordTime.toInstant,
tooEarlyUntil.map(_.toInstant),
tooLateFrom.map(_.toInstant),
)
.asStatus
def causalMonotonicityViolatedStatus(
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Time.CausalMonotonicityViolated
.Reject()
.asStatus
def duplicateCommandsRejectionStatus(
definiteAnswer: Boolean = false,
existingCommandSubmissionId: Option[String],
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.DuplicateCommand
.Reject(definiteAnswer, existingCommandSubmissionId)
.asGrpcStatusFromContext
)
def rejectionReasonNotSetStatus(
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Internal.RejectionReasonNotSet
.Reject()
.asStatus
def invalidParticipantStateStatus(
details: String,
metadata: Map[String, String],
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Internal.InvalidParticipantState
.Reject(details, metadata)
.asStatus
def validationFailureStatus(
details: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Consistency.ValidationFailure
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Disputed
.Reject(details)
.asStatus
def missingInputStateStatus(
key: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Internal.MissingInputState
.Reject(key)
.asStatus
def internallyInconsistentKeysStatus(
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Internal.InternallyInconsistentKeys
.Reject(InternallyInconsistentTransaction.InconsistentKeys.description)
.asGrpcStatusFromContext
)
def internallyDuplicateKeysStatus(
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Internal.InternallyDuplicateKeys
.Reject(InternallyInconsistentTransaction.DuplicateKeys.description)
.asGrpcStatusFromContext
)
def submittingPartyNotKnownOnLedgerStatus(
submitter: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.SubmittingPartyNotKnownOnLedger
.Reject(submitter)
.asGrpcStatusFromContext
)
def partiesNotKnownOnLedgerStatus(
parties: Seq[String]
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.Reject(parties.toSet)
.asGrpcStatusFromContext
)
def resourceExhaustedStatus(
details: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
KVErrors.Resources.ResourceExhausted
.Reject(details)
.asStatus
@deprecated
def invalidLedgerTimeStatus(
details: String,
ledgerTime: Instant,
ledgerTimeLowerBound: Instant,
ledgerTimeUpperBound: Instant,
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.InvalidLedgerTime
.RejectEnriched(details, ledgerTime, ledgerTimeLowerBound, ledgerTimeUpperBound)
.asGrpcStatusFromContext
)
@deprecated
def partyNotKnownOnLedgerStatus(
details: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.PartyNotKnownOnLedger
.RejectDeprecated(details)
.asGrpcStatusFromContext
)
@deprecated
def inconsistentStatus(
details: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.Inconsistent
.Reject(details)
.asGrpcStatusFromContext
)
@deprecated
def disputedStatus(
details: String
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.WriteServiceRejections.Disputed
.Reject(details)
.asGrpcStatusFromContext
)
}
@deprecated
private object V1 {
def status(
entry: DamlTransactionRejectionEntry,
code: Code,
message: String,
additionalMetadata: Map[String, String] = Map.empty,
): Status = Status.of(
code.value,
message,
Seq(
AnyProto.pack[ErrorInfo](
ErrorInfo(metadata =
additionalMetadata + (GrpcStatuses.DefiniteAnswerKey -> entry.getDefiniteAnswer.toString)
)
)
),
)
def disputedStatus(
entry: DamlTransactionRejectionEntry,
rejectionString: String,
code: Code,
): Status = status(
entry,
code,
s"Disputed: $rejectionString",
)
def invalidRecordTimeRejectionStatus(
rejectionEntry: DamlTransactionRejectionEntry,
reason: String,
errorCode: Code,
): Status = Status.of(
errorCode.value,
reason,
Seq(
AnyProto.pack[ErrorInfo](
ErrorInfo(metadata =
Map(
GrpcStatuses.DefiniteAnswerKey -> rejectionEntry.getDefiniteAnswer.toString
)
)
)
),
)
def duplicateCommandsRejectionStatus(
definiteAnswer: Boolean,
errorCode: Code,
): Status = Status.of(
errorCode.value,
"Duplicate commands",
Seq(
AnyProto.pack[ErrorInfo](
// the definite answer is false, as the rank-based deduplication is not yet implemented
ErrorInfo(metadata =
Map(
GrpcStatuses.DefiniteAnswerKey -> definiteAnswer.toString
)
)
)
),
.asGrpcStatusFromContext
)
}
private def duplicateCommandsRejectionStatus(
definiteAnswer: Boolean = false,
existingCommandSubmissionId: Option[String],
)(implicit loggingContext: ContextualizedErrorLogger): Status =
GrpcStatus.toProto(
LedgerApiErrors.ConsistencyErrors.DuplicateCommand
.Reject(definiteAnswer, existingCommandSubmissionId)
.asGrpcStatusFromContext
)
private def invalidRecordTimeReason(
recordTime: Timestamp,
tooEarlyUntil: Option[Timestamp],
@ -724,10 +320,4 @@ private[kvutils] object TransactionRejections {
"Record time outside of valid range"
}
private def toJsonString(parties: ProtocolStringList): String = {
val stringWriter = new StringWriter
val objectMapper = new ObjectMapper
objectMapper.writeValue(stringWriter, parties)
stringWriter.toString
}
}

View File

@ -6,7 +6,6 @@ package com.daml.ledger.participant.state.kvutils
import java.time.Duration
import com.codahale.metrics.MetricRegistry
import com.daml.daml_lf_dev.DamlLf
import com.daml.error.ValueSwitch
import com.daml.ledger.api.DeduplicationPeriod
import com.daml.ledger.configuration.{Configuration, LedgerTimeModel}
import com.daml.ledger.participant.state.kvutils.KeyValueCommitting.PreExecutionResult
@ -65,8 +64,6 @@ object KVTest {
)
private[kvutils] val metrics = new Metrics(new MetricRegistry)
private[kvutils] val errorVersionSwitch =
new ValueSwitch(enableSelfServiceErrorCodes = true)
private def initialTestState: KVTestState = {
val engine = Engine.DevEngine()
@ -391,7 +388,6 @@ object KVTest {
val _ = KeyValueConsumption.logEntryToUpdate(
entryId,
logEntry,
errorVersionSwitch,
)(loggingContext)
entryId -> logEntry
@ -422,13 +418,11 @@ object KVTest {
KeyValueConsumption.logEntryToUpdate(
entryId,
successfulLogEntry,
errorVersionSwitch,
recordTimeFromTimeUpdateLogEntry,
)(loggingContext)
KeyValueConsumption.logEntryToUpdate(
entryId,
outOfTimeBoundsLogEntry,
errorVersionSwitch,
recordTimeFromTimeUpdateLogEntry,
)(loggingContext)

View File

@ -3,7 +3,7 @@
package com.daml.ledger.participant.state.kvutils
import com.daml.error.{DamlContextualizedErrorLogger, ErrorResource, ValueSwitch}
import com.daml.error.{DamlContextualizedErrorLogger, ErrorResource}
import com.daml.ledger.api.DeduplicationPeriod
import com.daml.ledger.configuration.LedgerTimeModel
import com.daml.ledger.participant.state.kvutils.Conversions._
@ -17,13 +17,7 @@ import com.daml.ledger.participant.state.kvutils.store.events.{
DamlSubmitterInfo,
DamlTransactionBlindingInfo,
DamlTransactionRejectionEntry,
Disputed,
Duplicate,
Inconsistent,
InvalidLedgerTime,
PartyNotKnownOnLedger,
ResourcesExhausted,
SubmitterCannotActViaParticipant,
}
import com.daml.ledger.participant.state.v2.Update.CommandRejected
import com.daml.lf.crypto
@ -38,8 +32,6 @@ import com.daml.lf.transaction.{BlindingInfo, NodeId, TransactionOuterClass, Tra
import com.daml.lf.value.Value.ContractId
import com.daml.lf.value.ValueOuterClass
import com.daml.logging.{ContextualizedLogger, LoggingContext}
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.protobuf.{TextFormat, Timestamp}
import com.google.rpc.error_details.{ErrorInfo, ResourceInfo}
import io.grpc.Status.Code
import org.scalatest.OptionValues
@ -50,7 +42,6 @@ import org.scalatest.wordspec.AnyWordSpec
import java.time.Duration
import scala.annotation.nowarn
import scala.collection.immutable.{ListMap, ListSet}
import scala.collection.mutable
import scala.jdk.CollectionConverters._
@nowarn("msg=deprecated")
@ -124,113 +115,6 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
"encode/decode rejections" should {
val submitterInfo = DamlSubmitterInfo.newBuilder().build()
val now = LfTimestamp.now()
"convert rejection to proto models and back to expected grpc v1 code" in {
forAll(
Table[Rejection, Code, Map[String, String]](
("Rejection", "Expected Code", "Expected Additional Details"),
(
Rejection.ValidationFailure(Error.Package(Error.Package.Internal("ERROR", "ERROR"))),
Code.INVALID_ARGUMENT,
Map.empty,
),
(
Rejection.InternallyInconsistentTransaction.InconsistentKeys,
Code.INVALID_ARGUMENT,
Map.empty,
),
(
Rejection.InternallyInconsistentTransaction.DuplicateKeys,
Code.INVALID_ARGUMENT,
Map.empty,
),
(
Rejection.ExternallyInconsistentTransaction.InconsistentContracts,
Code.ABORTED,
Map.empty,
),
(
Rejection.ExternallyInconsistentTransaction.InconsistentKeys,
Code.ABORTED,
Map.empty,
),
(
Rejection.ExternallyInconsistentTransaction.DuplicateKeys,
Code.ABORTED,
Map.empty,
),
(
Rejection.MissingInputState(DamlStateKey.getDefaultInstance),
Code.ABORTED,
Map.empty,
),
(
Rejection.InvalidParticipantState(Err.InternalError("error")),
Code.INVALID_ARGUMENT,
Map.empty,
),
(
Rejection.InvalidParticipantState(
Err.ArchiveDecodingFailed(Ref.PackageId.assertFromString("id"), "reason")
),
Code.INVALID_ARGUMENT,
Map("package_id" -> "id"),
),
(
Rejection.InvalidParticipantState(Err.MissingDivulgedContractInstance("id")),
Code.INVALID_ARGUMENT,
Map("contract_id" -> "id"),
),
(
Rejection.RecordTimeOutOfRange(LfTimestamp.Epoch, LfTimestamp.Epoch),
Code.ABORTED,
Map(
"minimum_record_time" -> LfTimestamp.Epoch.toString,
"maximum_record_time" -> LfTimestamp.Epoch.toString,
),
),
(
Rejection.LedgerTimeOutOfRange(LedgerTimeModel.OutOfRange(now, now, now)),
Code.ABORTED,
Map.empty,
),
(
Rejection.CausalMonotonicityViolated,
Code.ABORTED,
Map.empty,
),
(
Rejection.PartiesNotKnownOnLedger(Seq.empty),
Code.INVALID_ARGUMENT,
Map.empty,
),
(
Rejection.MissingInputState(partyStateKey("party")),
Code.ABORTED,
Map("key" -> "party: \"party\"\n"),
),
(
Rejection.SubmittingPartyNotKnownOnLedger(party0),
Code.INVALID_ARGUMENT,
Map("submitter_party" -> party0),
),
(
Rejection.PartiesNotKnownOnLedger(Iterable(party0, party1)),
Code.INVALID_ARGUMENT,
Map("parties" -> s"""[\"$party0\",\"$party1\"]"""),
),
)
) { (rejection, expectedCode, expectedAdditionalDetails) =>
checkErrors(
v1ErrorSwitch,
submitterInfo,
rejection,
expectedCode,
expectedAdditionalDetails,
)
}
}
"convert rejection to proto models and back to expected grpc v2 code" in {
forAll(
@ -357,7 +241,6 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
)
) { (rejection, expectedCode, expectedAdditionalDetails, expectedResources) =>
checkErrors(
v2ErrorSwitch,
submitterInfo,
rejection,
expectedCode,
@ -366,134 +249,6 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
)
}
}
"produce metadata that can be easily parsed" in {
forAll(
Table[Rejection, String, String => Any, Any](
("rejection", "metadata key", "metadata parser", "expected parsed metadata"),
(
Rejection.MissingInputState(partyStateKey("party")),
"key",
TextFormat.parse(_, classOf[DamlStateKey]),
partyStateKey("party"),
),
(
Rejection.RecordTimeOutOfRange(LfTimestamp.Epoch, LfTimestamp.Epoch),
"minimum_record_time",
LfTimestamp.assertFromString,
LfTimestamp.Epoch,
),
(
Rejection.RecordTimeOutOfRange(LfTimestamp.Epoch, LfTimestamp.Epoch),
"maximum_record_time",
LfTimestamp.assertFromString,
LfTimestamp.Epoch,
),
(
Rejection.PartiesNotKnownOnLedger(Iterable(party0, party1)),
"parties",
jsonString => {
val objectMapper = new ObjectMapper
objectMapper.readValue(jsonString, classOf[java.util.List[_]]).asScala
},
mutable.Buffer(party0, party1),
),
)
) { (rejection, metadataKey, metadataParser, expectedParsedMetadata) =>
val encodedEntry = Conversions
.encodeTransactionRejectionEntry(
submitterInfo,
rejection,
)
.build()
val finalReason = Conversions
.decodeTransactionRejectionEntry(encodedEntry, v1ErrorSwitch)
finalReason.definiteAnswer shouldBe false
val actualDetails = finalReasonDetails(finalReason).toMap
metadataParser(actualDetails(metadataKey)) shouldBe expectedParsedMetadata
}
}
"convert v1 rejections" should {
"handle with expected status codes" in {
forAll(
Table[
DamlTransactionRejectionEntry.Builder => DamlTransactionRejectionEntry.Builder,
Code,
Map[String, String],
](
("rejection builder", "code", "expected additional details"),
(
_.setInconsistent(Inconsistent.newBuilder()),
Code.ABORTED,
Map.empty,
),
(
_.setDisputed(Disputed.newBuilder()),
Code.INVALID_ARGUMENT,
Map.empty,
),
(
_.setResourcesExhausted(ResourcesExhausted.newBuilder()),
Code.ABORTED,
Map.empty,
),
(
_.setPartyNotKnownOnLedger(PartyNotKnownOnLedger.newBuilder()),
Code.INVALID_ARGUMENT,
Map.empty,
),
(
_.setDuplicateCommand(Duplicate.newBuilder().setSubmissionId("not_used")),
Code.ALREADY_EXISTS,
Map(),
),
(
_.setSubmitterCannotActViaParticipant(
SubmitterCannotActViaParticipant
.newBuilder()
.setSubmitterParty("party")
.setParticipantId("id")
),
Code.PERMISSION_DENIED,
Map(
"submitter_party" -> "party",
"participant_id" -> "id",
),
),
(
_.setInvalidLedgerTime(
InvalidLedgerTime
.newBuilder()
.setLowerBound(Timestamp.newBuilder().setSeconds(1L))
.setLedgerTime(Timestamp.newBuilder().setSeconds(2L))
.setUpperBound(Timestamp.newBuilder().setSeconds(3L))
),
Code.ABORTED,
Map(
"lower_bound" -> "seconds: 1\n",
"ledger_time" -> "seconds: 2\n",
"upper_bound" -> "seconds: 3\n",
),
),
)
) { (rejectionBuilder, code, expectedAdditionalDetails) =>
{
val finalReason = Conversions
.decodeTransactionRejectionEntry(
rejectionBuilder(DamlTransactionRejectionEntry.newBuilder())
.build(),
v1ErrorSwitch,
)
finalReason.code shouldBe code.value()
finalReason.definiteAnswer shouldBe false
val actualDetails = finalReasonDetails(finalReason)
actualDetails should contain allElementsOf expectedAdditionalDetails
}
}
}
}
}
"decode duplicate command v2" in {
@ -502,8 +257,7 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
DamlTransactionRejectionEntry
.newBuilder()
.setDuplicateCommand(Duplicate.newBuilder().setSubmissionId("submissionId"))
.build(),
v2ErrorSwitch,
.build()
)
finalReason.code shouldBe Code.ALREADY_EXISTS.value()
finalReason.definiteAnswer shouldBe false
@ -666,12 +420,11 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
}
private def checkErrors(
errorVersionSwitch: ValueSwitch,
submitterInfo: DamlSubmitterInfo,
rejection: Rejection,
expectedCode: Code,
expectedAdditionalDetails: Map[String, String],
expectedResources: Map[ErrorResource, String] = Map.empty,
expectedResources: Map[ErrorResource, String],
) = {
val encodedEntry = Conversions
.encodeTransactionRejectionEntry(
@ -680,7 +433,7 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
)
.build()
val finalReason = Conversions
.decodeTransactionRejectionEntry(encodedEntry, errorVersionSwitch)
.decodeTransactionRejectionEntry(encodedEntry)
finalReason.code shouldBe expectedCode.value()
finalReason.definiteAnswer shouldBe false
val actualDetails = finalReasonDetails(finalReason)
@ -754,9 +507,6 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues {
)
.build
private lazy val v1ErrorSwitch = new ValueSwitch(enableSelfServiceErrorCodes = false)
private lazy val v2ErrorSwitch = new ValueSwitch(enableSelfServiceErrorCodes = true)
private[this] val txVersion = TransactionVersion.StableVersions.max
private def deduplicationKeyBytesFor(parties: List[String]): Array[Byte] = {

View File

@ -3,8 +3,6 @@
package com.daml.ledger.participant.state.kvutils
import com.daml.error.ValueSwitch
import java.time.Duration
import com.daml.ledger.participant.state.kvutils.TestHelpers._
import com.daml.ledger.participant.state.kvutils.store.events.DamlTransactionRejectionEntry
@ -41,9 +39,6 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers with Inside {
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
private val errorVersionSwitch =
new ValueSwitch(enableSelfServiceErrorCodes = false)
private val alice = party("Alice")
private val bob = party("Bob")
private val eve = party("Eve")
@ -101,7 +96,6 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers with Inside {
KeyValueConsumption.logEntryToUpdate(
entryId,
logEntry,
errorVersionSwitch,
)(loggingContext)
)
@ -226,7 +220,6 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers with Inside {
KeyValueConsumption.logEntryToUpdate(
entryId,
preExecutionResult.successfulLogEntry,
errorVersionSwitch,
Some(recordTime),
)(loggingContext)
)
@ -314,7 +307,6 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers with Inside {
KeyValueConsumption.logEntryToUpdate(
entryId,
logEntry,
errorVersionSwitch,
)(loggingContext)
)
@ -535,7 +527,6 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers with Inside {
KeyValueConsumption.logEntryToUpdate(
entryId,
strippedEntry.build,
errorVersionSwitch,
)(loggingContext)
inside(updates) { case Seq(txAccepted: Update.TransactionAccepted) =>
txAccepted.optCompletionInfo should be(None)
@ -591,7 +582,6 @@ class KVUtilsTransactionSpec extends AnyWordSpec with Matchers with Inside {
KeyValueConsumption.logEntryToUpdate(
entryId,
preExecutionResult.successfulLogEntry,
errorVersionSwitch,
Some(recordTime),
)(loggingContext)
)

View File

@ -3,7 +3,6 @@
package com.daml.ledger.participant.state.kvutils
import com.daml.error.ValueSwitch
import com.daml.ledger.configuration.Configuration
import com.daml.ledger.grpc.GrpcStatuses
import com.daml.ledger.participant.state.kvutils.Conversions.buildTimestamp
@ -37,7 +36,7 @@ import org.scalatest.Inside.inside
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks._
import org.scalatest.prop.Tables.Table
import org.scalatest.prop.{TableFor1, TableFor4, TableFor5}
import org.scalatest.prop.TableFor4
import org.scalatest.wordspec.AnyWordSpec
import java.time.Instant
@ -58,76 +57,50 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
.setPackageUploadEntry(DamlPackageUploadEntry.getDefaultInstance)
.build
private val errorVersionsTable: TableFor1[ValueSwitch] = Table[ValueSwitch](
"Error Version",
v1ErrorSwitch,
v2ErrorSwitch,
)
private implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
"logEntryToUpdate" should {
"throw in case no record time is available from the log entry or input argument" in {
forAll(
errorVersionsTable
) { errorSwitch =>
assertThrows[Err](
logEntryToUpdate(
aLogEntryId,
aLogEntryWithoutRecordTime,
errorSwitch,
recordTimeForUpdate = None,
)(loggingContext)
)
}
assertThrows[Err](
logEntryToUpdate(
aLogEntryId,
aLogEntryWithoutRecordTime,
recordTimeForUpdate = None,
)(loggingContext)
)
}
"use log entry's record time instead of one provided as input" in {
forAll(
errorVersionsTable
) { errorSwitch =>
val actual :: Nil = logEntryToUpdate(
aLogEntryId,
aLogEntryWithRecordTime,
errorSwitch,
recordTimeForUpdate = Some(aRecordTime),
)(loggingContext)
val actual :: Nil = logEntryToUpdate(
aLogEntryId,
aLogEntryWithRecordTime,
recordTimeForUpdate = Some(aRecordTime),
)(loggingContext)
actual.recordTime shouldBe aRecordTimeFromLogEntry
}
actual.recordTime shouldBe aRecordTimeFromLogEntry
}
"use record time from log entry if not provided as input" in {
forAll(
errorVersionsTable
) { errorSwitch =>
val actual :: Nil =
logEntryToUpdate(
aLogEntryId,
aLogEntryWithRecordTime,
errorSwitch,
recordTimeForUpdate = None,
)(loggingContext)
val actual :: Nil =
logEntryToUpdate(
aLogEntryId,
aLogEntryWithRecordTime,
recordTimeForUpdate = None,
)(loggingContext)
actual.recordTime shouldBe Timestamp.assertFromInstant(Instant.ofEpochSecond(100))
}
actual.recordTime shouldBe Timestamp.assertFromInstant(Instant.ofEpochSecond(100))
}
"not generate an update from a time update entry" in {
forAll(
errorVersionsTable
) { errorSwitch =>
val timeUpdateEntry = DamlLogEntry.newBuilder
.setRecordTime(Conversions.buildTimestamp(aRecordTime))
.setTimeUpdateEntry(Empty.getDefaultInstance)
.build
logEntryToUpdate(
aLogEntryId,
timeUpdateEntry,
errorSwitch,
recordTimeForUpdate = None,
)(loggingContext) shouldBe Nil
}
val timeUpdateEntry = DamlLogEntry.newBuilder
.setRecordTime(Conversions.buildTimestamp(aRecordTime))
.setTimeUpdateEntry(Empty.getDefaultInstance)
.build
logEntryToUpdate(
aLogEntryId,
timeUpdateEntry,
recordTimeForUpdate = None,
)(loggingContext) shouldBe Nil
}
}
@ -190,21 +163,17 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
}
"generate update for deduplicated transaction with definite answer set to true" in {
forAll(
errorVersionsTable
) { errorSwitch =>
val inputEntry = buildOutOfTimeBoundsEntry(
TimeBounds(deduplicateUntil = Some(aRecordTime)),
TRANSACTION_REJECTION_ENTRY,
definiteAnswer = Some(true),
val inputEntry = buildOutOfTimeBoundsEntry(
TimeBounds(deduplicateUntil = Some(aRecordTime)),
TRANSACTION_REJECTION_ENTRY,
definiteAnswer = Some(true),
)
val actual = outOfTimeBoundsEntryToUpdate(aRecordTime, inputEntry)
inside(actual) { case Some(CommandRejected(_, _, FinalReason(status))) =>
status.code shouldBe Code.ALREADY_EXISTS.value
unpackErrorInfo(status.details) should contain allElementsOf Map(
GrpcStatuses.DefiniteAnswerKey -> "true"
)
val actual = outOfTimeBoundsEntryToUpdate(aRecordTime, inputEntry, errorSwitch)
inside(actual) { case Some(CommandRejected(_, _, FinalReason(status))) =>
status.code shouldBe Code.ALREADY_EXISTS.value
unpackErrorInfo(status.details) should contain allElementsOf Map(
GrpcStatuses.DefiniteAnswerKey -> "true"
)
}
}
}
@ -230,57 +199,8 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
case _ => fail()
}
val testCases = Table(
("Error Version", "Time Bounds", "Record Time", "Log Entry Type", "Assertions"),
("Time Bounds", "Record Time", "Log Entry Type", "Assertions"),
(
v1ErrorSwitch,
TimeBounds(
tooLateFrom = Some(Timestamp.assertFromInstant(aRecordTimeInstant.minusMillis(1)))
),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
Assertions(verify = { update =>
verifyCommandRejection(update)
verifyStatusCode(update, Code.ABORTED)
}),
),
(
v1ErrorSwitch,
TimeBounds(
tooEarlyUntil = Some(Timestamp.assertFromInstant(aRecordTimeInstant.plusMillis(1)))
),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
Assertions(verify = { update =>
verifyCommandRejection(update)
verifyStatusCode(update, Code.ABORTED)
}),
),
(
v1ErrorSwitch,
TimeBounds(tooLateFrom = Some(aRecordTime)),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
Assertions(verify = verifyNoUpdateIsGenerated),
),
(
v1ErrorSwitch,
TimeBounds(tooEarlyUntil = Some(aRecordTime)),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
Assertions(verify = verifyNoUpdateIsGenerated),
),
(
v1ErrorSwitch,
TimeBounds(
tooEarlyUntil = Some(Timestamp.assertFromInstant(aRecordTimeInstant.minusMillis(1))),
tooLateFrom = Some(Timestamp.assertFromInstant(aRecordTimeInstant.plusMillis(1))),
),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
Assertions(verify = verifyNoUpdateIsGenerated),
),
(
v2ErrorSwitch,
TimeBounds(
tooLateFrom = Some(Timestamp.assertFromInstant(aRecordTimeInstant.minusMillis(1)))
),
@ -292,7 +212,6 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
}),
),
(
v2ErrorSwitch,
TimeBounds(
tooEarlyUntil = Some(Timestamp.assertFromInstant(aRecordTimeInstant.plusMillis(1)))
),
@ -304,14 +223,12 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
}),
),
(
v2ErrorSwitch,
TimeBounds(tooLateFrom = Some(aRecordTime)),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
Assertions(verify = verifyNoUpdateIsGenerated),
),
(
v2ErrorSwitch,
TimeBounds(tooEarlyUntil = Some(aRecordTime)),
aRecordTime,
TRANSACTION_REJECTION_ENTRY,
@ -319,7 +236,6 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
),
// Record time within time bounds.
(
v2ErrorSwitch,
TimeBounds(
tooEarlyUntil = Some(Timestamp.assertFromInstant(aRecordTimeInstant.minusMillis(1))),
tooLateFrom = Some(Timestamp.assertFromInstant(aRecordTimeInstant.plusMillis(1))),
@ -447,11 +363,10 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
}
private def runAll(
table: TableFor5[ValueSwitch, TimeBounds, Timestamp, DamlLogEntry.PayloadCase, Assertions]
table: TableFor4[TimeBounds, Timestamp, DamlLogEntry.PayloadCase, Assertions]
): Unit =
forAll(table) {
(
errorVersionSwitch: ValueSwitch,
timeBounds: TimeBounds,
recordTime: Timestamp,
logEntryType: DamlLogEntry.PayloadCase,
@ -460,38 +375,15 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
val inputEntry = buildOutOfTimeBoundsEntry(timeBounds, logEntryType)
if (assertions.throwsInternalError) {
assertThrows[Err.InternalError](
outOfTimeBoundsEntryToUpdate(recordTime, inputEntry, errorVersionSwitch)
outOfTimeBoundsEntryToUpdate(recordTime, inputEntry)
)
} else {
val actual = outOfTimeBoundsEntryToUpdate(recordTime, inputEntry, errorVersionSwitch)
val actual = outOfTimeBoundsEntryToUpdate(recordTime, inputEntry)
assertions.verify(actual)
()
}
}
private def runAll(
table: TableFor4[TimeBounds, Timestamp, DamlLogEntry.PayloadCase, Assertions]
): Unit = {
val (head1, head2, head3, head4) = table.heading
runAll(
Table(
heading = ("Error Version", head1, head2, head3, head4),
rows = table.flatMap {
case (
timeBounds: TimeBounds,
recordTime: Timestamp,
logEntryType: DamlLogEntry.PayloadCase,
assertions: Assertions,
) =>
Seq(
(v1ErrorSwitch, timeBounds, recordTime, logEntryType, assertions),
(v2ErrorSwitch, timeBounds, recordTime, logEntryType, assertions),
)
}: _*,
)
)
}
@nowarn("msg=deprecated")
private def buildOutOfTimeBoundsEntry(
timeBounds: TimeBounds,
@ -570,11 +462,4 @@ class KeyValueConsumptionSpec extends AnyWordSpec with Matchers {
else
Map.empty[String, String]
}
private lazy val v1ErrorSwitch = new ValueSwitch(enableSelfServiceErrorCodes = false) {
override def toString: String = "1"
}
private lazy val v2ErrorSwitch = new ValueSwitch(enableSelfServiceErrorCodes = true) {
override def toString: String = "2"
}
}

View File

@ -6,7 +6,6 @@ package com.daml.ledger.participant.state.kvutils.api
import akka.NotUsed
import akka.stream.scaladsl.{Sink, Source}
import com.codahale.metrics.MetricRegistry
import com.daml.error.ValueSwitch
import com.daml.ledger.api.testing.utils.AkkaBeforeAndAfterAll
import com.daml.ledger.offset.Offset
import com.daml.ledger.participant.state.kvutils.api.KeyValueParticipantStateReader.offsetForUpdate
@ -247,18 +246,16 @@ object KeyValueParticipantStateReaderSpec {
private val zeroUpdateGenerator: (
DamlLogEntryId,
DamlLogEntry,
ValueSwitch,
Option[Timestamp],
) => LoggingContext => List[Update] =
(_, _, _, _) => _ => List.empty
(_, _, _) => _ => List.empty
private val singleUpdateGenerator: (
DamlLogEntryId,
DamlLogEntry,
ValueSwitch,
Option[Timestamp],
) => LoggingContext => List[Update] =
(_, _, _, _) =>
(_, _, _) =>
_ =>
List(
Update.PartyAddedToParticipant(
@ -273,20 +270,17 @@ object KeyValueParticipantStateReaderSpec {
private val twoUpdatesGenerator: (
DamlLogEntryId,
DamlLogEntry,
ValueSwitch,
Option[Timestamp],
) => LoggingContext => List[Update] =
(entryId, entry, errorVersionSwitch, recordTime) =>
(entryId, entry, recordTime) =>
loggingContext =>
singleUpdateGenerator(
entryId,
entry,
errorVersionSwitch,
recordTime,
)(loggingContext) ::: singleUpdateGenerator(
entryId,
entry,
errorVersionSwitch,
recordTime,
)(loggingContext)
@ -309,7 +303,6 @@ object KeyValueParticipantStateReaderSpec {
logEntryToUpdate: (
DamlLogEntryId,
DamlLogEntry,
ValueSwitch,
Option[Timestamp],
) => LoggingContext => List[Update] = singleUpdateGenerator,
failOnUnexpectedEvent: Boolean = true,
@ -317,7 +310,6 @@ object KeyValueParticipantStateReaderSpec {
new KeyValueParticipantStateReader(
reader,
new Metrics(new MetricRegistry),
enableSelfServiceErrorCodes = true,
logEntryToUpdate,
() => None,
failOnUnexpectedEvent,

View File

@ -142,8 +142,6 @@ class DeduplicationPeriodSupportSpec
when(
errorFactories.invalidDeduplicationPeriod(
any[String],
any[String],
any[Option[Boolean]],
any[Option[Duration]],
)(any[ContextualizedErrorLogger])
).thenReturn(statusRuntimeException)
@ -159,8 +157,6 @@ class DeduplicationPeriodSupportSpec
)
verify(errorFactories).invalidDeduplicationPeriod(
any[String],
any[String],
any[Option[Boolean]],
any[Option[Duration]],
)(any[ContextualizedErrorLogger])
result shouldBe statusRuntimeException

View File

@ -66,7 +66,6 @@ final class LogAppendingReadServiceFactory(
keyValueSource,
metrics,
failOnUnexpectedEvent = false,
enableSelfServiceErrorCodes = true,
)
new ReplayingReadService {
override def updateCount(): Long = recordedBlocksSnapshot.length.toLong

View File

@ -313,7 +313,6 @@ object RecoveringIndexerIntegrationSpec {
KeyValueParticipantStateReader(
reader = reader,
metrics = metrics,
enableSelfServiceErrorCodes = true,
),
new KeyValueParticipantStateWriter(
writer = writer,

View File

@ -66,7 +66,6 @@ final case class SandboxConfig(
managementServiceTimeout: Duration,
sqlStartMode: Option[PostgresStartupMode],
enableCompression: Boolean,
enableSelfServiceErrorCodes: Boolean,
userManagementConfig: UserManagementConfig,
) {
@ -166,7 +165,6 @@ object SandboxConfig {
managementServiceTimeout = DefaultManagementServiceTimeout,
sqlStartMode = Some(DefaultSqlStartupMode),
enableCompression = false,
enableSelfServiceErrorCodes = true,
userManagementConfig = UserManagementConfig.default(true),
)

View File

@ -67,7 +67,6 @@ object ConfigConverter {
configurationLoadTimeout = sandboxConfig.configurationLoadTimeout,
commandConfig = sandboxConfig.commandConfig,
enableInMemoryFanOutForLedgerApi = false,
enableSelfServiceErrorCodes = sandboxConfig.enableSelfServiceErrorCodes,
eventsPageSize = sandboxConfig.eventsPageSize,
eventsProcessingParallelism = sandboxConfig.eventsProcessingParallelism,
extra = extraBridgeConfig,

Some files were not shown because too many files have changed in this diff Show More