Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[DPP-618][Self-service error codes] Adapt error codes in ApiPackageService #11284

Merged
merged 10 commits into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import com.daml.error.definitions.ErrorGroups.ParticipantErrorGroup.TransactionE
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.PackageId
import com.daml.lf.engine.Error.Validation.ReplayMismatch
import com.daml.lf.engine.{Error => LfError}
import com.daml.lf.interpretation.{Error => LfInterpretationError}
import com.daml.lf.language.{LanguageVersion, LookupError, Reference}
import com.daml.lf.transaction.GlobalKey
import com.daml.lf.value.Value
import com.daml.lf.{VersionRange, language}
import com.daml.lf.engine.{Error => LfError}
import com.daml.lf.interpretation.{Error => LfInterpretationError}

object LedgerApiErrors extends LedgerApiErrorGroup {

Expand All @@ -33,6 +33,40 @@ object LedgerApiErrors extends LedgerApiErrorGroup {
}

object ReadErrors extends ErrorGroup() {

@Explanation("This rejection is given when a package id is malformed.")
@Resolution(
"""Make sure the package id provided in the request has correct form.""".stripMargin
)
object MalformedPackageId
extends ErrorCode(
id = "MALFORMED_PACKAGE_ID",
ErrorCategory.InvalidIndependentOfSystemState,
) {
case class Reject(message: String)(implicit
loggingContext: ContextualizedErrorLogger
) extends LoggingTransactionErrorImpl(
cause = message
)
}
tudor-da marked this conversation as resolved.
Show resolved Hide resolved

@Explanation(
"This rejection is given when a read request tries to access a package which does not exist."
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
)
@Resolution("""Make sure the requested package is available on the ledger.
|It might have not been uploaded or the upload might have been rejected.""".stripMargin)
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
object CouldNotFindPackage
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
extends ErrorCode(
id = "COULD_NOT_FIND_PACKAGE",
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
ErrorCategory.InvalidGivenCurrentSystemStateResourceMissing,
) {
case class Reject()(implicit
loggingContext: ContextualizedErrorLogger
) extends LoggingTransactionErrorImpl(
cause = "Could not found package."
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
)
}

@Explanation("This rejection is given when a read request tries to access pruned data.")
@Resolution("Use an offset that is after the pruning offset.")
object ParticipantPrunedDataAccessed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ object ValidationLogger {
t
}

def logFailureWithContext[Request](request: Request, t: Throwable)(implicit
def logFailureWithContext[Request, T <: Throwable](request: Request, t: T)(implicit
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
logger: ContextualizedLogger,
loggingContext: LoggingContext,
): Throwable = {
): T = {
logger.debug(s"Request validation failed for $request. Message: ${t.getMessage}")
logger.info(t.getMessage)
t
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import com.daml.error.definitions.LedgerApiErrors
import com.daml.error.{ContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.grpc.GrpcStatuses
import com.daml.platform.server.api.ApiException
import com.daml.logging.{ContextualizedLogger, LoggingContext}
import com.daml.platform.server.api.validation.ErrorFactories.{
addDefiniteAnswerDetails,
definiteAnswers,
}
import com.daml.platform.server.api.{ApiException, ValidationLogger}
import com.google.protobuf.{Any => AnyProto}
import com.google.rpc.{ErrorInfo, Status}
import io.grpc.Status.Code
Expand All @@ -21,6 +22,35 @@ import scalaz.syntax.tag._

class ErrorFactories private (errorCodesVersionSwitcher: ErrorCodesVersionSwitcher) {

def malformedPackageId[Request](request: Request, message: String)(implicit
contextualizedErrorLogger: ContextualizedErrorLogger,
logger: ContextualizedLogger,
loggingContext: LoggingContext,
): StatusRuntimeException = {
errorCodesVersionSwitcher.choose(
v1 = ValidationLogger.logFailureWithContext(
request,
io.grpc.Status.INVALID_ARGUMENT
.withDescription(message)
.asRuntimeException(),
),
v2 = LedgerApiErrors.ReadErrors.MalformedPackageId
.Reject(
message = message
)
.asGrpcError,
)
}

def couldNotFindPackage(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException = {
errorCodesVersionSwitcher.choose(
v1 = io.grpc.Status.NOT_FOUND.asRuntimeException(),
v2 = LedgerApiErrors.ReadErrors.CouldNotFindPackage.Reject().asGrpcError,
)
}

def duplicateCommandException(implicit
contextualizedErrorLogger: ContextualizedErrorLogger
): StatusRuntimeException =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
package com.daml

import com.daml.error.{
DamlContextualizedErrorLogger,
ContextualizedErrorLogger,
DamlContextualizedErrorLogger,
ErrorCodesVersionSwitcher,
}
import com.daml.ledger.api.domain.LedgerId
Expand All @@ -31,6 +31,33 @@ class ErrorFactoriesSpec extends AnyWordSpec with Matchers with TableDrivenPrope
new DamlContextualizedErrorLogger(logger, loggingContext, Some(correlationId))

"ErrorFactories" should {

"return malformedPackageId" in {
assertVersionedError(
_.malformedPackageId(request = "request123", message = "message123")(
contextualizedErrorLogger = contextualizedErrorLogger,
logger = logger,
loggingContext = loggingContext,
)
)(
v1_code = Code.INVALID_ARGUMENT,
v1_message = "message123",
v1_details = Seq.empty,
v2_code = Code.INVALID_ARGUMENT,
v2_message = s"MALFORMED_PACKAGE_ID(8,$correlationId): message123",
)
}

"return couldNotFindPackage" in {
assertVersionedError(_.couldNotFindPackage)(
v1_code = Code.NOT_FOUND,
v1_message = "",
v1_details = Seq.empty,
v2_code = Code.NOT_FOUND,
v2_message = s"COULD_NOT_FIND_PACKAGE(11,$correlationId): Could not found package.",
)
}

"return the DuplicateCommandException" in {
assertVersionedError(_.duplicateCommandException)(
v1_code = Code.ALREADY_EXISTS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ private[daml] object ApiServices {
val apiVersionService =
ApiVersionService.create()

val apiPackageService = ApiPackageService.create(ledgerId, packagesService)
val apiPackageService =
ApiPackageService.create(ledgerId, packagesService, errorsVersionsSwitcher)

val apiConfigurationService =
ApiLedgerConfigurationService.create(ledgerId, configurationService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,34 @@
package com.daml.platform.apiserver.services

import com.daml.daml_lf_dev.DamlLf.{Archive, HashFunction}
import com.daml.error.{DamlContextualizedErrorLogger, ErrorCodesVersionSwitcher}
import com.daml.ledger.api.domain.LedgerId
import com.daml.ledger.api.v1.package_service.HashFunction.{
SHA256 => APISHA256,
Unrecognized => APIUnrecognized,
}
import com.daml.ledger.api.v1.package_service.PackageServiceGrpc.PackageService
import com.daml.ledger.api.v1.package_service.{HashFunction => APIHashFunction, _}
import com.daml.ledger.participant.state.index.v2.IndexPackagesService
import com.daml.lf.data.Ref
import com.daml.logging.LoggingContext.withEnrichedLoggingContext
import com.daml.logging.{ContextualizedLogger, LoggingContext}
import com.daml.platform.api.grpc.GrpcApiService
import com.daml.platform.server.api.ValidationLogger
import com.daml.platform.server.api.validation.PackageServiceValidation
import io.grpc.{BindableService, ServerServiceDefinition, Status}
import com.daml.platform.server.api.validation.{ErrorFactories, PackageServiceValidation}
import io.grpc.{BindableService, ServerServiceDefinition}

import scala.concurrent.{ExecutionContext, Future}

private[apiserver] final class ApiPackageService private (
backend: IndexPackagesService
backend: IndexPackagesService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit executionContext: ExecutionContext, loggingContext: LoggingContext)
extends PackageService
with GrpcApiService {

private implicit val logger: ContextualizedLogger = ContextualizedLogger.get(this.getClass)

private implicit val contextualizedErrorLogger: DamlContextualizedErrorLogger =
new DamlContextualizedErrorLogger(logger, loggingContext, None)
tudor-da marked this conversation as resolved.
Show resolved Hide resolved

private val errorFactories = ErrorFactories(errorCodesVersionSwitcher)

override def bindService(): ServerServiceDefinition =
PackageServiceGrpc.bindService(this, executionContext)

Expand All @@ -47,13 +49,14 @@ private[apiserver] final class ApiPackageService private (
withEnrichedLoggingContext("packageId" -> request.packageId) { implicit loggingContext =>
logger.info(s"Received request for a package: $request")
withValidatedPackageId(request.packageId, request) { packageId =>
def archiveOToResponse(archiveO: Option[Archive]): Future[GetPackageResponse] =
archiveO.fold(
Future.failed[GetPackageResponse](errorFactories.couldNotFindPackage)
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
)(archive => Future.successful(toGetPackageResponse(archive)))

tudor-da marked this conversation as resolved.
Show resolved Hide resolved
backend
.getLfArchive(packageId)
.flatMap(
_.fold(Future.failed[GetPackageResponse](Status.NOT_FOUND.asRuntimeException()))(
archive => Future.successful(toGetPackageResponse(archive))
)
)
.flatMap(archiveOToResponse)
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
.andThen(logger.logErrorsOnCall[GetPackageResponse])
}
}
Expand All @@ -80,28 +83,27 @@ private[apiserver] final class ApiPackageService private (

private def withValidatedPackageId[T, R](packageId: String, request: R)(
block: Ref.PackageId => Future[T]
) =
): Future[T] =
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
Ref.PackageId
.fromString(packageId)
.fold(
error =>
errorMessage =>
Future.failed[T](
ValidationLogger.logFailureWithContext(
request,
Status.INVALID_ARGUMENT
.withDescription(error)
.asRuntimeException(),
)
errorFactories.malformedPackageId(request = request, message = errorMessage)
),
pId => block(pId),
packageId => block(packageId),
)

private def toGetPackageResponse(archive: Archive): GetPackageResponse = {
val hashF: APIHashFunction = archive.getHashFunction match {
case HashFunction.SHA256 => APISHA256
case _ => APIUnrecognized(-1)
val hashFunction = archive.getHashFunction match {
case HashFunction.SHA256 => APIHashFunction.SHA256
case _ => APIHashFunction.Unrecognized(-1)
}
GetPackageResponse(hashF, archive.getPayload, archive.getHash)
GetPackageResponse(
hashFunction = hashFunction,
archivePayload = archive.getPayload,
hash = archive.getHash,
)
tudor-da marked this conversation as resolved.
Show resolved Hide resolved
}

}
Expand All @@ -110,12 +112,21 @@ private[platform] object ApiPackageService {
def create(
ledgerId: LedgerId,
backend: IndexPackagesService,
errorCodesVersionSwitcher: ErrorCodesVersionSwitcher,
)(implicit
executionContext: ExecutionContext,
loggingContext: LoggingContext,
): PackageService with GrpcApiService =
new PackageServiceValidation(new ApiPackageService(backend), ledgerId) with BindableService {
): PackageService with GrpcApiService = {
val service = new ApiPackageService(
backend = backend,
errorCodesVersionSwitcher = errorCodesVersionSwitcher,
)
new PackageServiceValidation(
service = service,
ledgerId = ledgerId,
) with BindableService {
override def bindService(): ServerServiceDefinition =
PackageServiceGrpc.bindService(this, executionContext)
}
}
}