From 46863f9ce1eb07639776a48854e60c78cfcb753f Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Sat, 10 Aug 2024 01:21:54 +0400 Subject: [PATCH 01/12] create schema that are DID url resolvable Signed-off-by: Shota Jolbordi --- .../SchemaRegistryEndpoints.scala | 67 ++++++++++++---- .../SchemaRegistryServerEndpoints.scala | 30 +++++-- .../CredentialSchemaController.scala | 10 ++- .../CredentialSchemaControllerImpl.scala | 48 +++++++++--- .../http/CredentialSchemaDidUrlResponse.scala | 78 +++++++++++++++++++ .../core/model/ResourceResolutionMethod.scala | 18 +++++ .../core/model/schema/CredentialSchema.scala | 11 ++- .../CredentialSchemaRepository.scala | 3 +- .../service/CredentialSchemaService.scala | 3 +- .../service/CredentialSchemaServiceImpl.scala | 26 +++++-- ...n_method_to_schema_and_cred_definition.sql | 10 +++ .../sql/model/db/CredentialSchema.scala | 12 ++- .../identus/pollux/sql/model/db/package.scala | 41 +++++++++- .../JdbcCredentialSchemaRepository.scala | 6 +- 14 files changed, 314 insertions(+), 49 deletions(-) create mode 100644 cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala create mode 100644 pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/ResourceResolutionMethod.scala create mode 100644 pollux/sql-doobie/src/main/resources/sql/pollux/V24__add_resolution_method_to_schema_and_cred_definition.sql diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala index 26e21ea5c9..3186e03a33 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -9,6 +9,7 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityL import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader import org.hyperledger.identus.pollux.credentialschema.http.{ + CredentialSchemaDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, @@ -16,17 +17,7 @@ import org.hyperledger.identus.pollux.credentialschema.http.{ } import sttp.apispec.{ExternalDocumentation, Tag} import sttp.model.StatusCode -import sttp.tapir.{ - endpoint, - extractFromRequest, - path, - query, - statusCode, - stringToPath, - Endpoint, - EndpointInput, - PublicEndpoint -} +import sttp.tapir.* import sttp.tapir.json.zio.{jsonBody, schemaForZioJsonValue} import zio.json.ast.Json @@ -66,7 +57,42 @@ object SchemaRegistryEndpoints { val tag = Tag(name = tagName, description = Option(tagDescription), externalDocs = Option(tagExternalDocumentation)) - val createSchemaEndpoint: Endpoint[ + val createSchemaDidUrlEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, CredentialSchemaInput), + ErrorResponse, + CredentialSchemaDidUrlResponse, + Any + ] = + endpoint.post + .securityIn(apiKeyHeader) + .securityIn(jwtAuthHeader) + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in("schema-registry" / "schemas" / "did-url") + .in( + jsonBody[CredentialSchemaInput] + .description( + "JSON object required for the credential schema creation" + ) + ) + .out( + statusCode(StatusCode.Created) + .description( + "The new credential schema record is successfully created" + ) + ) + .out(jsonBody[CredentialSchemaDidUrlResponse]) + .description("Credential schema record") + .errorOut(basicFailureAndNotFoundAndForbidden) + .name("createSchema") + .summary("Publish new schema to the schema registry, did url resolvable") + .description( + "Create the new credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + + "The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." + ) + .tag(tagName) + + val createSchemaHttpUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), (RequestContext, CredentialSchemaInput), ErrorResponse, @@ -94,7 +120,7 @@ object SchemaRegistryEndpoints { .description("Credential schema record") .errorOut(basicFailureAndNotFoundAndForbidden) .name("createSchema") - .summary("Publish new schema to the schema registry") + .summary("Publish new schema to the schema registry, http url resolvable") .description( "Create the new credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + "The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." @@ -143,7 +169,7 @@ object SchemaRegistryEndpoints { val getSchemaByIdEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - CredentialSchemaResponse, + CredentialSchemaResponse | CredentialSchemaDidUrlResponse, Any ] = endpoint.get @@ -153,7 +179,18 @@ object SchemaRegistryEndpoints { "Globally unique identifier of the credential schema record" ) ) - .out(jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`")) + .out( + oneOf[CredentialSchemaResponse | CredentialSchemaDidUrlResponse]( + oneOfVariant( + jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`, resolvable by HTTP URL") + ), + oneOfVariant( + jsonBody[CredentialSchemaDidUrlResponse].description( + "CredentialSchema found by `guid`, resolvable by DID URL" + ) + ) + ) + ) .errorOut(basicFailuresAndNotFound) .name("getSchemaById") .summary("Fetch the schema from the registry by `guid`") diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala index 75c8f4b1ec..7967c0c428 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala @@ -9,18 +9,21 @@ import org.hyperledger.identus.pollux.credentialschema.http.{CredentialSchemaInp import org.hyperledger.identus.pollux.credentialschema.SchemaRegistryEndpoints.* import org.hyperledger.identus.shared.models.WalletAccessContext import org.hyperledger.identus.LogUtils.* +import org.hyperledger.identus.agent.server.config.AppConfig import sttp.tapir.ztapir.* import zio.* import java.util.UUID class SchemaRegistryServerEndpoints( + config: AppConfig, credentialSchemaController: CredentialSchemaController, authenticator: Authenticator[BaseEntity], authorizer: Authorizer[BaseEntity] ) { - val createSchemaServerEndpoint: ZServerEndpoint[Any, Any] = - createSchemaEndpoint + + val createSchemaHttpUrlServerEndpoint: ZServerEndpoint[Any, Any] = + createSchemaHttpUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) .serverLogic { wac => { case (ctx: RequestContext, schemaInput: CredentialSchemaInput) => @@ -31,6 +34,18 @@ class SchemaRegistryServerEndpoints( } } + val createSchemaDidUrlServerEndpoint: ZServerEndpoint[Any, Any] = + createSchemaDidUrlEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => { + case (ctx: RequestContext, schemaInput: CredentialSchemaInput) => + credentialSchemaController + .createSchemaDidUrl(config, schemaInput)(ctx) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(ctx) + } + } + val updateSchemaServerEndpoint: ZServerEndpoint[Any, Any] = updateSchemaEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) @@ -47,7 +62,7 @@ class SchemaRegistryServerEndpoints( getSchemaByIdEndpoint .zServerLogic { case (ctx: RequestContext, guid: UUID) => credentialSchemaController - .getSchemaByGuid(guid)(ctx) + .getSchemaByGuid(config, guid)(ctx) .logTrace(ctx) } @@ -75,8 +90,9 @@ class SchemaRegistryServerEndpoints( val all: List[ZServerEndpoint[Any, Any]] = List( - createSchemaServerEndpoint, - updateSchemaServerEndpoint, + createSchemaHttpUrlServerEndpoint, + createSchemaDidUrlServerEndpoint, + updateSchemaServerEndpoint, // TODO: get back to it, see if changes are needed getSchemaByIdServerEndpoint, getRawSchemaByIdServerEndpoint, lookupSchemasByQueryServerEndpoint @@ -84,11 +100,13 @@ class SchemaRegistryServerEndpoints( } object SchemaRegistryServerEndpoints { - def all: URIO[CredentialSchemaController & DefaultAuthenticator, List[ZServerEndpoint[Any, Any]]] = { + def all: URIO[CredentialSchemaController & DefaultAuthenticator & AppConfig, List[ZServerEndpoint[Any, Any]]] = { for { authenticator <- ZIO.service[DefaultAuthenticator] + config <- ZIO.service[AppConfig] schemaRegistryService <- ZIO.service[CredentialSchemaController] schemaRegistryEndpoints = new SchemaRegistryServerEndpoints( + config, schemaRegistryService, authenticator, authenticator diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala index 938ddfaed1..cb1b8afcf8 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -1,8 +1,10 @@ package org.hyperledger.identus.pollux.credentialschema.controller +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} import org.hyperledger.identus.pollux.credentialschema.http.{ + CredentialSchemaDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, @@ -20,13 +22,17 @@ trait CredentialSchemaController { rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] + def createSchemaDidUrl(config: AppConfig, in: CredentialSchemaInput)(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] + def updateSchema(author: String, id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] - def getSchemaByGuid(id: UUID)(implicit + def getSchemaByGuid(config: AppConfig, id: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaResponse] + ): IO[ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] def getSchemaJsonByGuid(id: UUID)(implicit rc: RequestContext diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 3804d24a4e..5b4bddf3be 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -1,13 +1,17 @@ package org.hyperledger.identus.pollux.credentialschema.controller +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.{ManagedDIDState, PublicationState} import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{CollectionStats, Order, Pagination} import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID} +import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.FilteredEntries +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.service.CredentialSchemaService import org.hyperledger.identus.pollux.credentialschema.http.{ + CredentialSchemaDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, @@ -30,13 +34,31 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] = { for { - validated <- validatePrismDID(in.author) + _ <- validatePrismDID(in.author) result <- service .create(toDomain(in)) .map(cs => fromDomain(cs).withBaseUri(rc.request.uri)) } yield result } + def createSchemaDidUrl(config: AppConfig, in: CredentialSchemaInput)(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] = { + val res = for { + validated <- validatePrismDID(in.author) + serviceName = config.agent.httpEndpoint.serviceName + result <- service.create(toDomain(in), ResourceResolutionMethod.DID) + response <- ZIO + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(result, serviceName)) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) + + } yield response + + res + } + override def updateSchema(author: String, id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] = { @@ -48,15 +70,23 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI } yield result } - override def getSchemaByGuid(guid: UUID)(implicit + override def getSchemaByGuid(config: AppConfig, guid: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaResponse] = { - service - .getByGUID(guid) - .map( - fromDomain(_) - .withSelf(rc.request.uri.toString) - ) + ): IO[ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = { + val res: IO[ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = for { + cs <- service.getByGUID(guid) + serviceName = config.agent.httpEndpoint.serviceName + response <- cs.resolutionMethod match + case model.ResourceResolutionMethod.DID => + ZIO + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, serviceName)) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) + case model.ResourceResolutionMethod.HTTP => ZIO.succeed(fromDomain(cs).withSelf(rc.request.uri.toString)) + } yield response + + res } override def getSchemaJsonByGuid(guid: UUID)(implicit diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala new file mode 100644 index 0000000000..7d00fe5a53 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -0,0 +1,78 @@ +package org.hyperledger.identus.pollux.credentialschema.http + +import org.hyperledger.identus.api.http.* +import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} +import org.hyperledger.identus.pollux.core.model +import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponse.annotations +import org.hyperledger.identus.shared.crypto.Sha256Hash +import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} +import sttp.model.Uri +import sttp.model.Uri.* +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName} +import zio.json.* + +import scala.collection.immutable.ListMap + +case class CredentialSchemaDidUrlResponse( + @description(annotations.resource.description) + @encodedExample(annotations.resource.example) + resource: String, + @description(annotations.schemaUrl.description) + @encodedExample(annotations.schemaUrl.example) + schemaUrl: String, +) + +object CredentialSchemaDidUrlResponse { + + def fromDomain(cs: CredentialSchema, serviceName: String): Either[String, CredentialSchemaDidUrlResponse] = { + +// Url : TODO: change in codebase with DIDUrl usage +// .parse(s"${jwtIssuer.did}?resourceService=$statusListRegistryServiceName&resourcePath=$resourcePath#$segment") +// .toString +// , + for { + authorDid <- PrismDID.fromString(cs.author) + canonicalized <- JsonUtils.canonicalizeToJcs(cs.toJson).left.map(_.toString) + encoded = Base64Utils.encodeURL(canonicalized.getBytes) + hash = Sha256Hash.compute(encoded.getBytes).hexEncoded + didUrl = DIDUrl( + authorDid.did, + Seq(), + ListMap( + "resourceService" -> Seq(serviceName), + "resourcePath" -> Seq(s"schema-registry/schemas/${cs.guid}" ), + "resourceHash" -> Seq(hash) + ), + None + ).toString + } yield CredentialSchemaDidUrlResponse( + resource = encoded, + schemaUrl = didUrl + ) + } + + + given encoder: zio.json.JsonEncoder[CredentialSchemaDidUrlResponse] = + DeriveJsonEncoder.gen[CredentialSchemaDidUrlResponse] + given decoder: zio.json.JsonDecoder[CredentialSchemaDidUrlResponse] = + DeriveJsonDecoder.gen[CredentialSchemaDidUrlResponse] + given schema: Schema[CredentialSchemaDidUrlResponse] = Schema.derived + + object annotations { + object resource + extends Annotation[String]( + description = "JCS normalized and base64url encoded json schema", + example = + """ewogICJndWlkIjogIjNmODZhNzNmLTViNzgtMzljNy1hZjc3LTBjMTYxMjNmYTljMiIsCiAgImlkIjogImYyYmZiZjc4LThiZDYtNGNjNi04YjM5LWIzYTI1ZTAxZThlYSIsCiAgImxvbmdJZCI6ICJkaWQ6cHJpc206YWdlbnQvZjJiZmJmNzgtOGJkNi00Y2M2LThiMzktYjNhMjVlMDFlOGVhP3ZlcnNpb249MS4wLjAiLAogICJuYW1lIjogImRyaXZpbmctbGljZW5zZSIsCiAgInZlcnNpb24iOiAiMS4wLjAiLAogICJkZXNjcmlwdGlvbiI6ICJEcml2aW5nIExpY2Vuc2UgU2NoZW1hIiwKICAidHlwZSI6ICJodHRwczovL3czYy1jY2cuZ2l0aHViLmlvL3ZjLWpzb24tc2NoZW1hcy9zY2hlbWEvMi4wL3NjaGVtYS5qc29uIiwKICAiYXV0aG9yIjogImRpZDpwcmlzbTo0YTViNWNmMGE1MTNlODNiNTk4YmJlYTI1Y2Q2MTk2NzQ2NzQ3ZjM2MWE3M2VmNzcwNjgyNjhiYzliZDczMmZmIiwKICAiYXV0aG9yZWQiOiAiMjAyMy0wMy0xNFQxNDo0MTo0Ni43MTM5NDNaIiwKICAidGFncyI6IFsKICAgICJkcml2aW5nIiwKICAgICJsaWNlbnNlIgogIF0sCiAgInNjaGVtYSI6IHsKICAgICIkaWQiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9kcml2aW5nLWxpY2Vuc2UtMS4wLjAiLAogICAgIiRzY2hlbWEiOiAiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWEiLAogICAgImRlc2NyaXB0aW9uIjogIkRyaXZpbmcgTGljZW5zZSIsCiAgICAidHlwZSI6ICJvYmplY3QiLAogICAgInByb3BlcnRpZXMiOiB7CiAgICAgICJlbWFpbEFkZHJlc3MiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImVtYWlsIgogICAgICB9LAogICAgICAiZ2l2ZW5OYW1lIjogewogICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgfSwKICAgICAgImZhbWlseU5hbWUiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZGF0ZU9mSXNzdWFuY2UiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImRhdGUtdGltZSIKICAgICAgfSwKICAgICAgImRyaXZpbmdMaWNlbnNlSUQiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZHJpdmluZ0NsYXNzIjogewogICAgICAgICJ0eXBlIjogImludGVnZXIiCiAgICAgIH0KICAgIH0sCiAgICAicmVxdWlyZWQiOiBbCiAgICAgICJlbWFpbEFkZHJlc3MiLAogICAgICAiZmFtaWx5TmFtZSIsCiAgICAgICJkYXRlT2ZJc3N1YW5jZSIsCiAgICAgICJkcml2aW5nTGljZW5zZUlEIiwKICAgICAgImRyaXZpbmdDbGFzcyIKICAgIF0sCiAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgfQp9""" + ) + + object schemaUrl + extends Annotation[String]( + description = "DID url that can be used to resolve this schema", + example = + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" + ) + } +} diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/ResourceResolutionMethod.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/ResourceResolutionMethod.scala new file mode 100644 index 0000000000..03441de89d --- /dev/null +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/ResourceResolutionMethod.scala @@ -0,0 +1,18 @@ +package org.hyperledger.identus.pollux.core.model + +import zio.json.{JsonDecoder, JsonEncoder} + +enum ResourceResolutionMethod(val str: String) { + case DID extends ResourceResolutionMethod("did") + case HTTP extends ResourceResolutionMethod("http") +} + +object ResourceResolutionMethod { + given JsonEncoder[ResourceResolutionMethod] = JsonEncoder[String].contramap[ResourceResolutionMethod](_.str) + + given JsonDecoder[ResourceResolutionMethod] = JsonDecoder[String].mapOrFail { + case "did" => Right(ResourceResolutionMethod.DID) + case "http" => Right(ResourceResolutionMethod.HTTP) + case other => Left(s"Unknown ResourceResolutionMethod: $other") + } +} diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala index 853bc99a3f..b1f91e7d06 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala @@ -9,6 +9,7 @@ import org.hyperledger.identus.pollux.core.model.schema.`type`.{ } import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1 import org.hyperledger.identus.pollux.core.model.schema.validator.{JsonSchemaValidator, JsonSchemaValidatorImpl} +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.shared.http.UriResolver import zio.* import zio.json.* @@ -18,6 +19,7 @@ import java.net.URI import java.time.{OffsetDateTime, ZoneOffset} import java.util.UUID + type Schema = zio.json.ast.Json /** @param guid @@ -52,6 +54,7 @@ case class CredentialSchema( tags: Seq[String], description: String, `type`: String, + resolutionMethod: ResourceResolutionMethod, schema: Schema ) { def longId = CredentialSchema.makeLongId(author, id, version) @@ -65,13 +68,13 @@ object CredentialSchema { def makeGUID(author: String, id: UUID, version: String) = UUID.nameUUIDFromBytes(makeLongId(author, id, version).getBytes) - def make(in: Input): UIO[CredentialSchema] = { + def make(in: Input, resolutionMethod: ResourceResolutionMethod): UIO[CredentialSchema] = { for { id <- zio.Random.nextUUID - cs <- make(id, in) + cs <- make(id, in, resolutionMethod) } yield cs } - def make(id: UUID, in: Input): UIO[CredentialSchema] = { + def make(id: UUID, in: Input, resolutionMethod: ResourceResolutionMethod): UIO[CredentialSchema] = { for { ts <- zio.Clock.currentDateTime.map( _.atZoneSameInstant(ZoneOffset.UTC).toOffsetDateTime @@ -87,6 +90,7 @@ object CredentialSchema { tags = in.tags, description = in.description, `type` = in.`type`, + resolutionMethod = resolutionMethod, schema = in.schema ) } @@ -112,6 +116,7 @@ object CredentialSchema { ) case class FilteredEntries(entries: Seq[CredentialSchema], count: Long, totalCount: Long) + given JsonEncoder[CredentialSchema] = DeriveJsonEncoder.gen[CredentialSchema] given JsonDecoder[CredentialSchema] = DeriveJsonDecoder.gen[CredentialSchema] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala index d3fa782ba8..2f237d6af8 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.core.repository +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.* import org.hyperledger.identus.pollux.core.repository.Repository.SearchCapability @@ -17,7 +18,7 @@ trait CredentialSchemaRepository def update(cs: CredentialSchema): URIO[WalletAccessContext, CredentialSchema] - def getAllVersions(id: UUID, author: String): URIO[WalletAccessContext, List[String]] + def getAllVersions(id: UUID, author: String): URIO[WalletAccessContext, List[CredentialSchema]] def delete(guid: UUID): URIO[WalletAccessContext, CredentialSchema] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala index 1fdb69d04e..38ae584bb2 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.core.service +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaServiceError import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.* @@ -15,7 +16,7 @@ trait CredentialSchemaService { * @return * Created instance of the Credential Schema */ - def create(in: Input): Result[CredentialSchema] + def create(in: Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): Result[CredentialSchema] /** @param guid * Globally unique UUID of the credential schema diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala index ec96d7d678..f6724079f5 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala @@ -9,6 +9,7 @@ import org.hyperledger.identus.pollux.core.model.error.{ } import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.FilteredEntries +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.repository.CredentialSchemaRepository import org.hyperledger.identus.pollux.core.repository.Repository.SearchQuery import zio.* @@ -18,9 +19,12 @@ import java.util.UUID class CredentialSchemaServiceImpl( credentialSchemaRepository: CredentialSchemaRepository ) extends CredentialSchemaService { - override def create(in: CredentialSchema.Input): Result[CredentialSchema] = { + override def create( + in: CredentialSchema.Input, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): Result[CredentialSchema] = { for { - credentialSchema <- CredentialSchema.make(in) + credentialSchema <- CredentialSchema.make(in, resolutionMethod) _ <- CredentialSchema.validateCredentialSchema(credentialSchema) createdCredentialSchema <- credentialSchemaRepository.create(credentialSchema) } yield createdCredentialSchema @@ -48,11 +52,21 @@ class CredentialSchemaServiceImpl( in: CredentialSchema.Input ): Result[CredentialSchema] = { for { - cs <- CredentialSchema.make(guid, in) - _ <- CredentialSchema.validateCredentialSchema(cs).mapError(CredentialSchemaValidationError.apply) existingVersions <- credentialSchemaRepository.getAllVersions(guid, in.author) + _ <- if existingVersions.isEmpty then + ZIO.fail( + CredentialSchemaUpdateError( + guid, + in.version, + in.author, + s"No Schema exists of author: ${in.author}, with provided id: $guid" + ) + ) else ZIO.unit + resolutionMethod = existingVersions.head.resolutionMethod + cs <- CredentialSchema.make(guid, in, resolutionMethod) + _ <- CredentialSchema.validateCredentialSchema(cs).mapError(CredentialSchemaValidationError.apply) _ <- ZIO.fromOption(existingVersions.headOption).mapError(_ => CredentialSchemaGuidNotFoundError(guid)) - _ <- existingVersions.find(_ > in.version) match { + _ <- existingVersions.find(_.version > in.version) match { case Some(higherVersion) => ZIO.fail( CredentialSchemaUpdateError( @@ -65,7 +79,7 @@ class CredentialSchemaServiceImpl( case None => ZIO.succeed(cs) } - _ <- existingVersions.find(_ == in.version) match { + _ <- existingVersions.find(_.version == in.version) match { case Some(existingVersion) => ZIO.fail( CredentialSchemaUpdateError( diff --git a/pollux/sql-doobie/src/main/resources/sql/pollux/V24__add_resolution_method_to_schema_and_cred_definition.sql b/pollux/sql-doobie/src/main/resources/sql/pollux/V24__add_resolution_method_to_schema_and_cred_definition.sql new file mode 100644 index 0000000000..a85df4838b --- /dev/null +++ b/pollux/sql-doobie/src/main/resources/sql/pollux/V24__add_resolution_method_to_schema_and_cred_definition.sql @@ -0,0 +1,10 @@ +-- Create the enum type +CREATE TYPE resolution_method_enum AS ENUM ('http', 'did'); + +-- Add the column to credential_definition table +ALTER TABLE public.credential_definition + ADD COLUMN resolution_method resolution_method_enum NOT NULL DEFAULT 'http'; + +-- Add the column to credential_schema table +ALTER TABLE public.credential_schema + ADD COLUMN resolution_method resolution_method_enum NOT NULL DEFAULT 'http'; diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala index 358a175d70..8a3ac5f75c 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala @@ -6,7 +6,7 @@ import io.getquill.doobie.DoobieContext import io.getquill.idiom.* import org.hyperledger.identus.pollux.core.model.schema.Schema import org.hyperledger.identus.shared.models.WalletId - +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import java.time.temporal.ChronoUnit import java.time.OffsetDateTime import java.util.UUID @@ -22,6 +22,7 @@ case class CredentialSchema( description: String, `type`: String, schema: JsonValue[Schema], + resolutionMethod: ResourceResolutionMethod, walletId: WalletId ) { lazy val uniqueConstraintKey = author + name + version @@ -47,6 +48,7 @@ object CredentialSchema { description = m.description, `type` = m.`type`, schema = JsonValue(m.schema), + resolutionMethod = m.resolutionMethod, walletId = walletId ) @@ -63,12 +65,17 @@ object CredentialSchema { tags = db.tags, description = db.description, `type` = db.`type`, + resolutionMethod = db.resolutionMethod, schema = db.schema.value ) } } -object CredentialSchemaSql extends DoobieContext.Postgres(SnakeCase) with PostgresJsonExtensions { +object CredentialSchemaSql + extends DoobieContext.Postgres(SnakeCase) + with PostgresJsonExtensions + with PostgresEnumEncoders { + def insert(schema: CredentialSchema) = run { quote( query[CredentialSchema] @@ -90,7 +97,6 @@ object CredentialSchemaSql extends DoobieContext.Postgres(SnakeCase) with Postgr .filter(_.id == lift(id)) .filter(_.author == lift(author)) .sortBy(_.version)(ord = Ord.asc) - .map(_.version) ) } diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala index 652b137fc4..1ea1ad3abf 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala @@ -1,13 +1,50 @@ package org.hyperledger.identus.pollux.sql.model -import io.getquill.MappedEncoding +import doobie._ +import doobie.postgres._ +import doobie.postgres.implicits._ +import io.getquill.{MappedEncoding, PostgresJdbcContext, SnakeCase} +import io.getquill.doobie.DoobieContext +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.shared.models.WalletId - import java.util.UUID +import org.postgresql.util.PGobject package object db { given MappedEncoding[WalletId, UUID] = MappedEncoding(_.toUUID) given MappedEncoding[UUID, WalletId] = MappedEncoding(WalletId.fromUUID) + given mappedDecoderResourceResolutionMethod: MappedEncoding[ResourceResolutionMethod, String] = + MappedEncoding[ResourceResolutionMethod, String](_.str) + + given mappedEncoderResourceResolutionMethod: MappedEncoding[String, ResourceResolutionMethod] = + MappedEncoding[String, ResourceResolutionMethod] { + case "did" => ResourceResolutionMethod.DID + case "http" => ResourceResolutionMethod.HTTP + case other => throw new IllegalArgumentException(s"Unknown ResourceResolutionMethod: $other") + } + + trait PostgresEnumEncoders { + this: DoobieContext.Postgres[_] => + + given encoderResourceResolutionMethod: Encoder[ResourceResolutionMethod] = encoder[ResourceResolutionMethod]( + java.sql.Types.OTHER, + (index: Index, value: ResourceResolutionMethod, row: PrepareRow) => { + val pgObj = new PGobject() + pgObj.setType("resolution_method_enum") + pgObj.setValue(value.str) + row.setObject(index, pgObj, java.sql.Types.OTHER) + } + ) + + given decoderResourceResolutionMethod: Decoder[ResourceResolutionMethod] = decoder(row => + index => + row.getObject(index).toString match { + case "did" => ResourceResolutionMethod.DID + case "http" => ResourceResolutionMethod.HTTP + } + ) + } + } diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala index 606a1e577d..90e2fba15f 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.pollux.sql.repository import doobie.* import doobie.implicits.* import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.repository.{CredentialSchemaRepository, Repository} import org.hyperledger.identus.pollux.core.repository.Repository.* import org.hyperledger.identus.pollux.sql.model.db.{CredentialSchema as CredentialSchemaRow, CredentialSchemaSql} @@ -38,6 +39,7 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: ) } + // NOTE: this function is not used anywhere override def update(cs: CredentialSchema): URIO[WalletAccessContext, CredentialSchema] = { ZIO.serviceWithZIO[WalletAccessContext](ctx => CredentialSchemaSql @@ -48,11 +50,13 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: ) } - def getAllVersions(id: UUID, author: String): URIO[WalletAccessContext, List[String]] = { + def getAllVersions(id: UUID, author: String): URIO[WalletAccessContext, List[CredentialSchema]] = { CredentialSchemaSql .getAllVersions(id, author) .transactWallet(xa) .orDie + .map(_.map(CredentialSchemaRow.toModel)) + } override def delete(guid: UUID): URIO[WalletAccessContext, CredentialSchema] = { From d1514e04479957e15a34002d7012799d7915c7a2 Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Mon, 19 Aug 2024 16:27:30 +0400 Subject: [PATCH 02/12] List of schemas and fix update endpoint Signed-off-by: Shota Jolbordi --- .../SchemaRegistryEndpoints.scala | 24 ++++++--- .../SchemaRegistryServerEndpoints.scala | 7 +-- .../CredentialSchemaController.scala | 7 +-- .../CredentialSchemaControllerImpl.scala | 50 +++++++++++++++---- .../CredentialSchemaControllerLogic.scala | 6 +-- .../http/CredentialSchemaDidUrlResponse.scala | 3 +- .../http/CredentialSchemaResponsePage.scala | 9 ++-- 7 files changed, 74 insertions(+), 32 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala index 3186e03a33..1c74bc9475 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -129,9 +129,9 @@ object SchemaRegistryEndpoints { val updateSchemaEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), - (RequestContext, String, UUID, CredentialSchemaInput), + (RequestContext, UUID, CredentialSchemaInput), ErrorResponse, - CredentialSchemaResponse, + CredentialSchemaResponse | CredentialSchemaDidUrlResponse, Any ] = endpoint.put @@ -139,9 +139,9 @@ object SchemaRegistryEndpoints { .securityIn(jwtAuthHeader) .in(extractFromRequest[RequestContext](RequestContext.apply)) .in( - "schema-registry" / - path[String]("author").description(CredentialSchemaResponse.annotations.author.description) / - path[UUID]("id").description(CredentialSchemaResponse.annotations.id.description) + "schema-registry" / "schemas" / path[UUID]("id").description( + CredentialSchemaResponse.annotations.id.description + ) ) .in( jsonBody[CredentialSchemaInput] @@ -155,7 +155,18 @@ object SchemaRegistryEndpoints { "The credential schema record is successfully updated" ) ) - .out(jsonBody[CredentialSchemaResponse]) + .out( + oneOf[CredentialSchemaResponse | CredentialSchemaDidUrlResponse]( + oneOfVariant( + jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`, resolvable by HTTP URL") + ), + oneOfVariant( + jsonBody[CredentialSchemaDidUrlResponse].description( + "CredentialSchema found by `guid`, resolvable by DID URL" + ) + ) + ) + ) .description("Credential schema record") .errorOut(basicFailureAndNotFoundAndForbidden) .name("updateSchema") @@ -221,6 +232,7 @@ object SchemaRegistryEndpoints { private val credentialSchemaFilterInput: EndpointInput[FilterInput] = EndpointInput.derived[FilterInput] private val paginationInput: EndpointInput[PaginationInput] = EndpointInput.derived[PaginationInput] + val lookupSchemasByQueryEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), ( diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala index 7967c0c428..06a5302ddb 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala @@ -50,9 +50,9 @@ class SchemaRegistryServerEndpoints( updateSchemaEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) .serverLogic { wac => - { case (ctx: RequestContext, author: String, id: UUID, schemaInput: CredentialSchemaInput) => + { case (ctx: RequestContext, id: UUID, schemaInput: CredentialSchemaInput) => credentialSchemaController - .updateSchema(author, id, schemaInput)(ctx) + .updateSchema(id, schemaInput)(ctx) .provideSomeLayer(ZLayer.succeed(wac)) .logTrace(ctx) } @@ -81,7 +81,8 @@ class SchemaRegistryServerEndpoints( .lookupSchemas( filter, paginationInput.toPagination, - order + order, + config )(ctx) .provideSomeLayer(ZLayer.succeed(wac)) .logTrace(ctx) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala index cb1b8afcf8..b231a7481f 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -26,9 +26,9 @@ trait CredentialSchemaController { rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] - def updateSchema(author: String, id: UUID, in: CredentialSchemaInput)(implicit + def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] def getSchemaByGuid(config: AppConfig, id: UUID)(implicit rc: RequestContext @@ -45,7 +45,8 @@ trait CredentialSchemaController { def lookupSchemas( filter: FilterInput, pagination: Pagination, - order: Option[Order] + order: Option[Order], + config: AppConfig )(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponsePage] diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 5b4bddf3be..17f67a3201 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -1,11 +1,13 @@ package org.hyperledger.identus.pollux.credentialschema.controller +import cats.implicits.* import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.{ManagedDIDState, PublicationState} import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{CollectionStats, Order, Pagination} import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID} +import org.hyperledger.identus.pollux.core import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.FilteredEntries import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod @@ -21,6 +23,7 @@ import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaInpu import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaResponse.fromDomain import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* +import zio.json.* import zio.json.ast.Json import java.util.UUID @@ -59,15 +62,26 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI res } - override def updateSchema(author: String, id: UUID, in: CredentialSchemaInput)(implicit + override def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] = { - for { + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = { + val res = for { _ <- validatePrismDID(in.author) - result <- service - .update(id, toDomain(in).copy(author = author)) - .map(cs => fromDomain(cs).withBaseUri(rc.request.uri)) + cs <- service + .update(id, toDomain(in)) + result <- cs.resolutionMethod match + case ResourceResolutionMethod.DID => + ZIO + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, "")) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) + + case ResourceResolutionMethod.HTTP => + ZIO.succeed(fromDomain(cs).withBaseUri(rc.request.uri)) } yield result + + res } override def getSchemaByGuid(config: AppConfig, guid: UUID)(implicit @@ -113,7 +127,8 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI override def lookupSchemas( filter: FilterInput, pagination: Pagination, - order: Option[Order] + order: Option[Order], + config: AppConfig )(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponsePage] = { @@ -123,9 +138,24 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI pagination.offset, pagination.limit ) - entries = filteredEntries.entries - .map(fromDomain(_).withBaseUri(rc.request.uri)) - .toList + serviceName = config.agent.httpEndpoint.serviceName + entriesZio = filteredEntries.entries + .traverse(cs => + cs.resolutionMethod match { + case ResourceResolutionMethod.DID => + CredentialSchemaDidUrlResponse.fromDomain(cs, serviceName).flatMap(_.toJsonAST) + case ResourceResolutionMethod.HTTP => + fromDomain(cs).withBaseUri(rc.request.uri).toJsonAST + + } + ) + + entries <- ZIO + .fromEither(entriesZio) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) + page = CredentialSchemaResponsePage(entries) stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) } yield CredentialSchemaControllerLogic(rc, pagination, page, stats).result diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala index b4afaf255e..034054c5b8 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala @@ -30,11 +30,7 @@ case class CredentialSchemaControllerLogic( pageOf = pageOf, next = next, previous = previous, - contents = page.contents.map(item => - item.withBaseUri( - ctx.request.uri.copy(querySegments = Seq.empty) - ) - ) + contents = page.contents ) pageResult diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala index 7d00fe5a53..03fb551b4b 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -42,7 +42,7 @@ object CredentialSchemaDidUrlResponse { Seq(), ListMap( "resourceService" -> Seq(serviceName), - "resourcePath" -> Seq(s"schema-registry/schemas/${cs.guid}" ), + "resourcePath" -> Seq(s"schema-registry/schemas/${cs.guid}"), "resourceHash" -> Seq(hash) ), None @@ -53,7 +53,6 @@ object CredentialSchemaDidUrlResponse { ) } - given encoder: zio.json.JsonEncoder[CredentialSchemaDidUrlResponse] = DeriveJsonEncoder.gen[CredentialSchemaDidUrlResponse] given decoder: zio.json.JsonDecoder[CredentialSchemaDidUrlResponse] = diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala index ac2f0850ea..a23980d024 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala @@ -4,12 +4,15 @@ import org.hyperledger.identus.api.http.Annotation import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaResponsePage.annotations import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{description, encodedExample} -import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} +import zio.json.* +import zio.json.ast.Json +import sttp.tapir.generic.auto.* +import sttp.tapir.json.zio.schemaForZioJsonValue case class CredentialSchemaResponsePage( @description(annotations.contents.description) @encodedExample(annotations.contents.example) - contents: Seq[CredentialSchemaResponse], + contents: Seq[Json], // TODO: update type to CredentialSchemaResponse | CredentialSchemaDidUrlResponse once we update tapir to a version that supports sttp.tapir.Schema for union types @description(annotations.kind.description) @encodedExample(annotations.kind.example) kind: String = "CredentialSchemaPage", @@ -48,7 +51,7 @@ object CredentialSchemaResponsePage { object annotations { object contents - extends Annotation[Seq[CredentialSchemaResponse]]( + extends Annotation[Seq[Json]]( description = "A sequence of CredentialSchemaResponse objects representing the list of credential schemas that the API response contains", example = Seq.empty From 6bf680b070eadae6bf8805e532436ea96f589d90 Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Mon, 19 Aug 2024 16:32:32 +0400 Subject: [PATCH 03/12] Use valid service name in updat endpoint Signed-off-by: Shota Jolbordi --- .../credentialschema/SchemaRegistryServerEndpoints.scala | 2 +- .../controller/CredentialSchemaController.scala | 2 +- .../controller/CredentialSchemaControllerImpl.scala | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala index 06a5302ddb..19344fbf33 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala @@ -52,7 +52,7 @@ class SchemaRegistryServerEndpoints( .serverLogic { wac => { case (ctx: RequestContext, id: UUID, schemaInput: CredentialSchemaInput) => credentialSchemaController - .updateSchema(id, schemaInput)(ctx) + .updateSchema(config, id, schemaInput)(ctx) .provideSomeLayer(ZLayer.succeed(wac)) .logTrace(ctx) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala index b231a7481f..bb42ff3d46 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -26,7 +26,7 @@ trait CredentialSchemaController { rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] - def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit + def updateSchema(config: AppConfig, id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 17f67a3201..45a219fa24 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -62,17 +62,18 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI res } - override def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit + override def updateSchema(config: AppConfig, id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = { val res = for { _ <- validatePrismDID(in.author) + serviceName = config.agent.httpEndpoint.serviceName cs <- service .update(id, toDomain(in)) result <- cs.resolutionMethod match case ResourceResolutionMethod.DID => ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, "")) + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, serviceName)) .mapError(e => ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) ) From 2441b744a6f1fbc35853e4d9575692fb11d8c43f Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Thu, 29 Aug 2024 16:45:18 +0400 Subject: [PATCH 04/12] Split did and http url endpoints for schemas Signed-off-by: Shota Jolbordi --- .../controller/IssueControllerImpl.scala | 45 +++-- .../SchemaRegistryEndpoints.scala | 180 ++++++++++++++---- .../SchemaRegistryServerEndpoints.scala | 125 ++++++++---- .../CredentialSchemaController.scala | 35 +++- .../CredentialSchemaControllerImpl.scala | 140 ++++++++------ .../CredentialSchemaControllerLogic.scala | 38 ++-- .../http/CredentialSchemaDidUrlResponse.scala | 81 +++++++- .../CredentialSchemaDidUrlResponsePage.scala | 91 +++++++++ .../http/CredentialSchemaResponsePage.scala | 10 +- .../credentialschema/http/FilterInput.scala | 3 +- .../core/model/schema/CredentialSchema.scala | 5 +- .../CredentialSchemaRepository.scala | 4 +- .../service/CredentialSchemaService.scala | 6 +- .../service/CredentialSchemaServiceImpl.scala | 44 ++--- .../sql/model/db/CredentialSchema.scala | 38 +++- .../JdbcCredentialSchemaRepository.scala | 13 +- 16 files changed, 633 insertions(+), 225 deletions(-) create mode 100644 cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index bd8ec73851..250b3f641c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -1,6 +1,5 @@ package org.hyperledger.identus.issue.controller -import io.lemonlabs.uri.Url import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.server.ControllerHelper import org.hyperledger.identus.agent.walletapi.model.PublicationState @@ -9,7 +8,7 @@ import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.{ErrorResponse, RequestContext} import org.hyperledger.identus.api.http.model.{CollectionStats, PaginationInput} import org.hyperledger.identus.api.util.PaginationUtils -import org.hyperledger.identus.castor.core.model.did.{PrismDID, VerificationRelationship} +import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID, VerificationRelationship} import org.hyperledger.identus.castor.core.service.DIDService import org.hyperledger.identus.connect.core.service.ConnectionService import org.hyperledger.identus.issue.controller.http.{ @@ -21,14 +20,19 @@ import org.hyperledger.identus.issue.controller.http.{ import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID} import org.hyperledger.identus.pollux.core.model.CredentialFormat.{AnonCreds, JWT, SDJWT} import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.Role -import org.hyperledger.identus.pollux.core.service.CredentialService +import org.hyperledger.identus.pollux.core.service.{CredentialDefinitionService, CredentialService} import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} -import zio.{URLayer, ZIO, ZLayer} +import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} +import zio.* +import zio.json.given +import org.hyperledger.identus.shared.crypto.Sha256Hash +import scala.collection.immutable.ListMap import scala.language.implicitConversions class IssueControllerImpl( credentialService: CredentialService, + credentialDefinitionService: CredentialDefinitionService, connectionService: ConnectionService, didService: DIDService, managedDIDService: ManagedDIDService, @@ -92,16 +96,31 @@ class IssueControllerImpl( .mapError(_ => ErrorResponse.badRequest(detail = Some("Missing request parameter: credentialDefinitionId")) ) - credentialDefinitionId = { + credentialDefinition <- credentialDefinitionService.getByGUID(credentialDefinitionGUID) + credentialDefinitionId <- { val publicEndpointServiceName = appConfig.agent.httpEndpoint.serviceName val resourcePath = s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition" - val didUrl = Url - .parse(s"$issuingDID?resourceService=$publicEndpointServiceName&resourcePath=$resourcePath") - .toString - didUrl + val didUrl = for { + canonicalized <- JsonUtils.canonicalizeToJcs(credentialDefinition.definition.toJson) + encoded = Base64Utils.encodeURL(canonicalized.getBytes) + hash = Sha256Hash.compute(encoded.getBytes).hexEncoded + didUrl = DIDUrl( + issuingDID.did, + Seq(), + ListMap( + "resourceService" -> Seq(publicEndpointServiceName), + "resourcePath" -> Seq(resourcePath), + "resourceHash" -> Seq(hash) + ), + None + ).toString + } yield didUrl + + ZIO.fromEither(didUrl).mapError(_ => ErrorResponse.badRequest(detail = Some("Could not parse credential definition"))) + } record <- credentialService .createAnonCredsIssueCredentialRecord( @@ -244,7 +263,9 @@ class IssueControllerImpl( } object IssueControllerImpl { - val layer - : URLayer[CredentialService & ConnectionService & DIDService & ManagedDIDService & AppConfig, IssueController] = - ZLayer.fromFunction(IssueControllerImpl(_, _, _, _, _)) + val layer: URLayer[ + CredentialService & CredentialDefinitionService & ConnectionService & DIDService & ManagedDIDService & AppConfig, + IssueController + ] = + ZLayer.fromFunction(IssueControllerImpl(_, _, _, _, _, _)) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala index 1c74bc9475..713a773718 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -10,6 +10,8 @@ import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader import org.hyperledger.identus.pollux.credentialschema.http.{ CredentialSchemaDidUrlResponse, + CredentialSchemaDidUrlResponsePage, + CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, @@ -56,19 +58,21 @@ object SchemaRegistryEndpoints { ) val tag = Tag(name = tagName, description = Option(tagDescription), externalDocs = Option(tagExternalDocumentation)) + val httpUrlPathPrefix = "schema-registry" / "schemas" + val didUrlPathPrefix = "schema-registry" / "schemas" / "did-url" - val createSchemaDidUrlEndpoint: Endpoint[ + val createSchemaHttpUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), (RequestContext, CredentialSchemaInput), ErrorResponse, - CredentialSchemaDidUrlResponse, + CredentialSchemaResponse, Any ] = endpoint.post .securityIn(apiKeyHeader) .securityIn(jwtAuthHeader) .in(extractFromRequest[RequestContext](RequestContext.apply)) - .in("schema-registry" / "schemas" / "did-url") + .in(httpUrlPathPrefix) .in( jsonBody[CredentialSchemaInput] .description( @@ -81,29 +85,29 @@ object SchemaRegistryEndpoints { "The new credential schema record is successfully created" ) ) - .out(jsonBody[CredentialSchemaDidUrlResponse]) + .out(jsonBody[CredentialSchemaResponse]) .description("Credential schema record") .errorOut(basicFailureAndNotFoundAndForbidden) .name("createSchema") - .summary("Publish new schema to the schema registry, did url resolvable") + .summary("Publish new schema to the schema registry, http url resolvable") .description( "Create the new credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + "The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." ) .tag(tagName) - val createSchemaHttpUrlEndpoint: Endpoint[ + val createSchemaDidUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), (RequestContext, CredentialSchemaInput), ErrorResponse, - CredentialSchemaResponse, + CredentialSchemaDidUrlResponse, Any ] = endpoint.post .securityIn(apiKeyHeader) .securityIn(jwtAuthHeader) .in(extractFromRequest[RequestContext](RequestContext.apply)) - .in("schema-registry" / "schemas") + .in(didUrlPathPrefix) .in( jsonBody[CredentialSchemaInput] .description( @@ -116,22 +120,22 @@ object SchemaRegistryEndpoints { "The new credential schema record is successfully created" ) ) - .out(jsonBody[CredentialSchemaResponse]) + .out(jsonBody[CredentialSchemaDidUrlResponse]) .description("Credential schema record") .errorOut(basicFailureAndNotFoundAndForbidden) .name("createSchema") - .summary("Publish new schema to the schema registry, http url resolvable") + .summary("Publish new schema to the schema registry, did url resolvable") .description( "Create the new credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + "The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." ) .tag(tagName) - val updateSchemaEndpoint: Endpoint[ + val updateSchemaHttpUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), (RequestContext, UUID, CredentialSchemaInput), ErrorResponse, - CredentialSchemaResponse | CredentialSchemaDidUrlResponse, + CredentialSchemaResponse, Any ] = endpoint.put @@ -139,7 +143,7 @@ object SchemaRegistryEndpoints { .securityIn(jwtAuthHeader) .in(extractFromRequest[RequestContext](RequestContext.apply)) .in( - "schema-registry" / "schemas" / path[UUID]("id").description( + httpUrlPathPrefix / path[UUID]("id").description( CredentialSchemaResponse.annotations.id.description ) ) @@ -155,19 +159,47 @@ object SchemaRegistryEndpoints { "The credential schema record is successfully updated" ) ) + .out(jsonBody[CredentialSchemaResponse]) + .description("Credential schema record") + .errorOut(basicFailureAndNotFoundAndForbidden) + .name("updateSchema") + .summary("Publish the new version of the credential schema to the schema registry") + .description( + "Publish the new version of the credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + + "The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." + ) + .tag(tagName) + + val updateSchemaDidUrlEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID, CredentialSchemaInput), + ErrorResponse, + CredentialSchemaDidUrlResponse, + Any + ] = + endpoint.put + .securityIn(apiKeyHeader) + .securityIn(jwtAuthHeader) + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in( + didUrlPathPrefix / path[UUID]("id").description( + CredentialSchemaResponse.annotations.id.description + ) + ) + .in( + jsonBody[CredentialSchemaInput] + .description( + "JSON object required for the credential schema update" + ) + ) .out( - oneOf[CredentialSchemaResponse | CredentialSchemaDidUrlResponse]( - oneOfVariant( - jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`, resolvable by HTTP URL") - ), - oneOfVariant( - jsonBody[CredentialSchemaDidUrlResponse].description( - "CredentialSchema found by `guid`, resolvable by DID URL" - ) + statusCode(StatusCode.Ok) + .description( + "The credential schema record is successfully updated" ) - ) ) - .description("Credential schema record") + .out(jsonBody[CredentialSchemaDidUrlResponse]) + .description("Credential schema record wrapped in an envelope") .errorOut(basicFailureAndNotFoundAndForbidden) .name("updateSchema") .summary("Publish the new version of the credential schema to the schema registry") @@ -177,29 +209,44 @@ object SchemaRegistryEndpoints { ) .tag(tagName) - val getSchemaByIdEndpoint: PublicEndpoint[ + val getSchemaByIdHttpUrlEndpoint: PublicEndpoint[ + (RequestContext, UUID), + ErrorResponse, + CredentialSchemaResponse, + Any + ] = + endpoint.get + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in( + httpUrlPathPrefix / path[UUID]("guid").description( + "Globally unique identifier of the credential schema record" + ) + ) + .out(jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`")) + .errorOut(basicFailuresAndNotFound) + .name("getSchemaById") + .summary("Fetch the schema from the registry by `guid`") + .description( + "Fetch the credential schema by the unique identifier" + ) + .tag(tagName) + + val getSchemaByIdDidUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - CredentialSchemaResponse | CredentialSchemaDidUrlResponse, + CredentialSchemaDidUrlResponse, Any ] = endpoint.get .in(extractFromRequest[RequestContext](RequestContext.apply)) .in( - "schema-registry" / "schemas" / path[UUID]("guid").description( + didUrlPathPrefix / path[UUID]("guid").description( "Globally unique identifier of the credential schema record" ) ) .out( - oneOf[CredentialSchemaResponse | CredentialSchemaDidUrlResponse]( - oneOfVariant( - jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`, resolvable by HTTP URL") - ), - oneOfVariant( - jsonBody[CredentialSchemaDidUrlResponse].description( - "CredentialSchema found by `guid`, resolvable by DID URL" - ) - ) + jsonBody[CredentialSchemaDidUrlResponse].description( + "CredentialSchema found by `guid`, wrapped in an envelope" ) ) .errorOut(basicFailuresAndNotFound) @@ -210,16 +257,16 @@ object SchemaRegistryEndpoints { ) .tag(tagName) - val getRawSchemaByIdEndpoint: PublicEndpoint[ + val getRawSchemaByIdHttpUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - Json, // changed to generic Json type + Json, // returns json of raw schema Any ] = endpoint.get .in(extractFromRequest[RequestContext](RequestContext.apply)) .in( - "schema-registry" / "schemas" / path[UUID]("guid") / "schema".description( + httpUrlPathPrefix / path[UUID]("guid") / "schema".description( "Globally unique identifier of the credential schema record" ) ) @@ -230,10 +277,32 @@ object SchemaRegistryEndpoints { .description("Fetch the credential schema by the unique identifier") .tag("Schema Registry") + val getRawSchemaByIdDidUrlEndpoint: PublicEndpoint[ + (RequestContext, UUID), + ErrorResponse, + CredentialSchemaInnerDidUrlResponse, // returns an envelope, where resource is a json of wrapped schema + Any + ] = + endpoint.get + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in( + didUrlPathPrefix / path[UUID]("guid") / "schema".description( + "Globally unique identifier of the credential schema record" + ) + ) + .out( + jsonBody[CredentialSchemaInnerDidUrlResponse].description("Raw JSON response of the CredentialSchema") + ) // changed to Json + .errorOut(basicFailuresAndNotFound) + .name("getRawSchemaById") + .summary("Fetch the schema from the registry by `guid`") + .description("Fetch the credential schema by the unique identifier") + .tag("Schema Registry") + private val credentialSchemaFilterInput: EndpointInput[FilterInput] = EndpointInput.derived[FilterInput] private val paginationInput: EndpointInput[PaginationInput] = EndpointInput.derived[PaginationInput] - val lookupSchemasByQueryEndpoint: Endpoint[ + val lookupSchemasByQueryHttpUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), ( RequestContext, @@ -261,4 +330,37 @@ object SchemaRegistryEndpoints { "Lookup schemas by `author`, `name`, `tags` parameters and control the pagination by `offset` and `limit` parameters " ) .tag(tagName) + + val lookupSchemasByQueryDidUrlEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + ( + RequestContext, + FilterInput, + PaginationInput, + Option[Order] + ), + ErrorResponse, + CredentialSchemaDidUrlResponsePage, + Any + ] = + endpoint.get + .securityIn(apiKeyHeader) + .securityIn(jwtAuthHeader) + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in("schema-registry" / "schemas" / "did-url".description("Lookup schemas by query")) + .in(credentialSchemaFilterInput) + .in(paginationInput) + .in(query[Option[Order]]("order")) + .out( + jsonBody[CredentialSchemaDidUrlResponsePage].description( + "Collection of CredentialSchema records each wrapped in an envelope." + ) + ) + .errorOut(basicFailuresAndForbidden) + .name("lookupSchemasByQuery") + .summary("Lookup schemas by indexed fields") + .description( + "Lookup schemas by `author`, `name`, `tags` parameters and control the pagination by `offset` and `limit` parameters " + ) + .tag(tagName) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala index 19344fbf33..975f852932 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.credentialschema +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.api.http.model.{Order, PaginationInput} import org.hyperledger.identus.api.http.RequestContext @@ -9,7 +10,6 @@ import org.hyperledger.identus.pollux.credentialschema.http.{CredentialSchemaInp import org.hyperledger.identus.pollux.credentialschema.SchemaRegistryEndpoints.* import org.hyperledger.identus.shared.models.WalletAccessContext import org.hyperledger.identus.LogUtils.* -import org.hyperledger.identus.agent.server.config.AppConfig import sttp.tapir.ztapir.* import zio.* @@ -22,8 +22,8 @@ class SchemaRegistryServerEndpoints( authorizer: Authorizer[BaseEntity] ) { - val createSchemaHttpUrlServerEndpoint: ZServerEndpoint[Any, Any] = - createSchemaHttpUrlEndpoint + object create { + val http: ZServerEndpoint[Any, Any] = createSchemaHttpUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) .serverLogic { wac => { case (ctx: RequestContext, schemaInput: CredentialSchemaInput) => @@ -33,71 +33,114 @@ class SchemaRegistryServerEndpoints( .logTrace(ctx) } } - - val createSchemaDidUrlServerEndpoint: ZServerEndpoint[Any, Any] = - createSchemaDidUrlEndpoint + val did: ZServerEndpoint[Any, Any] = createSchemaDidUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) - .serverLogic { wac => { - case (ctx: RequestContext, schemaInput: CredentialSchemaInput) => + .serverLogic { wac => + { case (ctx: RequestContext, schemaInput: CredentialSchemaInput) => credentialSchemaController - .createSchemaDidUrl(config, schemaInput)(ctx) + .createSchemaDidUrl(config.agent.httpEndpoint.serviceName, schemaInput)(ctx) .provideSomeLayer(ZLayer.succeed(wac)) .logTrace(ctx) + } } - } - val updateSchemaServerEndpoint: ZServerEndpoint[Any, Any] = - updateSchemaEndpoint + val all = List(http, did) + + } + + object update { + val http: ZServerEndpoint[Any, Any] = updateSchemaHttpUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) .serverLogic { wac => { case (ctx: RequestContext, id: UUID, schemaInput: CredentialSchemaInput) => credentialSchemaController - .updateSchema(config, id, schemaInput)(ctx) + .updateSchema(id, schemaInput)(ctx) .provideSomeLayer(ZLayer.succeed(wac)) .logTrace(ctx) } } + val did: ZServerEndpoint[Any, Any] = updateSchemaDidUrlEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (ctx: RequestContext, id: UUID, schemaInput: CredentialSchemaInput) => + credentialSchemaController + .updateSchemaDidUrl(config.agent.httpEndpoint.serviceName, id, schemaInput)(ctx) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(ctx) + } + } + val all = List(http, did) + + } - val getSchemaByIdServerEndpoint: ZServerEndpoint[Any, Any] = - getSchemaByIdEndpoint + object get { + val http: ZServerEndpoint[Any, Any] = getSchemaByIdHttpUrlEndpoint .zServerLogic { case (ctx: RequestContext, guid: UUID) => credentialSchemaController - .getSchemaByGuid(config, guid)(ctx) + .getSchemaByGuid(guid)(ctx) .logTrace(ctx) } + val did: ZServerEndpoint[Any, Any] = getSchemaByIdDidUrlEndpoint + .zServerLogic { case (ctx: RequestContext, guid: UUID) => + credentialSchemaController + .getSchemaByGuidDidUrl(config.agent.httpEndpoint.serviceName, guid)(ctx) + .logTrace(ctx) + } + val all = List(http, did) + + } - val getRawSchemaByIdServerEndpoint: ZServerEndpoint[Any, Any] = - getRawSchemaByIdEndpoint + object getRaw { + val http: ZServerEndpoint[Any, Any] = getRawSchemaByIdHttpUrlEndpoint .zServerLogic { case (ctx: RequestContext, guid: UUID) => credentialSchemaController.getSchemaJsonByGuid(guid)(ctx) } + val did: ZServerEndpoint[Any, Any] = getRawSchemaByIdDidUrlEndpoint + .zServerLogic { case (ctx: RequestContext, guid: UUID) => + credentialSchemaController.getSchemaJsonByGuidDidUrl(config.agent.httpEndpoint.serviceName, guid)(ctx) + } + val all = List(http, did) - val lookupSchemasByQueryServerEndpoint: ZServerEndpoint[Any, Any] = - lookupSchemasByQueryEndpoint - .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) - .serverLogic { wac => - { case (ctx: RequestContext, filter: FilterInput, paginationInput: PaginationInput, order: Option[Order]) => - credentialSchemaController - .lookupSchemas( - filter, - paginationInput.toPagination, - order, - config - )(ctx) - .provideSomeLayer(ZLayer.succeed(wac)) - .logTrace(ctx) + } + + object getMany { + val http: ZServerEndpoint[Any, Any] = + lookupSchemasByQueryHttpUrlEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (ctx: RequestContext, filter: FilterInput, paginationInput: PaginationInput, order: Option[Order]) => + credentialSchemaController + .lookupSchemas( + filter, + paginationInput.toPagination, + order, + )(ctx) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(ctx) + } } - } + val did: ZServerEndpoint[Any, Any] = + lookupSchemasByQueryDidUrlEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (ctx: RequestContext, filter: FilterInput, paginationInput: PaginationInput, order: Option[Order]) => + credentialSchemaController + .lookupSchemasDidUrl( + config.agent.httpEndpoint.serviceName, + filter, + paginationInput.toPagination, + order, + )(ctx) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(ctx) + } + } + val all = List(http, did) + + } val all: List[ZServerEndpoint[Any, Any]] = - List( - createSchemaHttpUrlServerEndpoint, - createSchemaDidUrlServerEndpoint, - updateSchemaServerEndpoint, // TODO: get back to it, see if changes are needed - getSchemaByIdServerEndpoint, - getRawSchemaByIdServerEndpoint, - lookupSchemasByQueryServerEndpoint - ) + create.all ++ update.all ++ get.all ++ getRaw.all ++ getMany.all } object SchemaRegistryServerEndpoints { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala index bb42ff3d46..02ebfcdaea 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -1,10 +1,11 @@ package org.hyperledger.identus.pollux.credentialschema.controller -import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} import org.hyperledger.identus.pollux.credentialschema.http.{ CredentialSchemaDidUrlResponse, + CredentialSchemaDidUrlResponsePage, + CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, @@ -22,32 +23,48 @@ trait CredentialSchemaController { rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] - def createSchemaDidUrl(config: AppConfig, in: CredentialSchemaInput)(implicit + def createSchemaDidUrl(baseUrlServiceName: String, in: CredentialSchemaInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] - def updateSchema(config: AppConfig, id: UUID, in: CredentialSchemaInput)(implicit + def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] + + def updateSchemaDidUrl(baseUrlServiceName: String, id: UUID, in: CredentialSchemaInput)(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] - def getSchemaByGuid(config: AppConfig, id: UUID)(implicit + def getSchemaByGuid(id: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] + ): IO[ErrorResponse, CredentialSchemaResponse] + + def getSchemaByGuidDidUrl(baseUrlServiceName: String, id: UUID)(implicit + rc: RequestContext + ): IO[ErrorResponse, CredentialSchemaDidUrlResponse] def getSchemaJsonByGuid(id: UUID)(implicit rc: RequestContext ): IO[ErrorResponse, Json] - def delete(guid: UUID)(implicit + def getSchemaJsonByGuidDidUrl(baseUrlServiceName: String, id: UUID)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] + ): IO[ErrorResponse, CredentialSchemaInnerDidUrlResponse] def lookupSchemas( filter: FilterInput, pagination: Pagination, order: Option[Order], - config: AppConfig )(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponsePage] + + def lookupSchemasDidUrl( + baseUrlServiceName: String, + filter: FilterInput, + pagination: Pagination, + order: Option[Order], + )(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponsePage] } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 45a219fa24..b4211bcf1e 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -14,13 +14,14 @@ import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.service.CredentialSchemaService import org.hyperledger.identus.pollux.credentialschema.http.{ CredentialSchemaDidUrlResponse, + CredentialSchemaDidUrlResponsePage, + CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, FilterInput } import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaInput.toDomain -import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaResponse.fromDomain import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* import zio.json.* @@ -40,19 +41,18 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI _ <- validatePrismDID(in.author) result <- service .create(toDomain(in)) - .map(cs => fromDomain(cs).withBaseUri(rc.request.uri)) + .map(cs => CredentialSchemaResponse.fromDomain(cs).withBaseUri(rc.request.uri)) } yield result } - def createSchemaDidUrl(config: AppConfig, in: CredentialSchemaInput)(implicit + def createSchemaDidUrl(baseUrlServiceName: String, in: CredentialSchemaInput)(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] = { val res = for { validated <- validatePrismDID(in.author) - serviceName = config.agent.httpEndpoint.serviceName result <- service.create(toDomain(in), ResourceResolutionMethod.DID) response <- ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(result, serviceName)) + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(result, baseUrlServiceName)) .mapError(e => ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) ) @@ -62,43 +62,56 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI res } - override def updateSchema(config: AppConfig, id: UUID, in: CredentialSchemaInput)(implicit + override def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = { - val res = for { + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] = { + for { _ <- validatePrismDID(in.author) - serviceName = config.agent.httpEndpoint.serviceName - cs <- service + result <- service .update(id, toDomain(in)) - result <- cs.resolutionMethod match - case ResourceResolutionMethod.DID => - ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, serviceName)) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) + .map(cs => CredentialSchemaResponse.fromDomain(cs).withBaseUri(rc.request.uri)) + } yield result + } - case ResourceResolutionMethod.HTTP => - ZIO.succeed(fromDomain(cs).withBaseUri(rc.request.uri)) + override def updateSchemaDidUrl(baseUrlServiceName: String, id: UUID, in: CredentialSchemaInput)(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] = { + val res = for { + _ <- validatePrismDID(in.author) + cs <- service + .update(id, toDomain(in), ResourceResolutionMethod.DID) + result <- ZIO + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) } yield result res } - override def getSchemaByGuid(config: AppConfig, guid: UUID)(implicit + override def getSchemaByGuid(guid: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = { - val res: IO[ErrorResponse, CredentialSchemaResponse | CredentialSchemaDidUrlResponse] = for { - cs <- service.getByGUID(guid) - serviceName = config.agent.httpEndpoint.serviceName - response <- cs.resolutionMethod match - case model.ResourceResolutionMethod.DID => - ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, serviceName)) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) - case model.ResourceResolutionMethod.HTTP => ZIO.succeed(fromDomain(cs).withSelf(rc.request.uri.toString)) + ): IO[ErrorResponse, CredentialSchemaResponse] = { + service + .getByGUID(guid) + .map( + CredentialSchemaResponse + .fromDomain(_) + .withSelf(rc.request.uri.toString) + ) + } + + override def getSchemaByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit + rc: RequestContext + ): IO[ErrorResponse, CredentialSchemaDidUrlResponse] = { + val res: IO[ErrorResponse, CredentialSchemaDidUrlResponse] = for { + cs <- service.getByGUID(guid, ResourceResolutionMethod.DID) + response <- ZIO + .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) } yield response res @@ -114,42 +127,61 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI ) } - override def delete(guid: UUID)(implicit + override def getSchemaJsonByGuidDidUrl(baseUrlServiceName: String, id: UUID)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponse] = { - service - .delete(guid) - .map( - fromDomain(_) - .withBaseUri(rc.request.uri) - ) + ): IO[ErrorResponse, CredentialSchemaInnerDidUrlResponse] = { + val res = for { + cs <- service.getByGUID(id, ResourceResolutionMethod.DID) + authorDid <- ZIO + .fromEither(PrismDID.fromString(cs.author)) + .mapError(_ => ErrorResponse.internalServerError(detail = Some("Invalid schema author DID"))) + response <- ZIO + .fromEither(CredentialSchemaInnerDidUrlResponse.fromDomain(cs.schema, authorDid, cs.id, baseUrlServiceName)) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) + } yield response + + res } override def lookupSchemas( filter: FilterInput, pagination: Pagination, - order: Option[Order], - config: AppConfig + order: Option[Order] )(implicit rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponsePage] = { + for { + filteredEntries: FilteredEntries <- service.lookup( + filter.toDomain(ResourceResolutionMethod.DID), + pagination.offset, + pagination.limit + ) + entries = filteredEntries.entries + .map(CredentialSchemaResponse.fromDomain(_).withBaseUri(rc.request.uri)) + .toList + page = CredentialSchemaResponsePage(entries) + stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) + } yield CredentialSchemaControllerLogic(rc, pagination, stats).result(page) + } + + override def lookupSchemasDidUrl( + baseUrlServiceName: String, + filter: FilterInput, + pagination: Pagination, + order: Option[Order], + )(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponsePage] = { for { filteredEntries: FilteredEntries <- service.lookup( filter.toDomain, pagination.offset, pagination.limit ) - serviceName = config.agent.httpEndpoint.serviceName entriesZio = filteredEntries.entries - .traverse(cs => - cs.resolutionMethod match { - case ResourceResolutionMethod.DID => - CredentialSchemaDidUrlResponse.fromDomain(cs, serviceName).flatMap(_.toJsonAST) - case ResourceResolutionMethod.HTTP => - fromDomain(cs).withBaseUri(rc.request.uri).toJsonAST - - } - ) + .traverse(cs => CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) entries <- ZIO .fromEither(entriesZio) @@ -157,9 +189,9 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) ) - page = CredentialSchemaResponsePage(entries) + page = CredentialSchemaDidUrlResponsePage(entries) stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) - } yield CredentialSchemaControllerLogic(rc, pagination, page, stats).result + } yield CredentialSchemaControllerLogic(rc, pagination, stats).resultDidUrl(page) } private def validatePrismDID(author: String) = diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala index 034054c5b8..f80e256ff9 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala @@ -3,27 +3,43 @@ package org.hyperledger.identus.pollux.credentialschema.controller import org.hyperledger.identus.api.http.model.{CollectionStats, Pagination} import org.hyperledger.identus.api.http.RequestContext import org.hyperledger.identus.api.util.PaginationUtils -import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaResponsePage +import org.hyperledger.identus.pollux.credentialschema.http.{ + CredentialSchemaDidUrlResponsePage, + CredentialSchemaResponsePage +} import sttp.model.Uri case class CredentialSchemaControllerLogic( ctx: RequestContext, pagination: Pagination, - page: CredentialSchemaResponsePage, stats: CollectionStats ) { - private def composeNextUri(uri: Uri): Option[Uri] = - PaginationUtils.composeNextUri(uri, page.contents, pagination, stats) + val self = ctx.request.uri.toString + val pageOf = ctx.request.uri.copy(querySegments = Seq.empty).toString + + def result(page: CredentialSchemaResponsePage): CredentialSchemaResponsePage = { + val next = PaginationUtils.composeNextUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) + val previous = PaginationUtils.composePreviousUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) - private def composePreviousUri(uri: Uri): Option[Uri] = - PaginationUtils.composePreviousUri(uri, page.contents, pagination, stats) + val pageResult = page.copy( + self = self, + pageOf = pageOf, + next = next, + previous = previous, + contents = page.contents.map(item => + item.withBaseUri( + ctx.request.uri.copy(querySegments = Seq.empty) + ) + ) + ) + + pageResult + } - def result: CredentialSchemaResponsePage = { - val self = ctx.request.uri.toString - val pageOf = ctx.request.uri.copy(querySegments = Seq.empty).toString - val next = composeNextUri(ctx.request.uri).map(_.toString) - val previous = composePreviousUri(ctx.request.uri).map(_.toString) + def resultDidUrl(page: CredentialSchemaDidUrlResponsePage): CredentialSchemaDidUrlResponsePage = { + val next = PaginationUtils.composeNextUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) + val previous = PaginationUtils.composePreviousUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) val pageResult = page.copy( self = self, diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala index 03fb551b4b..824b27c63a 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -4,7 +4,8 @@ import org.hyperledger.identus.api.http.* import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema -import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponse.annotations +import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponse.annotations as SchemaResponseAnnotations +import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaInnerDidUrlResponse.annotations as SchemaResponseInnerAnnotations import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import sttp.model.Uri @@ -12,15 +13,17 @@ import sttp.model.Uri.* import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName} import zio.json.* +import zio.json.ast.Json +import java.util.UUID import scala.collection.immutable.ListMap case class CredentialSchemaDidUrlResponse( - @description(annotations.resource.description) - @encodedExample(annotations.resource.example) + @description(SchemaResponseAnnotations.resource.description) + @encodedExample(SchemaResponseAnnotations.resource.example) resource: String, - @description(annotations.schemaUrl.description) - @encodedExample(annotations.schemaUrl.example) + @description(SchemaResponseAnnotations.schemaUrl.description) + @encodedExample(SchemaResponseAnnotations.schemaUrl.example) schemaUrl: String, ) @@ -42,7 +45,7 @@ object CredentialSchemaDidUrlResponse { Seq(), ListMap( "resourceService" -> Seq(serviceName), - "resourcePath" -> Seq(s"schema-registry/schemas/${cs.guid}"), + "resourcePath" -> Seq(s"schema-registry/schemas/did-url/${cs.guid}"), "resourceHash" -> Seq(hash) ), None @@ -53,9 +56,9 @@ object CredentialSchemaDidUrlResponse { ) } - given encoder: zio.json.JsonEncoder[CredentialSchemaDidUrlResponse] = + given encoder: JsonEncoder[CredentialSchemaDidUrlResponse] = DeriveJsonEncoder.gen[CredentialSchemaDidUrlResponse] - given decoder: zio.json.JsonDecoder[CredentialSchemaDidUrlResponse] = + given decoder: JsonDecoder[CredentialSchemaDidUrlResponse] = DeriveJsonDecoder.gen[CredentialSchemaDidUrlResponse] given schema: Schema[CredentialSchemaDidUrlResponse] = Schema.derived @@ -71,7 +74,67 @@ object CredentialSchemaDidUrlResponse { extends Annotation[String]( description = "DID url that can be used to resolve this schema", example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" ) } } + +case class CredentialSchemaInnerDidUrlResponse( + @description(SchemaResponseInnerAnnotations.resource.description) + @encodedExample(SchemaResponseInnerAnnotations.resource.example) + resource: String, + @description(SchemaResponseInnerAnnotations.schemaUrl.description) + @encodedExample(SchemaResponseInnerAnnotations.schemaUrl.example) + schemaUrl: String, +) + +object CredentialSchemaInnerDidUrlResponse { + + def fromDomain( + innerSchema: Json, + authorDid: PrismDID, + schemaGuid: UUID, + serviceName: String + ): Either[String, CredentialSchemaInnerDidUrlResponse] = { + for { + canonicalized <- JsonUtils.canonicalizeToJcs(innerSchema.toJson).left.map(_.toString) + encoded = Base64Utils.encodeURL(canonicalized.getBytes) + hash = Sha256Hash.compute(encoded.getBytes).hexEncoded + didUrl = DIDUrl( + authorDid.did, + Seq(), + ListMap( + "resourceService" -> Seq(serviceName), + "resourcePath" -> Seq(s"schema-registry/schemas/did-url$schemaGuid/schema"), + "resourceHash" -> Seq(hash) + ), + None + ).toString + } yield CredentialSchemaInnerDidUrlResponse( + resource = encoded, + schemaUrl = didUrl + ) + } + + object annotations { + object resource + extends Annotation[String]( + description = "JCS normalized and base64url encoded inner json schema (without metadata)", + example = + """ewogICJndWlkIjogIjNmODZhNzNmLTViNzgtMzljNy1hZjc3LTBjMTYxMjNmYTljMiIsCiAgImlkIjogImYyYmZiZjc4LThiZDYtNGNjNi04YjM5LWIzYTI1ZTAxZThlYSIsCiAgImxvbmdJZCI6ICJkaWQ6cHJpc206YWdlbnQvZjJiZmJmNzgtOGJkNi00Y2M2LThiMzktYjNhMjVlMDFlOGVhP3ZlcnNpb249MS4wLjAiLAogICJuYW1lIjogImRyaXZpbmctbGljZW5zZSIsCiAgInZlcnNpb24iOiAiMS4wLjAiLAogICJkZXNjcmlwdGlvbiI6ICJEcml2aW5nIExpY2Vuc2UgU2NoZW1hIiwKICAidHlwZSI6ICJodHRwczovL3czYy1jY2cuZ2l0aHViLmlvL3ZjLWpzb24tc2NoZW1hcy9zY2hlbWEvMi4wL3NjaGVtYS5qc29uIiwKICAiYXV0aG9yIjogImRpZDpwcmlzbTo0YTViNWNmMGE1MTNlODNiNTk4YmJlYTI1Y2Q2MTk2NzQ2NzQ3ZjM2MWE3M2VmNzcwNjgyNjhiYzliZDczMmZmIiwKICAiYXV0aG9yZWQiOiAiMjAyMy0wMy0xNFQxNDo0MTo0Ni43MTM5NDNaIiwKICAidGFncyI6IFsKICAgICJkcml2aW5nIiwKICAgICJsaWNlbnNlIgogIF0sCiAgInNjaGVtYSI6IHsKICAgICIkaWQiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9kcml2aW5nLWxpY2Vuc2UtMS4wLjAiLAogICAgIiRzY2hlbWEiOiAiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWEiLAogICAgImRlc2NyaXB0aW9uIjogIkRyaXZpbmcgTGljZW5zZSIsCiAgICAidHlwZSI6ICJvYmplY3QiLAogICAgInByb3BlcnRpZXMiOiB7CiAgICAgICJlbWFpbEFkZHJlc3MiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImVtYWlsIgogICAgICB9LAogICAgICAiZ2l2ZW5OYW1lIjogewogICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgfSwKICAgICAgImZhbWlseU5hbWUiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZGF0ZU9mSXNzdWFuY2UiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImRhdGUtdGltZSIKICAgICAgfSwKICAgICAgImRyaXZpbmdMaWNlbnNlSUQiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZHJpdmluZ0NsYXNzIjogewogICAgICAgICJ0eXBlIjogImludGVnZXIiCiAgICAgIH0KICAgIH0sCiAgICAicmVxdWlyZWQiOiBbCiAgICAgICJlbWFpbEFkZHJlc3MiLAogICAgICAiZmFtaWx5TmFtZSIsCiAgICAgICJkYXRlT2ZJc3N1YW5jZSIsCiAgICAgICJkcml2aW5nTGljZW5zZUlEIiwKICAgICAgImRyaXZpbmdDbGFzcyIKICAgIF0sCiAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgfQp9""" + ) + + object schemaUrl + extends Annotation[String]( + description = "DID url that can be used to resolve this schema inner schema", + example = + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46/schema" + ) + } + + given encoder: JsonEncoder[CredentialSchemaInnerDidUrlResponse] = + DeriveJsonEncoder.gen[CredentialSchemaInnerDidUrlResponse] + given decoder: JsonDecoder[CredentialSchemaInnerDidUrlResponse] = + DeriveJsonDecoder.gen[CredentialSchemaInnerDidUrlResponse] + given schema: Schema[CredentialSchemaInnerDidUrlResponse] = Schema.derived +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala new file mode 100644 index 0000000000..3b115d1157 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala @@ -0,0 +1,91 @@ +package org.hyperledger.identus.pollux.credentialschema.http + +import org.hyperledger.identus.api.http.Annotation +import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponsePage.annotations +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +case class CredentialSchemaDidUrlResponsePage( + @description(annotations.contents.description) + @encodedExample(annotations.contents.example) + contents: Seq[CredentialSchemaDidUrlResponse], + @description(annotations.kind.description) + @encodedExample(annotations.kind.example) + kind: String = "CredentialSchemaDidUrlPage", + @description(annotations.self.description) + @encodedExample(annotations.self.example) + self: String = "", + @description(annotations.pageOf.description) + @encodedExample(annotations.pageOf.example) + pageOf: String = "", + @description(annotations.next.description) + @encodedExample(annotations.next.example) + next: Option[String] = None, + @description(annotations.previous.description) + @encodedExample(annotations.previous.example) + previous: Option[String] = None +) { + def withSelf(self: String) = copy(self = self) +} + + +object CredentialSchemaDidUrlResponsePage { + given encoder: JsonEncoder[CredentialSchemaDidUrlResponsePage] = + DeriveJsonEncoder.gen[CredentialSchemaDidUrlResponsePage] + given decoder: JsonDecoder[CredentialSchemaDidUrlResponsePage] = + DeriveJsonDecoder.gen[CredentialSchemaDidUrlResponsePage] + given schema: Schema[CredentialSchemaDidUrlResponsePage] = Schema.derived + + val Example = CredentialSchemaDidUrlResponsePage( + contents = annotations.contents.example, + kind = annotations.kind.example, + self = annotations.self.example, + pageOf = annotations.pageOf.example, + next = Some(annotations.next.example), + previous = Some(annotations.previous.example) + ) + + object annotations { + + object contents + extends Annotation[Seq[CredentialSchemaDidUrlResponse]]( + description = + "A sequence of CredentialSchemaDidUrlResponse objects representing the list of credential schemas wrapped in an envelope", + example = Seq.empty + ) + + object kind + extends Annotation[String]( + description = + "A string field indicating the type of the API response. In this case, it will always be set to `CredentialSchemaPage`", + example = "CredentialSchemaPage" + ) // TODO Tech Debt ticket - the kind in a collection should be collection, not the underlying record type + + object self + extends Annotation[String]( + description = "A string field containing the URL of the current API endpoint", + example = "/cloud-agent/schema-registry/schemas/did-url?skip=10&limit=10" + ) + + object pageOf + extends Annotation[String]( + description = "A string field indicating the type of resource that the contents field contains", + example = "/cloud-agent/schema-registry/schemas/did-url" + ) + + object next + extends Annotation[String]( + description = "An optional string field containing the URL of the next page of results. " + + "If the API response does not contain any more pages, this field should be set to None.", + example = "/cloud-agent/schema-registry/schemas/did-url?skip=20&limit=10" + ) + + object previous + extends Annotation[String]( + description = "An optional string field containing the URL of the previous page of results. " + + "If the API response is the first page of results, this field should be set to None.", + example = "/cloud-agent/schema-registry/schemas/did-url?skip=0&limit=10" + ) + } +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala index a23980d024..34f7692b93 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala @@ -2,17 +2,15 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.api.http.Annotation import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaResponsePage.annotations +import sttp.tapir.generic.auto.* import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{description, encodedExample} import zio.json.* -import zio.json.ast.Json -import sttp.tapir.generic.auto.* -import sttp.tapir.json.zio.schemaForZioJsonValue case class CredentialSchemaResponsePage( @description(annotations.contents.description) @encodedExample(annotations.contents.example) - contents: Seq[Json], // TODO: update type to CredentialSchemaResponse | CredentialSchemaDidUrlResponse once we update tapir to a version that supports sttp.tapir.Schema for union types + contents: Seq[CredentialSchemaResponse], @description(annotations.kind.description) @encodedExample(annotations.kind.example) kind: String = "CredentialSchemaPage", @@ -51,7 +49,7 @@ object CredentialSchemaResponsePage { object annotations { object contents - extends Annotation[Seq[Json]]( + extends Annotation[Seq[CredentialSchemaResponse]]( description = "A sequence of CredentialSchemaResponse objects representing the list of credential schemas that the API response contains", example = Seq.empty @@ -91,3 +89,5 @@ object CredentialSchemaResponsePage { ) } } + + diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala index 48ad4f542b..4ca5f09935 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala @@ -2,6 +2,7 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.pollux.core.model +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.credentialschema.http.FilterInput.annotations import sttp.tapir.EndpointIO.annotations.{example, query} @@ -21,7 +22,7 @@ case class FilterInput( @example(annotations.tags.example.headOption) tags: Option[String] = Option.empty[String] ) { - def toDomain = CredentialSchema.Filter(author, name, version, tags) + def toDomain(resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP) = CredentialSchema.Filter(author, name, version, tags, resolutionMethod) } object FilterInput { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala index b1f91e7d06..1625c03348 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala @@ -112,11 +112,12 @@ object CredentialSchema { author: Option[String] = None, name: Option[String] = None, version: Option[String] = None, - tags: Option[String] = None + tags: Option[String] = None, + resolutionMethod: ResourceResolutionMethod ) case class FilteredEntries(entries: Seq[CredentialSchema], count: Long, totalCount: Long) - + given JsonEncoder[CredentialSchema] = DeriveJsonEncoder.gen[CredentialSchema] given JsonDecoder[CredentialSchema] = DeriveJsonDecoder.gen[CredentialSchema] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala index 2f237d6af8..5e1cbaeffc 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala @@ -14,11 +14,11 @@ trait CredentialSchemaRepository with SearchCapability[WalletTask, CredentialSchema.Filter, CredentialSchema] { def create(cs: CredentialSchema): URIO[WalletAccessContext, CredentialSchema] - def findByGuid(guid: UUID): UIO[Option[CredentialSchema]] + def findByGuid(guid: UUID, resolutionMethod: ResourceResolutionMethod): UIO[Option[CredentialSchema]] def update(cs: CredentialSchema): URIO[WalletAccessContext, CredentialSchema] - def getAllVersions(id: UUID, author: String): URIO[WalletAccessContext, List[CredentialSchema]] + def getAllVersions(id: UUID, author: String, resolutionMethod: ResourceResolutionMethod): URIO[WalletAccessContext, List[CredentialSchema]] def delete(guid: UUID): URIO[WalletAccessContext, CredentialSchema] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala index 38ae584bb2..15445d69e1 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala @@ -23,11 +23,9 @@ trait CredentialSchemaService { * @return * The instance of the credential schema or credential service error */ - def getByGUID(guid: UUID): IO[CredentialSchemaServiceError, CredentialSchema] + def getByGUID(guid: UUID, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): IO[CredentialSchemaServiceError, CredentialSchema] - def update(id: UUID, in: Input): Result[CredentialSchema] - - def delete(id: UUID): Result[CredentialSchema] + def update(id: UUID, in: Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): Result[CredentialSchema] def lookup(filter: Filter, skip: Int, limit: Int): Result[FilteredEntries] } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala index f6724079f5..5afe660dfe 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala @@ -32,9 +32,12 @@ class CredentialSchemaServiceImpl( CredentialSchemaValidationError(e) } - override def getByGUID(guid: UUID): IO[CredentialSchemaServiceError, CredentialSchema] = { + override def getByGUID( + guid: UUID, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): IO[CredentialSchemaServiceError, CredentialSchema] = { for { - resultOpt <- credentialSchemaRepository.findByGuid(guid) + resultOpt <- credentialSchemaRepository.findByGuid(guid, resolutionMethod) result <- ZIO.fromOption(resultOpt).mapError(_ => CredentialSchemaGuidNotFoundError(guid)) } yield result } @@ -42,26 +45,30 @@ class CredentialSchemaServiceImpl( def getBy( author: String, id: UUID, - version: String + version: String, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ): Result[CredentialSchema] = { - getByGUID(CredentialSchema.makeGUID(author, id, version)) + getByGUID(CredentialSchema.makeGUID(author, id, version), resolutionMethod) } override def update( guid: UUID, - in: CredentialSchema.Input + in: CredentialSchema.Input, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ): Result[CredentialSchema] = { for { - existingVersions <- credentialSchemaRepository.getAllVersions(guid, in.author) - _ <- if existingVersions.isEmpty then - ZIO.fail( - CredentialSchemaUpdateError( - guid, - in.version, - in.author, - s"No Schema exists of author: ${in.author}, with provided id: $guid" + existingVersions <- credentialSchemaRepository.getAllVersions(guid, in.author, resolutionMethod) + _ <- + if existingVersions.isEmpty then + ZIO.fail( + CredentialSchemaUpdateError( + guid, + in.version, + in.author, + s"No Schema exists of author: ${in.author}, with provided id: $guid" + ) ) - ) else ZIO.unit + else ZIO.unit resolutionMethod = existingVersions.head.resolutionMethod cs <- CredentialSchema.make(guid, in, resolutionMethod) _ <- CredentialSchema.validateCredentialSchema(cs).mapError(CredentialSchemaValidationError.apply) @@ -95,17 +102,10 @@ class CredentialSchemaServiceImpl( } yield updated } - override def delete(guid: UUID): Result[CredentialSchema] = - for { - existingOpt <- credentialSchemaRepository.findByGuid(guid) - _ <- ZIO.fromOption(existingOpt).mapError(_ => CredentialSchemaGuidNotFoundError(guid)) - result <- credentialSchemaRepository.delete(guid) - } yield result - override def lookup( filter: CredentialSchema.Filter, skip: Int, - limit: Int + limit: Int, ): Result[CredentialSchema.FilteredEntries] = { credentialSchemaRepository .search(SearchQuery(filter, skip, limit)) diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala index 8a3ac5f75c..a3a499c476 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialSchema.scala @@ -5,8 +5,9 @@ import io.getquill.context.json.PostgresJsonExtensions import io.getquill.doobie.DoobieContext import io.getquill.idiom.* import org.hyperledger.identus.pollux.core.model.schema.Schema -import org.hyperledger.identus.shared.models.WalletId import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod +import org.hyperledger.identus.shared.models.WalletId + import java.time.temporal.ChronoUnit import java.time.OffsetDateTime import java.util.UUID @@ -83,19 +84,26 @@ object CredentialSchemaSql ).returning(cs => cs) } - def findByGUID(guid: UUID) = run { - quote(query[CredentialSchema].filter(_.guid == lift(guid)).take(1)) + def findByGUID(guid: UUID, resolutionMethod: ResourceResolutionMethod) = run { + quote( + query[CredentialSchema] + .filter(_.guid == lift(guid)) + .filter(_.resolutionMethod == lift(resolutionMethod)) + .take(1) + ) } + // NOTE: this function is not used def findByID(id: UUID) = run { quote(query[CredentialSchema].filter(_.id == lift(id))) } - def getAllVersions(id: UUID, author: String) = run { + def getAllVersions(id: UUID, author: String, resolutionMethod: ResourceResolutionMethod) = run { quote( query[CredentialSchema] .filter(_.id == lift(id)) .filter(_.author == lift(author)) + .filter(_.resolutionMethod == lift(resolutionMethod)) .sortBy(_.version)(ord = Ord.asc) ) } @@ -135,10 +143,16 @@ object CredentialSchemaSql authorOpt: Option[String] = None, nameOpt: Option[String] = None, versionOpt: Option[String] = None, - tagOpt: Option[String] = None + tagOpt: Option[String] = None, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ) = run { val q = - idOpt.fold(quote(query[CredentialSchema]))(id => quote(query[CredentialSchema].filter(cs => cs.id == lift(id)))) + idOpt.fold(quote(query[CredentialSchema]))(id => + quote( + query[CredentialSchema] + .filter(cs => cs.id == lift(id)) + ) + ) q.dynamic .filterOpt(authorOpt)((cs, author) => quote(cs.author.like(author))) @@ -148,6 +162,7 @@ object CredentialSchemaSql tagOpt .fold(quote(true))(tag => quote(cs.tags.contains(lift(tag)))) ) + .filter(_.resolutionMethod == lift(resolutionMethod)) .size } @@ -158,10 +173,16 @@ object CredentialSchemaSql versionOpt: Option[String] = None, tagOpt: Option[String] = None, offset: Int = 0, - limit: Int = 1000 + limit: Int = 1000, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ) = run { val q = - idOpt.fold(quote(query[CredentialSchema]))(id => quote(query[CredentialSchema].filter(cs => cs.id == lift(id)))) + idOpt.fold(quote(query[CredentialSchema]))(id => + quote( + query[CredentialSchema] + .filter(cs => cs.id == lift(id)) + ) + ) q.dynamic .filterOpt(authorOpt)((cs, author) => quote(cs.author.like(author))) @@ -171,6 +192,7 @@ object CredentialSchemaSql tagOpt .fold(quote(true))(tag => quote(cs.tags.contains(lift(tag)))) ) + .filter(_.resolutionMethod == lift(resolutionMethod)) .sortBy(cs => cs.id) .drop(offset) .take(limit) diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala index 90e2fba15f..1b3896e310 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala @@ -28,9 +28,9 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: ) } - override def findByGuid(guid: UUID): UIO[Option[CredentialSchema]] = { + override def findByGuid(guid: UUID, resolutionMethod: ResourceResolutionMethod): UIO[Option[CredentialSchema]] = { CredentialSchemaSql - .findByGUID(guid) + .findByGUID(guid, resolutionMethod) .transact(xb) .orDie .map( @@ -50,15 +50,15 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: ) } - def getAllVersions(id: UUID, author: String): URIO[WalletAccessContext, List[CredentialSchema]] = { + def getAllVersions(id: UUID, author: String, resolutionMethod: ResourceResolutionMethod): URIO[WalletAccessContext, List[CredentialSchema]] = { CredentialSchemaSql - .getAllVersions(id, author) + .getAllVersions(id, author, resolutionMethod) .transactWallet(xa) .orDie .map(_.map(CredentialSchemaRow.toModel)) - } + // NOTE: this function is not used anywhere override def delete(guid: UUID): URIO[WalletAccessContext, CredentialSchema] = { CredentialSchemaSql .delete(guid) @@ -85,7 +85,8 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: versionOpt = query.filter.version, tagOpt = query.filter.tags, offset = query.skip, - limit = query.limit + limit = query.limit, + resolutionMethod = query.filter.resolutionMethod ) .transactWallet(xa) .orDie From 4ad4f38d0ca06a35193fd81b0bf1428d825d036a Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Fri, 30 Aug 2024 04:51:58 +0400 Subject: [PATCH 05/12] Fix some errors after refactoring Signed-off-by: Shota Jolbordi --- .../issue/controller/IssueControllerImpl.scala | 3 +-- .../SchemaRegistryEndpoints.scala | 12 ++++++------ .../SchemaRegistryServerEndpoints.scala | 5 +++-- .../CredentialSchemaControllerImpl.scala | 5 ++--- .../http/CredentialSchemaDidUrlResponse.scala | 10 ++++------ .../error/CredentialSchemaServiceError.scala | 6 ++++++ .../service/CredentialSchemaServiceImpl.scala | 18 ++++++++++-------- 7 files changed, 32 insertions(+), 27 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index 250b3f641c..ccdbc27cf4 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -112,8 +112,7 @@ class IssueControllerImpl( Seq(), ListMap( "resourceService" -> Seq(publicEndpointServiceName), - "resourcePath" -> Seq(resourcePath), - "resourceHash" -> Seq(hash) + "resourcePath" -> Seq(s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition?resourceHash=$hash"), ), None ).toString diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala index 713a773718..5f831dbb29 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -123,7 +123,7 @@ object SchemaRegistryEndpoints { .out(jsonBody[CredentialSchemaDidUrlResponse]) .description("Credential schema record") .errorOut(basicFailureAndNotFoundAndForbidden) - .name("createSchema") + .name("createSchemaDidUrl") .summary("Publish new schema to the schema registry, did url resolvable") .description( "Create the new credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + @@ -201,7 +201,7 @@ object SchemaRegistryEndpoints { .out(jsonBody[CredentialSchemaDidUrlResponse]) .description("Credential schema record wrapped in an envelope") .errorOut(basicFailureAndNotFoundAndForbidden) - .name("updateSchema") + .name("updateSchemaDidUrl") .summary("Publish the new version of the credential schema to the schema registry") .description( "Publish the new version of the credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. " + @@ -250,7 +250,7 @@ object SchemaRegistryEndpoints { ) ) .errorOut(basicFailuresAndNotFound) - .name("getSchemaById") + .name("getSchemaByIdDidUrl") .summary("Fetch the schema from the registry by `guid`") .description( "Fetch the credential schema by the unique identifier" @@ -292,9 +292,9 @@ object SchemaRegistryEndpoints { ) .out( jsonBody[CredentialSchemaInnerDidUrlResponse].description("Raw JSON response of the CredentialSchema") - ) // changed to Json + ) .errorOut(basicFailuresAndNotFound) - .name("getRawSchemaById") + .name("getRawSchemaByIdDidUrl") .summary("Fetch the schema from the registry by `guid`") .description("Fetch the credential schema by the unique identifier") .tag("Schema Registry") @@ -357,7 +357,7 @@ object SchemaRegistryEndpoints { ) ) .errorOut(basicFailuresAndForbidden) - .name("lookupSchemasByQuery") + .name("lookupSchemasByQueryDidUrl") .summary("Lookup schemas by indexed fields") .description( "Lookup schemas by `author`, `name`, `tags` parameters and control the pagination by `offset` and `limit` parameters " diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala index 975f852932..7d8c57e7ca 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryServerEndpoints.scala @@ -119,6 +119,7 @@ class SchemaRegistryServerEndpoints( .logTrace(ctx) } } + val did: ZServerEndpoint[Any, Any] = lookupSchemasByQueryDidUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) @@ -135,12 +136,12 @@ class SchemaRegistryServerEndpoints( .logTrace(ctx) } } - val all = List(http, did) + val all = List(http, did) } val all: List[ZServerEndpoint[Any, Any]] = - create.all ++ update.all ++ get.all ++ getRaw.all ++ getMany.all + create.all ++ update.all ++ getMany.all ++ getRaw.all ++ get.all } object SchemaRegistryServerEndpoints { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index b4211bcf1e..5211dfe90f 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -1,7 +1,6 @@ package org.hyperledger.identus.pollux.credentialschema.controller import cats.implicits.* -import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.{ManagedDIDState, PublicationState} import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.* @@ -154,7 +153,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaResponsePage] = { for { filteredEntries: FilteredEntries <- service.lookup( - filter.toDomain(ResourceResolutionMethod.DID), + filter.toDomain(), pagination.offset, pagination.limit ) @@ -176,7 +175,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponsePage] = { for { filteredEntries: FilteredEntries <- service.lookup( - filter.toDomain, + filter.toDomain(ResourceResolutionMethod.DID), pagination.offset, pagination.limit ) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala index 824b27c63a..3b36a62320 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -45,8 +45,7 @@ object CredentialSchemaDidUrlResponse { Seq(), ListMap( "resourceService" -> Seq(serviceName), - "resourcePath" -> Seq(s"schema-registry/schemas/did-url/${cs.guid}"), - "resourceHash" -> Seq(hash) + "resourcePath" -> Seq(s"schema-registry/schemas/did-url/${cs.guid}?resourceHash=$hash"), ), None ).toString @@ -74,7 +73,7 @@ object CredentialSchemaDidUrlResponse { extends Annotation[String]( description = "DID url that can be used to resolve this schema", example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" ) } } @@ -105,8 +104,7 @@ object CredentialSchemaInnerDidUrlResponse { Seq(), ListMap( "resourceService" -> Seq(serviceName), - "resourcePath" -> Seq(s"schema-registry/schemas/did-url$schemaGuid/schema"), - "resourceHash" -> Seq(hash) + "resourcePath" -> Seq(s"schema-registry/schemas/did-url/$schemaGuid/schema?resourceHash=$hash"), ), None ).toString @@ -121,7 +119,7 @@ object CredentialSchemaInnerDidUrlResponse { extends Annotation[String]( description = "JCS normalized and base64url encoded inner json schema (without metadata)", example = - """ewogICJndWlkIjogIjNmODZhNzNmLTViNzgtMzljNy1hZjc3LTBjMTYxMjNmYTljMiIsCiAgImlkIjogImYyYmZiZjc4LThiZDYtNGNjNi04YjM5LWIzYTI1ZTAxZThlYSIsCiAgImxvbmdJZCI6ICJkaWQ6cHJpc206YWdlbnQvZjJiZmJmNzgtOGJkNi00Y2M2LThiMzktYjNhMjVlMDFlOGVhP3ZlcnNpb249MS4wLjAiLAogICJuYW1lIjogImRyaXZpbmctbGljZW5zZSIsCiAgInZlcnNpb24iOiAiMS4wLjAiLAogICJkZXNjcmlwdGlvbiI6ICJEcml2aW5nIExpY2Vuc2UgU2NoZW1hIiwKICAidHlwZSI6ICJodHRwczovL3czYy1jY2cuZ2l0aHViLmlvL3ZjLWpzb24tc2NoZW1hcy9zY2hlbWEvMi4wL3NjaGVtYS5qc29uIiwKICAiYXV0aG9yIjogImRpZDpwcmlzbTo0YTViNWNmMGE1MTNlODNiNTk4YmJlYTI1Y2Q2MTk2NzQ2NzQ3ZjM2MWE3M2VmNzcwNjgyNjhiYzliZDczMmZmIiwKICAiYXV0aG9yZWQiOiAiMjAyMy0wMy0xNFQxNDo0MTo0Ni43MTM5NDNaIiwKICAidGFncyI6IFsKICAgICJkcml2aW5nIiwKICAgICJsaWNlbnNlIgogIF0sCiAgInNjaGVtYSI6IHsKICAgICIkaWQiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9kcml2aW5nLWxpY2Vuc2UtMS4wLjAiLAogICAgIiRzY2hlbWEiOiAiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWEiLAogICAgImRlc2NyaXB0aW9uIjogIkRyaXZpbmcgTGljZW5zZSIsCiAgICAidHlwZSI6ICJvYmplY3QiLAogICAgInByb3BlcnRpZXMiOiB7CiAgICAgICJlbWFpbEFkZHJlc3MiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImVtYWlsIgogICAgICB9LAogICAgICAiZ2l2ZW5OYW1lIjogewogICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgfSwKICAgICAgImZhbWlseU5hbWUiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZGF0ZU9mSXNzdWFuY2UiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImRhdGUtdGltZSIKICAgICAgfSwKICAgICAgImRyaXZpbmdMaWNlbnNlSUQiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZHJpdmluZ0NsYXNzIjogewogICAgICAgICJ0eXBlIjogImludGVnZXIiCiAgICAgIH0KICAgIH0sCiAgICAicmVxdWlyZWQiOiBbCiAgICAgICJlbWFpbEFkZHJlc3MiLAogICAgICAiZmFtaWx5TmFtZSIsCiAgICAgICJkYXRlT2ZJc3N1YW5jZSIsCiAgICAgICJkcml2aW5nTGljZW5zZUlEIiwKICAgICAgImRyaXZpbmdDbGFzcyIKICAgIF0sCiAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgfQp9""" + """eyIkaWQiOiJodHRwczovL2V4YW1wbGUuY29tL2RyaXZpbmctbGljZW5zZS0xLjAuMCIsIiRzY2hlbWEiOiJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYSIsImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjp0cnVlLCJkZXNjcmlwdGlvbiI6IkRyaXZpbmcgTGljZW5zZSIsInByb3BlcnRpZXMiOnsiZGF0ZU9mSXNzdWFuY2UiOnsiZm9ybWF0IjoiZGF0ZS10aW1lIiwidHlwZSI6InN0cmluZyJ9LCJkcml2aW5nQ2xhc3MiOnsidHlwZSI6ImludGVnZXIifSwiZHJpdmluZ0xpY2Vuc2VJRCI6eyJ0eXBlIjoic3RyaW5nIn0sImVtYWlsQWRkcmVzcyI6eyJmb3JtYXQiOiJlbWFpbCIsInR5cGUiOiJzdHJpbmcifSwiZmFtaWx5TmFtZSI6eyJ0eXBlIjoic3RyaW5nIn0sImdpdmVuTmFtZSI6eyJ0eXBlIjoic3RyaW5nIn19LCJyZXF1aXJlZCI6WyJlbWFpbEFkZHJlc3MiLCJmYW1pbHlOYW1lIiwiZGF0ZU9mSXNzdWFuY2UiLCJkcml2aW5nTGljZW5zZUlEIiwiZHJpdmluZ0NsYXNzIl0sInR5cGUiOiJvYmplY3QifQ==""" ) object schemaUrl diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaServiceError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaServiceError.scala index 362e2aa59d..f2d19a3d17 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaServiceError.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaServiceError.scala @@ -19,6 +19,12 @@ final case class CredentialSchemaGuidNotFoundError(guid: UUID) s"Credential Schema record cannot be found by `guid`=$guid" ) +final case class CredentialSchemaIdNotFoundError(id: UUID) + extends CredentialSchemaServiceError( + StatusCode.NotFound, + s"Credential Schema record cannot be found by `id`=$id" + ) + final case class CredentialSchemaUpdateError(id: UUID, version: String, author: String, message: String) extends CredentialSchemaServiceError( StatusCode.BadRequest, diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala index 5afe660dfe..551d46cba8 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaServiceImpl.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.pollux.core.service import org.hyperledger.identus.pollux.core.model.error.{ CredentialSchemaError, CredentialSchemaGuidNotFoundError, + CredentialSchemaIdNotFoundError, CredentialSchemaServiceError, CredentialSchemaUpdateError, CredentialSchemaValidationError @@ -52,32 +53,33 @@ class CredentialSchemaServiceImpl( } override def update( - guid: UUID, + id: UUID, in: CredentialSchema.Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ): Result[CredentialSchema] = { + for { - existingVersions <- credentialSchemaRepository.getAllVersions(guid, in.author, resolutionMethod) + existingVersions <- credentialSchemaRepository.getAllVersions(id, in.author, resolutionMethod) _ <- if existingVersions.isEmpty then ZIO.fail( CredentialSchemaUpdateError( - guid, + id, in.version, in.author, - s"No Schema exists of author: ${in.author}, with provided id: $guid" + s"No Schema exists of author: ${in.author}, with provided id: $id" ) ) else ZIO.unit resolutionMethod = existingVersions.head.resolutionMethod - cs <- CredentialSchema.make(guid, in, resolutionMethod) + cs <- CredentialSchema.make(id, in, resolutionMethod) _ <- CredentialSchema.validateCredentialSchema(cs).mapError(CredentialSchemaValidationError.apply) - _ <- ZIO.fromOption(existingVersions.headOption).mapError(_ => CredentialSchemaGuidNotFoundError(guid)) + _ <- ZIO.fromOption(existingVersions.headOption).mapError(_ => CredentialSchemaIdNotFoundError(id)) _ <- existingVersions.find(_.version > in.version) match { case Some(higherVersion) => ZIO.fail( CredentialSchemaUpdateError( - guid, + id, in.version, in.author, s"Higher version is found: $higherVersion" @@ -90,7 +92,7 @@ class CredentialSchemaServiceImpl( case Some(existingVersion) => ZIO.fail( CredentialSchemaUpdateError( - guid, + id, in.version, in.author, s"The version already exists: $existingVersion" From 2401d3aa5d0a7ead3e1abc946217fa7250d7b5a6 Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Sat, 31 Aug 2024 20:50:44 +0400 Subject: [PATCH 06/12] Credential definition did url endpoints in source files Signed-off-by: Shota Jolbordi --- .../controller/IssueControllerImpl.scala | 1 + ...redentialDefinitionRegistryEndpoints.scala | 143 ++++++++++++++++-- ...ialDefinitionRegistryServerEndpoints.scala | 102 ++++++++++--- .../CredentialDefinitionController.scala | 26 +++- .../CredentialDefinitionControllerImpl.scala | 110 ++++++++++++-- .../CredentialDefinitionControllerLogic.scala | 37 +++-- .../CredentialDefinitionDidUrlResponse.scala | 137 +++++++++++++++++ ...edentialDefinitionDidUrlResponsePage.scala | 92 +++++++++++ .../http/FilterInput.scala | 3 +- .../model/schema/CredentialDefinition.scala | 18 ++- .../CredentialDefinitionRepository.scala | 3 +- .../service/CredentialDefinitionService.scala | 7 +- .../CredentialDefinitionServiceImpl.scala | 27 ++-- .../sql/model/db/CredentialDefinition.scala | 29 +++- .../JdbcCredentialDefinitionRepository.scala | 11 +- .../JdbcCredentialSchemaRepository.scala | 3 +- 16 files changed, 650 insertions(+), 99 deletions(-) create mode 100644 cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala create mode 100644 cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index ccdbc27cf4..1d3eb05fba 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -99,6 +99,7 @@ class IssueControllerImpl( credentialDefinition <- credentialDefinitionService.getByGUID(credentialDefinitionGUID) credentialDefinitionId <- { + // TODO: this needs to be either DID or HTTP url based on credentialDefinition.resolutionMethod val publicEndpointServiceName = appConfig.agent.httpEndpoint.serviceName val resourcePath = s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition" diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala index 96592dc7d7..0aa3841e82 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala @@ -9,6 +9,9 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityL import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponse, + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, @@ -52,7 +55,7 @@ object CredentialDefinitionRegistryEndpoints { val tag = Tag(name = tagName, description = Option(tagDescription), externalDocs = Option(tagExternalDocumentation)) - val createCredentialDefinitionEndpoint: Endpoint[ + val createCredentialDefinitionHttpUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), (RequestContext, CredentialDefinitionInput), ErrorResponse, @@ -79,15 +82,52 @@ object CredentialDefinitionRegistryEndpoints { .out(jsonBody[http.CredentialDefinitionResponse]) .description("Credential definition record") .errorOut(basicFailureAndNotFoundAndForbidden) - .name("createCredentialDefinition") - .summary("Publish new definition to the definition registry") + .name("createCredentialDefinitionHttpUrl") + .summary("Publish new definition to the definition registry, resolvable by HTTP url") .description( "Create the new credential definition record with metadata and internal JSON Schema on behalf of Cloud Agent. " + "The credential definition will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." ) .tag(tagName) - val getCredentialDefinitionByIdEndpoint: PublicEndpoint[ + val createCredentialDefinitionDidUrlEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, CredentialDefinitionInput), + ErrorResponse, + CredentialDefinitionResponse, + Any + ] = + endpoint.post + .securityIn(apiKeyHeader) + .securityIn(jwtAuthHeader) + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in("credential-definition-registry" / "definitions" / "did-url") + .in( + jsonBody[CredentialDefinitionInput] + .description( + "JSON object required for the credential definition creation" + ) + ) + .out( + statusCode(StatusCode.Created) + .description( + "The new credential definition record is successfully created" + ) + ) + .out( + jsonBody[http.CredentialDefinitionResponse] + ) // We use same response as for HTTP url on DID url for definitions + .description("Credential definition record") + .errorOut(basicFailureAndNotFoundAndForbidden) + .name("createCredentialDefinitionDidUrl") + .summary("Publish new definition to the definition registry, resolvable by DID url") + .description( + "Create the new credential definition record with metadata and internal JSON Schema on behalf of Cloud Agent. " + + "The credential definition will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." + ) + .tag(tagName) + + val getCredentialDefinitionByIdHttpUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, CredentialDefinitionResponse, @@ -102,14 +142,40 @@ object CredentialDefinitionRegistryEndpoints { ) .out(jsonBody[CredentialDefinitionResponse].description("CredentialDefinition found by `guid`")) .errorOut(basicFailuresAndNotFound) - .name("getCredentialDefinitionById") + .name("getCredentialDefinitionByIdHttpUrl") .summary("Fetch the credential definition from the registry by `guid`") .description( "Fetch the credential definition by the unique identifier" ) .tag(tagName) - val getCredentialDefinitionInnerDefinitionByIdEndpoint: PublicEndpoint[ + val getCredentialDefinitionByIdDidUrlEndpoint: PublicEndpoint[ + (RequestContext, UUID), + ErrorResponse, + CredentialDefinitionDidUrlResponse, + Any + ] = + endpoint.get + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in( + "credential-definition-registry" / "definitions" / "did-url" / path[UUID]("guid").description( + "Globally unique identifier of the credential definition record" + ) + ) + .out( + jsonBody[CredentialDefinitionDidUrlResponse].description( + "CredentialDefinition found by `guid`, wrapped in an envelope" + ) + ) + .errorOut(basicFailuresAndNotFound) + .name("getCredentialDefinitionByIdDidUrl") + .summary("Fetch the credential definition from the registry by `guid`, wrapped in an envelope") + .description( + "Fetch the credential definition by the unique identifier, it should have been crated via DID url, otherwise not found error is returned." + ) + .tag(tagName) + + val getCredentialDefinitionInnerDefinitionByIdHttpUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, zio.json.ast.Json, @@ -124,16 +190,42 @@ object CredentialDefinitionRegistryEndpoints { ) .out(jsonBody[zio.json.ast.Json].description("CredentialDefinition found by `guid`")) .errorOut(basicFailuresAndNotFound) - .name("getCredentialDefinitionInnerDefinitionById") + .name("getCredentialDefinitionInnerDefinitionByIdHttpUrl") .summary("Fetch the inner definition field of the credential definition from the registry by `guid`") .description( "Fetch the inner definition fields of the credential definition by the unique identifier" ) .tag(tagName) + val getCredentialDefinitionInnerDefinitionByIdDidUrlEndpoint: PublicEndpoint[ + (RequestContext, UUID), + ErrorResponse, + CredentialDefinitionInnerDefinitionDidUrlResponse, + Any + ] = + endpoint.get + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in( + "credential-definition-registry" / "definitions" / path[UUID]("guid") / "definition".description( + "Globally unique identifier of the credential definition record" + ) + ) + .out( + jsonBody[CredentialDefinitionInnerDefinitionDidUrlResponse].description("CredentialDefinition found by `guid`") + ) + .errorOut(basicFailuresAndNotFound) + .name("getCredentialDefinitionInnerDefinitionByIdDidUrl") + .summary( + "Fetch the inner definition field of the credential definition from the registry by `guid`, wrapped in an envelope" + ) + .description( + "Fetch the inner definition fields of the credential definition by the unique identifier, it should have been crated via DID url, otherwise not found error is returned." + ) + .tag(tagName) + private val credentialDefinitionFilterInput: EndpointInput[http.FilterInput] = EndpointInput.derived[http.FilterInput] private val paginationInput: EndpointInput[PaginationInput] = EndpointInput.derived[PaginationInput] - val lookupCredentialDefinitionsByQueryEndpoint: Endpoint[ + val lookupCredentialDefinitionsByQueryHttpUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), ( RequestContext, @@ -159,10 +251,43 @@ object CredentialDefinitionRegistryEndpoints { .in(query[Option[Order]]("order")) .out(jsonBody[CredentialDefinitionResponsePage].description("Collection of CredentialDefinitions records.")) .errorOut(basicFailures) - .name("lookupCredentialDefinitionsByQuery") + .name("lookupCredentialDefinitionsByQueryHttpUrl") .summary("Lookup credential definitions by indexed fields") .description( "Lookup credential definitions by `author`, `name`, `tag` parameters and control the pagination by `offset` and `limit` parameters " ) .tag(tagName) + + val lookupCredentialDefinitionsByQueryDidUrlEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + ( + RequestContext, + FilterInput, + PaginationInput, + Option[Order] + ), + ErrorResponse, + CredentialDefinitionDidUrlResponsePage, + Any + ] = + endpoint.get + .securityIn(apiKeyHeader) + .securityIn(jwtAuthHeader) + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in( + "credential-definition-registry" / "definitions" / "did-url".description( + "Lookup credential definitions by query" + ) + ) + .in(credentialDefinitionFilterInput) + .in(paginationInput) + .in(query[Option[Order]]("order")) + .out(jsonBody[CredentialDefinitionDidUrlResponsePage].description("Collection of CredentialDefinitions records.")) + .errorOut(basicFailures) + .name("lookupCredentialDefinitionsByQueryDidUrl") + .summary("Lookup credential definitions by indexed fields") + .description( + "Lookup DID url resolvable credential definitions by `author`, `name`, `tag` parameters and control the pagination by `offset` and `limit` parameters " + ) + .tag(tagName) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryServerEndpoints.scala index 6d3c053ecb..5d4e60615a 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryServerEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryServerEndpoints.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.credentialdefinition +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.api.http.{ErrorResponse, RequestContext} import org.hyperledger.identus.api.http.model.{Order, PaginationInput} @@ -15,39 +16,57 @@ import zio.* import java.util.UUID class CredentialDefinitionRegistryServerEndpoints( + config: AppConfig, credentialDefinitionController: CredentialDefinitionController, authenticator: Authenticator[BaseEntity], authorizer: Authorizer[BaseEntity] ) { - val createCredentialDefinitionServerEndpoint: ZServerEndpoint[Any, Any] = - createCredentialDefinitionEndpoint + object create { + val http: ZServerEndpoint[Any, Any] = createCredentialDefinitionHttpUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) - .serverLogic { - case wac => { case (ctx: RequestContext, credentialDefinitionInput: CredentialDefinitionInput) => + .serverLogic { wac => + { case (ctx: RequestContext, credentialDefinitionInput: CredentialDefinitionInput) => credentialDefinitionController .createCredentialDefinition(credentialDefinitionInput)(ctx) .provideSomeLayer(ZLayer.succeed(wac)) .logTrace(ctx) } } + val did: ZServerEndpoint[Any, Any] = createCredentialDefinitionDidUrlEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (ctx: RequestContext, credentialDefinitionInput: CredentialDefinitionInput) => + credentialDefinitionController + .createCredentialDefinitionDidUrl(credentialDefinitionInput)(ctx) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(ctx) + } + } - val getCredentialDefinitionByIdServerEndpoint: ZServerEndpoint[Any, Any] = - getCredentialDefinitionByIdEndpoint.zServerLogic { case (ctx: RequestContext, guid: UUID) => - credentialDefinitionController - .getCredentialDefinitionByGuid(guid)(ctx) - .logTrace(ctx) - } + val all = List(http, did) + } - val getCredentialDefinitionInnerDefinitionByIdServerEndpoint: ZServerEndpoint[Any, Any] = - getCredentialDefinitionInnerDefinitionByIdEndpoint.zServerLogic { case (ctx: RequestContext, guid: UUID) => - credentialDefinitionController - .getCredentialDefinitionInnerDefinitionByGuid(guid)(ctx) - .logTrace(ctx) + object get { + val http: ZServerEndpoint[Any, Any] = getCredentialDefinitionByIdHttpUrlEndpoint.zServerLogic { + case (ctx: RequestContext, guid: UUID) => + credentialDefinitionController + .getCredentialDefinitionByGuid(guid)(ctx) + .logTrace(ctx) } + val did: ZServerEndpoint[Any, Any] = getCredentialDefinitionByIdDidUrlEndpoint.zServerLogic { + case (ctx: RequestContext, guid: UUID) => + credentialDefinitionController + .getCredentialDefinitionByGuidDidUrl(config.agent.httpEndpoint.serviceName, guid)(ctx) + .logTrace(ctx) + } + + val all = List(http, did) - val lookupCredentialDefinitionsByQueryServerEndpoint: ZServerEndpoint[Any, Any] = - lookupCredentialDefinitionsByQueryEndpoint + } + + object getMany { + val http: ZServerEndpoint[Any, Any] = lookupCredentialDefinitionsByQueryHttpUrlEndpoint .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) .serverLogic { case wac => { @@ -62,22 +81,57 @@ class CredentialDefinitionRegistryServerEndpoints( .logTrace(ctx) } } + val did: ZServerEndpoint[Any, Any] = lookupCredentialDefinitionsByQueryDidUrlEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { + case wac => { + case (ctx: RequestContext, filter: FilterInput, paginationInput: PaginationInput, order: Option[Order]) => + credentialDefinitionController + .lookupCredentialDefinitionsDidUrl( + config.agent.httpEndpoint.serviceName, + filter, + paginationInput.toPagination, + order + )(ctx) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(ctx) + } + } + + val all = List(http, did) + + } + + object getRaw { + val http: ZServerEndpoint[Any, Any] = getCredentialDefinitionInnerDefinitionByIdHttpUrlEndpoint.zServerLogic { + case (ctx: RequestContext, guid: UUID) => + credentialDefinitionController + .getCredentialDefinitionInnerDefinitionByGuid(guid)(ctx) + .logTrace(ctx) + } + val did: ZServerEndpoint[Any, Any] = getCredentialDefinitionInnerDefinitionByIdDidUrlEndpoint.zServerLogic { + case (ctx: RequestContext, guid: UUID) => + credentialDefinitionController + .getCredentialDefinitionInnerDefinitionByGuidDidUrl(config.agent.httpEndpoint.serviceName, guid)(ctx) + .logTrace(ctx) + } + + val all = List(http, did) + + } val all: List[ZServerEndpoint[Any, Any]] = - List( - createCredentialDefinitionServerEndpoint, - getCredentialDefinitionByIdServerEndpoint, - getCredentialDefinitionInnerDefinitionByIdServerEndpoint, - lookupCredentialDefinitionsByQueryServerEndpoint - ) + create.all ++ getMany.all ++ getRaw.all ++ get.all } object CredentialDefinitionRegistryServerEndpoints { - def all: URIO[CredentialDefinitionController & DefaultAuthenticator, List[ZServerEndpoint[Any, Any]]] = { + def all: URIO[CredentialDefinitionController & DefaultAuthenticator & AppConfig, List[ZServerEndpoint[Any, Any]]] = { for { credentialDefinitionRegistryService <- ZIO.service[CredentialDefinitionController] authenticator <- ZIO.service[DefaultAuthenticator] + config <- ZIO.service[AppConfig] credentialDefinitionRegistryEndpoints = new CredentialDefinitionRegistryServerEndpoints( + config, credentialDefinitionRegistryService, authenticator, authenticator diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala index 3f3b8ffe31..9641911409 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala @@ -3,6 +3,9 @@ package org.hyperledger.identus.pollux.credentialdefinition.controller import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponse, + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, @@ -19,17 +22,25 @@ trait CredentialDefinitionController { rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponse] - def getCredentialDefinitionByGuid(id: UUID)(implicit + def createCredentialDefinitionDidUrl(in: CredentialDefinitionInput)(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponse] + + def getCredentialDefinitionByGuid(guid: UUID)(implicit rc: RequestContext ): IO[ErrorResponse, CredentialDefinitionResponse] + def getCredentialDefinitionByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit + rc: RequestContext + ): IO[ErrorResponse, CredentialDefinitionDidUrlResponse] + def getCredentialDefinitionInnerDefinitionByGuid(id: UUID)(implicit rc: RequestContext ): IO[ErrorResponse, zio.json.ast.Json] - def delete(guid: UUID)(implicit + def getCredentialDefinitionInnerDefinitionByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponse] + ): IO[ErrorResponse, CredentialDefinitionInnerDefinitionDidUrlResponse] def lookupCredentialDefinitions( filter: FilterInput, @@ -39,4 +50,13 @@ trait CredentialDefinitionController { rc: RequestContext ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponsePage] + def lookupCredentialDefinitionsDidUrl( + baseUrlServiceName: String, + filter: FilterInput, + pagination: Pagination, + order: Option[Order] + )(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionDidUrlResponsePage] + } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala index c4039f4d0f..cff05360ac 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala @@ -1,21 +1,25 @@ package org.hyperledger.identus.pollux.credentialdefinition.controller +import cats.implicits.* import org.hyperledger.identus.agent.walletapi.model.{ManagedDIDState, PublicationState} import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{CollectionStats, Order, Pagination} import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID} import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.FilteredEntries +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.service.CredentialDefinitionService import org.hyperledger.identus.pollux.credentialdefinition import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponse, + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput } import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionInput.toDomain -import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionResponse.fromDomain import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* import zio.json.ast.Json @@ -34,7 +38,20 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m _ <- validatePrismDID(in.author) result <- service .create(toDomain(in)) - .map(cs => fromDomain(cs).withBaseUri(rc.request.uri)) + .map(cs => CredentialDefinitionResponse.fromDomain(cs).withBaseUri(rc.request.uri)) + } yield result + } + + override def createCredentialDefinitionDidUrl( + in: CredentialDefinitionInput + )(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponse] = { + for { + _ <- validatePrismDID(in.author) + result <- service + .create(toDomain(in), ResourceResolutionMethod.DID) + .map(cs => CredentialDefinitionResponse.fromDomain(cs).withBaseUri(rc.request.uri)) } yield result } @@ -44,28 +61,62 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m service .getByGUID(guid) .map( - fromDomain(_) + CredentialDefinitionResponse + .fromDomain(_) .withSelf(rc.request.uri.toString) ) } + override def getCredentialDefinitionByGuidDidUrl( + baseUrlServiceName: String, + guid: UUID + )(implicit rc: RequestContext): IO[ErrorResponse, CredentialDefinitionDidUrlResponse] = { + + val res = for { + cd <- service.getByGUID(guid, ResourceResolutionMethod.DID) + response <- ZIO + .fromEither(CredentialDefinitionDidUrlResponse.fromDomain(cd, baseUrlServiceName)) + .mapError(e => + ErrorResponse + .internalServerError(detail = Some(s"Error occurred while parsing a credential definition response: $e")) + ) + + } yield response + + res + } + override def getCredentialDefinitionInnerDefinitionByGuid(id: UUID)(implicit rc: RequestContext ): IO[ErrorResponse, Json] = { service .getByGUID(id) - .map(fromDomain(_).definition) + .map(CredentialDefinitionResponse.fromDomain(_).definition) } - override def delete(guid: UUID)(implicit + override def getCredentialDefinitionInnerDefinitionByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponse] = { - service - .delete(guid) - .map( - fromDomain(_) - .withBaseUri(rc.request.uri) - ) + ): IO[ErrorResponse, CredentialDefinitionInnerDefinitionDidUrlResponse] = { + val res = for { + cd <- service.getByGUID(guid, ResourceResolutionMethod.DID) + authorDid <- ZIO + .fromEither(PrismDID.fromString(cd.author)) + .mapError(_ => ErrorResponse.internalServerError(detail = Some("Invalid credential definition author DID"))) + response <- ZIO + .fromEither( + CredentialDefinitionInnerDefinitionDidUrlResponse + .fromDomain(cd.definition, authorDid, cd.guid, baseUrlServiceName) + ) + .mapError(e => + ErrorResponse + .internalServerError(detail = + Some(s"Error occurred while parsing inner definition of the credential definition response: $e") + ) + ) + + } yield response + + res } override def lookupCredentialDefinitions( @@ -77,16 +128,45 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionResponsePage] = { for { filteredEntries: FilteredEntries <- service.lookup( - filter.toDomain, + filter.toDomain(), pagination.offset, pagination.limit ) entries = filteredEntries.entries - .map(fromDomain(_).withBaseUri(rc.request.uri)) + .map(CredentialDefinitionResponse.fromDomain(_).withBaseUri(rc.request.uri)) .toList page = CredentialDefinitionResponsePage(entries) stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) - } yield CredentialDefinitionControllerLogic(rc, pagination, page, stats).result + } yield CredentialDefinitionControllerLogic(rc, pagination, stats).result(page) + } + + override def lookupCredentialDefinitionsDidUrl( + baseUrlServiceName: String, + filter: FilterInput, + pagination: Pagination, + order: Option[Order] + )(implicit + rc: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialDefinitionDidUrlResponsePage] = { + for { + filteredEntries: FilteredEntries <- service.lookup( + filter.toDomain(ResourceResolutionMethod.DID), + pagination.offset, + pagination.limit + ) + + entriesZio = filteredEntries.entries + .traverse(cd => CredentialDefinitionDidUrlResponse.fromDomain(cd, baseUrlServiceName)) + + entries <- ZIO + .fromEither(entriesZio) + .mapError(e => + ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) + ) + + page = CredentialDefinitionDidUrlResponsePage(entries) + stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) + } yield CredentialDefinitionControllerLogic(rc, pagination, stats).resultDidUrl(page) } private def validatePrismDID(author: String) = diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerLogic.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerLogic.scala index 9d90e3b5bd..ee446116f8 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerLogic.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerLogic.scala @@ -3,27 +3,24 @@ package org.hyperledger.identus.pollux.credentialdefinition.controller import org.hyperledger.identus.api.http.model.{CollectionStats, Pagination} import org.hyperledger.identus.api.http.RequestContext import org.hyperledger.identus.api.util.PaginationUtils -import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionResponsePage +import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionResponsePage +} import sttp.model.Uri case class CredentialDefinitionControllerLogic( ctx: RequestContext, pagination: Pagination, - page: CredentialDefinitionResponsePage, stats: CollectionStats ) { - private def composeNextUri(uri: Uri): Option[Uri] = - PaginationUtils.composeNextUri(uri, page.contents, pagination, stats) - - private def composePreviousUri(uri: Uri): Option[Uri] = - PaginationUtils.composePreviousUri(uri, page.contents, pagination, stats) + val self = ctx.request.uri.toString + val pageOf = ctx.request.uri.copy(querySegments = Seq.empty).toString - def result: CredentialDefinitionResponsePage = { - val self = ctx.request.uri.toString - val pageOf = ctx.request.uri.copy(querySegments = Seq.empty).toString - val next = composeNextUri(ctx.request.uri).map(_.toString) - val previous = composePreviousUri(ctx.request.uri).map(_.toString) + def result(page: CredentialDefinitionResponsePage): CredentialDefinitionResponsePage = { + val next = PaginationUtils.composeNextUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) + val previous = PaginationUtils.composePreviousUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) val pageResult = page.copy( self = self, @@ -39,4 +36,20 @@ case class CredentialDefinitionControllerLogic( pageResult } + + def resultDidUrl(page: CredentialDefinitionDidUrlResponsePage): CredentialDefinitionDidUrlResponsePage = { + val next = PaginationUtils.composeNextUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) + val previous = PaginationUtils.composePreviousUri(ctx.request.uri, page.contents, pagination, stats).map(_.toString) + + val pageResult = page.copy( + self = self, + pageOf = pageOf, + next = next, + previous = previous, + contents = page.contents + ) + + pageResult + } + } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala new file mode 100644 index 0000000000..2c42c23b33 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala @@ -0,0 +1,137 @@ +package org.hyperledger.identus.pollux.credentialdefinition.http + +import org.hyperledger.identus.api.http.* +import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} +import org.hyperledger.identus.pollux.core.model +import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition +import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionDidUrlResponse.annotations as credentialDefinitionResponseAnnotations +import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionInnerDefinitionDidUrlResponse.annotations as credentialDefinitionInnerResponseAnnotations +import org.hyperledger.identus.shared.crypto.Sha256Hash +import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} +import sttp.model.Uri +import sttp.model.Uri.* +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName} +import zio.json.* +import zio.json.ast.Json + +import java.util.UUID +import scala.collection.immutable.ListMap + +case class CredentialDefinitionDidUrlResponse( + @description(credentialDefinitionResponseAnnotations.resource.description) + @encodedExample(credentialDefinitionResponseAnnotations.resource.example) + resource: String, + @description(credentialDefinitionResponseAnnotations.credentialDefinitionUrl.description) + @encodedExample(credentialDefinitionResponseAnnotations.credentialDefinitionUrl.example) + credentialDefinitionUrl: String, +) + +object CredentialDefinitionDidUrlResponse { + + def fromDomain(cd: CredentialDefinition, serviceName: String): Either[String, CredentialDefinitionDidUrlResponse] = { + for { + authorDid <- PrismDID.fromString(cd.author) + canonicalized <- JsonUtils.canonicalizeToJcs(cd.toJson).left.map(_.toString) + encoded = Base64Utils.encodeURL(canonicalized.getBytes) + hash = Sha256Hash.compute(encoded.getBytes).hexEncoded + didUrl = DIDUrl( + authorDid.did, + Seq(), + ListMap( + "resourceService" -> Seq(serviceName), + "resourcePath" -> Seq(s"credential-definition-registry/definitions/did-url/${cd.guid}?resourceHash=$hash"), + ), + None + ).toString + } yield CredentialDefinitionDidUrlResponse( + resource = encoded, + credentialDefinitionUrl = didUrl + ) + } + + given encoder: zio.json.JsonEncoder[CredentialDefinitionDidUrlResponse] = + DeriveJsonEncoder.gen[CredentialDefinitionDidUrlResponse] + + given decoder: zio.json.JsonDecoder[CredentialDefinitionDidUrlResponse] = + DeriveJsonDecoder.gen[CredentialDefinitionDidUrlResponse] + + given schema: Schema[CredentialDefinitionDidUrlResponse] = Schema.derived + + object annotations { + object resource + extends Annotation[String]( + description = "JCS normalized and base64url encoded json credential definition", + example = "" // TODO Add example + ) + + object credentialDefinitionUrl + extends Annotation[String]( + description = "DID url that can be used to resolve this credential definition", + example = + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=credential-definition-registry/definitions/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" + ) + } + +} + +case class CredentialDefinitionInnerDefinitionDidUrlResponse( + @description(credentialDefinitionInnerResponseAnnotations.resource.description) + @encodedExample(credentialDefinitionInnerResponseAnnotations.resource.example) + resource: String, + @description(credentialDefinitionInnerResponseAnnotations.credentialDefinitionUrl.description) + @encodedExample(credentialDefinitionInnerResponseAnnotations.credentialDefinitionUrl.example) + credentialDefinitionUrl: String, +) + +object CredentialDefinitionInnerDefinitionDidUrlResponse { + + def fromDomain( + innerDefinition: Json, + authorDid: PrismDID, + definitionGuid: UUID, + serviceName: String + ): Either[String, CredentialDefinitionInnerDefinitionDidUrlResponse] = { + for { + canonicalized <- JsonUtils.canonicalizeToJcs(innerDefinition.toJson).left.map(_.toString) + encoded = Base64Utils.encodeURL(canonicalized.getBytes) + hash = Sha256Hash.compute(encoded.getBytes).hexEncoded + didUrl = DIDUrl( + authorDid.did, + Seq(), + ListMap( + "resourceService" -> Seq(serviceName), + "resourcePath" -> Seq( + s"credential-definition-registry/definitions/did-url/$definitionGuid/definition?resourceHash=$hash" + ), + ), + None + ).toString + } yield CredentialDefinitionInnerDefinitionDidUrlResponse( + resource = encoded, + credentialDefinitionUrl = didUrl + ) + } + + object annotations { + object resource + extends Annotation[String]( + description = + "JCS normalized and base64url encoded inner json definition of the credential definition (without metadata)", + example = "" // TODO Add example + ) + + object credentialDefinitionUrl + extends Annotation[String]( + description = "DID url that can be used to resolve this schema inner schema", + example = + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=credential-definition-registry/definitions/did-url/definition/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" + ) + } + + given encoder: JsonEncoder[CredentialDefinitionInnerDefinitionDidUrlResponse] = + DeriveJsonEncoder.gen[CredentialDefinitionInnerDefinitionDidUrlResponse] + given decoder: JsonDecoder[CredentialDefinitionInnerDefinitionDidUrlResponse] = + DeriveJsonDecoder.gen[CredentialDefinitionInnerDefinitionDidUrlResponse] + given schema: Schema[CredentialDefinitionInnerDefinitionDidUrlResponse] = Schema.derived +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala new file mode 100644 index 0000000000..4c7214261a --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala @@ -0,0 +1,92 @@ +package org.hyperledger.identus.pollux.credentialdefinition.http + +import org.hyperledger.identus.api.http.Annotation +import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionResponsePage.annotations +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +case class CredentialDefinitionDidUrlResponsePage( + @description(annotations.contents.description) + @encodedExample(annotations.contents.example) + contents: Seq[CredentialDefinitionDidUrlResponse], + @description(annotations.kind.description) + @encodedExample(annotations.kind.example) + kind: String = "CredentialDefinitionDidUrlPage", + @description(annotations.self.description) + @encodedExample(annotations.self.example) + self: String = "", + @description(annotations.pageOf.description) + @encodedExample(annotations.pageOf.example) + pageOf: String = "", + @description(annotations.next.description) + @encodedExample(annotations.next.example) + next: Option[String] = None, + @description(annotations.previous.description) + @encodedExample(annotations.previous.example) + previous: Option[String] = None +) { + def withSelf(self: String) = copy(self = self) +} + +object CredentialDefinitionDidUrlResponsePage { + given encoder: JsonEncoder[CredentialDefinitionDidUrlResponsePage] = + DeriveJsonEncoder.gen[CredentialDefinitionDidUrlResponsePage] + + given decoder: JsonDecoder[CredentialDefinitionDidUrlResponsePage] = + DeriveJsonDecoder.gen[CredentialDefinitionDidUrlResponsePage] + + given schema: Schema[CredentialDefinitionDidUrlResponsePage] = Schema.derived + + val Example = CredentialDefinitionDidUrlResponsePage( + contents = annotations.contents.example, + kind = annotations.kind.example, + self = annotations.self.example, + pageOf = annotations.pageOf.example, + next = Some(annotations.next.example), + previous = Some(annotations.previous.example) + ) + + object annotations { + + object contents + extends Annotation[Seq[CredentialDefinitionDidUrlResponse]]( + description = + "A sequence of CredentialDefinitionDidUrlResponse objects representing the list of credential definitions that the API response contains", + example = Seq.empty + ) + + object kind + extends Annotation[String]( + description = + "A string field indicating the type of the API response. In this case, it will always be set to `CredentialDefinitionDidUrlPage`", + example = "CredentialDefinitionDidUrlPage" + ) // TODO Tech Debt ticket - the kind in a collection should be collection, not the underlying record type + + object self + extends Annotation[String]( + description = "A string field containing the URL of the current API endpoint", + example = "/cloud-agent/credential-definition-registry/definitions/did-url?skip=10&limit=10" + ) + + object pageOf + extends Annotation[String]( + description = "A string field indicating the type of resource that the contents field contains", + example = "/cloud-agent/credential-definition-registry/definitions/did-url" + ) + + object next + extends Annotation[String]( + description = "An optional string field containing the URL of the next page of results. " + + "If the API response does not contain any more pages, this field should be set to None.", + example = "/cloud-agent/credential-definition-registry/definitions/did-url?skip=20&limit=10" + ) + + object previous + extends Annotation[String]( + description = "An optional string field containing the URL of the previous page of results. " + + "If the API response is the first page of results, this field should be set to None.", + example = "/cloud-agent/credential-definition-registry/definitions/did-url?skip=0&limit=10" + ) + } +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala index cd39afb2b1..cf71c08f69 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala @@ -2,6 +2,7 @@ package org.hyperledger.identus.pollux.credentialdefinition.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.pollux.core.model +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition import org.hyperledger.identus.pollux.credentialdefinition.http.FilterInput.annotations import sttp.tapir.EndpointIO.annotations.{example, query} @@ -21,7 +22,7 @@ case class FilterInput( @example(Option(annotations.tag.example)) tag: Option[String] = Option.empty[String] ) { - def toDomain = CredentialDefinition.Filter(author, name, version, tag) + def toDomain(resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP) = CredentialDefinition.Filter(author, name, version, tag, resolutionMethod) } object FilterInput { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala index d6d97a45d6..1ac73512b5 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala @@ -2,6 +2,7 @@ package org.hyperledger.identus.pollux.core.model.schema import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.* +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import zio.* import zio.json.* @@ -56,7 +57,8 @@ case class CredentialDefinition( keyCorrectnessProofJsonSchemaId: String, keyCorrectnessProof: CorrectnessProof, signatureType: String, - supportRevocation: Boolean + supportRevocation: Boolean, + resolutionMethod: ResourceResolutionMethod ) { def longId = CredentialDefinition.makeLongId(author, guid, version) } @@ -85,11 +87,12 @@ object CredentialDefinition { definitionSchemaId: String, definition: Definition, proofSchemaId: String, - proof: CorrectnessProof + proof: CorrectnessProof, + resolutionMethod: ResourceResolutionMethod ): UIO[CredentialDefinition] = { for { id <- zio.Random.nextUUID - cs <- make(id, in, definitionSchemaId, definition, proofSchemaId, proof) + cs <- make(id, in, definitionSchemaId, definition, proofSchemaId, proof, resolutionMethod) } yield cs } @@ -99,7 +102,8 @@ object CredentialDefinition { definitionSchemaId: String, definition: Definition, keyCorrectnessProofSchemaId: String, - keyCorrectnessProof: CorrectnessProof + keyCorrectnessProof: CorrectnessProof, + resolutionMethod: ResourceResolutionMethod ): UIO[CredentialDefinition] = { for { ts <- zio.Clock.currentDateTime.map( @@ -121,7 +125,8 @@ object CredentialDefinition { keyCorrectnessProofJsonSchemaId = keyCorrectnessProofSchemaId, keyCorrectnessProof = keyCorrectnessProof, signatureType = in.signatureType, - supportRevocation = in.supportRevocation + supportRevocation = in.supportRevocation, + resolutionMethod = resolutionMethod ) } @@ -143,7 +148,8 @@ object CredentialDefinition { author: Option[String] = None, name: Option[String] = None, version: Option[String] = None, - tag: Option[String] = None + tag: Option[String] = None, + resolutionMethod: ResourceResolutionMethod ) case class FilteredEntries(entries: Seq[CredentialDefinition], count: Long, totalCount: Long) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepository.scala index bcf889d2fd..49c08b7d1f 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepository.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepository.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.pollux.core.repository import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.repository.Repository.SearchCapability import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{UIO, URIO} @@ -12,7 +13,7 @@ trait CredentialDefinitionRepository with SearchCapability[WalletTask, CredentialDefinition.Filter, CredentialDefinition] { def create(cs: CredentialDefinition): URIO[WalletAccessContext, CredentialDefinition] - def findByGuid(guid: UUID): UIO[Option[CredentialDefinition]] + def findByGuid(guid: UUID, resolutionMethod: ResourceResolutionMethod): UIO[Option[CredentialDefinition]] def update(cs: CredentialDefinition): URIO[WalletAccessContext, CredentialDefinition] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala index 1aecbbe7a4..562ba20788 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.core.service +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.error.CredentialDefinitionServiceError import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.* @@ -16,16 +17,14 @@ trait CredentialDefinitionService { * @return * Created instance of the Credential Definition */ - def create(in: Input): Result[CredentialDefinition] + def create(in: Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): Result[CredentialDefinition] /** @param guid * Globally unique UUID of the credential definition * @return * The instance of the credential definition or credential service error */ - def getByGUID(guid: UUID): IO[CredentialDefinitionServiceError, CredentialDefinition] - - def delete(guid: UUID): Result[CredentialDefinition] + def getByGUID(guid: UUID, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): IO[CredentialDefinitionServiceError, CredentialDefinition] def lookup(filter: Filter, skip: Int, limit: Int): Result[FilteredEntries] } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala index 730cd4d36b..c35b26f50e 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala @@ -19,6 +19,7 @@ import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaErro import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.{Filter, FilteredEntries} import org.hyperledger.identus.pollux.core.model.secret.CredentialDefinitionSecret +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.repository.CredentialDefinitionRepository import org.hyperledger.identus.pollux.core.repository.Repository.SearchQuery import org.hyperledger.identus.pollux.core.service.serdes.{ @@ -38,7 +39,10 @@ class CredentialDefinitionServiceImpl( uriResolver: UriResolver ) extends CredentialDefinitionService { - override def create(in: CredentialDefinition.Input): Result[CredentialDefinition] = { + override def create( + in: CredentialDefinition.Input, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): Result[CredentialDefinition] = { for { content <- uriResolver.resolve(in.schemaId).orDieAsUnmanagedFailure anoncredSchema <- AnoncredSchemaSerDesV1.schemaSerDes @@ -83,7 +87,8 @@ class CredentialDefinitionServiceImpl( PublicCredentialDefinitionSerDesV1.version, publicCredentialDefinitionJson, ProofKeyCredentialDefinitionSchemaSerDesV1.version, - proofKeyCredentialDefinitionJson + proofKeyCredentialDefinitionJson, + resolutionMethod ) createdCredentialDefinition <- credentialDefinitionRepository.create(cd) _ <- genericSecretStorage @@ -99,25 +104,21 @@ class CredentialDefinitionServiceImpl( CredentialDefinitionValidationError(CredentialSchemaValidationError(e)) } - override def delete(guid: UUID): Result[CredentialDefinition] = + override def getByGUID( + guid: UUID, + resolutionMethod: ResourceResolutionMethod + ): IO[CredentialDefinitionServiceError, CredentialDefinition] = { for { - existingOpt <- credentialDefinitionRepository.findByGuid(guid) - _ <- ZIO.fromOption(existingOpt).mapError(_ => CredentialDefinitionGuidNotFoundError(guid)) - result <- credentialDefinitionRepository.delete(guid) + resultOpt <- credentialDefinitionRepository.findByGuid(guid, resolutionMethod) + result <- ZIO.fromOption(resultOpt).mapError(_ => CredentialDefinitionGuidNotFoundError(guid)) } yield result + } override def lookup(filter: CredentialDefinition.Filter, skip: Int, limit: Int): Result[FilteredEntries] = { credentialDefinitionRepository .search(SearchQuery(filter, skip, limit)) .map(sr => FilteredEntries(sr.entries, sr.count.toInt, sr.totalCount.toInt)) } - - override def getByGUID(guid: UUID): IO[CredentialDefinitionServiceError, CredentialDefinition] = { - for { - resultOpt <- credentialDefinitionRepository.findByGuid(guid) - result <- ZIO.fromOption(resultOpt).mapError(_ => CredentialDefinitionGuidNotFoundError(guid)) - } yield result - } } object CredentialDefinitionServiceImpl { diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialDefinition.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialDefinition.scala index 8f82f8e1e8..a70e4567ec 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialDefinition.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/CredentialDefinition.scala @@ -5,6 +5,7 @@ import io.getquill.context.json.PostgresJsonExtensions import io.getquill.doobie.DoobieContext import io.getquill.idiom.* import org.hyperledger.identus.pollux.core.model.schema.{CorrectnessProof, Definition} +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.shared.models.WalletId import java.time.temporal.ChronoUnit @@ -27,6 +28,7 @@ case class CredentialDefinition( keyCorrectnessProof: JsonValue[CorrectnessProof], signatureType: String, supportRevocation: Boolean, + resolutionMethod: ResourceResolutionMethod, walletId: WalletId ) { lazy val uniqueConstraintKey = author + name + version @@ -56,6 +58,7 @@ object CredentialDefinition { schemaId = m.schemaId, signatureType = m.signatureType, supportRevocation = m.supportRevocation, + resolutionMethod = m.resolutionMethod, walletId = walletId ) @@ -77,12 +80,17 @@ object CredentialDefinition { keyCorrectnessProof = db.keyCorrectnessProof.value, schemaId = db.schemaId, signatureType = db.signatureType, - supportRevocation = db.supportRevocation + supportRevocation = db.supportRevocation, + resolutionMethod = db.resolutionMethod ) } } -object CredentialDefinitionSql extends DoobieContext.Postgres(SnakeCase) with PostgresJsonExtensions { +object CredentialDefinitionSql + extends DoobieContext.Postgres(SnakeCase) + with PostgresJsonExtensions + with PostgresEnumEncoders { + def insert(credentialDefinition: CredentialDefinition) = run { quote( query[CredentialDefinition] @@ -90,8 +98,13 @@ object CredentialDefinitionSql extends DoobieContext.Postgres(SnakeCase) with Po ).returning(cs => cs) } - def findByGUID(guid: UUID) = run { - quote(query[CredentialDefinition].filter(_.guid == lift(guid)).take(1)) + def findByGUID(guid: UUID, resolutionMethod: ResourceResolutionMethod) = run { + quote( + query[CredentialDefinition] + .filter(_.guid == lift(guid)) + .filter(_.resolutionMethod == lift(resolutionMethod)) + .take(1) + ) } def findByID(id: UUID) = run { @@ -143,7 +156,8 @@ object CredentialDefinitionSql extends DoobieContext.Postgres(SnakeCase) with Po authorOpt: Option[String] = None, nameOpt: Option[String] = None, versionOpt: Option[String] = None, - tagOpt: Option[String] = None + tagOpt: Option[String] = None, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ) = run { val q = idOpt.fold(quote(query[CredentialDefinition]))(id => @@ -158,6 +172,7 @@ object CredentialDefinitionSql extends DoobieContext.Postgres(SnakeCase) with Po tagOpt .fold(quote(true))(tag => quote(cs.tags.contains(lift(tag)))) ) + .filter(_.resolutionMethod == lift(resolutionMethod)) .size } @@ -168,7 +183,8 @@ object CredentialDefinitionSql extends DoobieContext.Postgres(SnakeCase) with Po versionOpt: Option[String] = None, tagOpt: Option[String] = None, offset: Int = 0, - limit: Int = 1000 + limit: Int = 1000, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ) = run { val q = idOpt.fold(quote(query[CredentialDefinition]))(id => @@ -183,6 +199,7 @@ object CredentialDefinitionSql extends DoobieContext.Postgres(SnakeCase) with Po tagOpt .fold(quote(true))(tag => quote(cs.tags.contains(lift(tag)))) ) + .filter(_.resolutionMethod == lift(resolutionMethod)) .sortBy(cs => cs.id) .drop(offset) .take(limit) diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialDefinitionRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialDefinitionRepository.scala index 5dda0f6298..bd093d8de0 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialDefinitionRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialDefinitionRepository.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.pollux.sql.repository import doobie.* import doobie.implicits.* import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.repository.{CredentialDefinitionRepository, Repository} import org.hyperledger.identus.pollux.core.repository.Repository.* import org.hyperledger.identus.pollux.sql.model.db.{ @@ -31,9 +32,9 @@ case class JdbcCredentialDefinitionRepository(xa: Transactor[ContextAwareTask], ) } - override def findByGuid(guid: UUID): UIO[Option[CredentialDefinition]] = { + override def findByGuid(guid: UUID, resolutionMethod: ResourceResolutionMethod): UIO[Option[CredentialDefinition]] = { CredentialDefinitionSql - .findByGUID(guid) + .findByGUID(guid, resolutionMethod) .transact(xb) .orDie .map( @@ -85,7 +86,8 @@ case class JdbcCredentialDefinitionRepository(xa: Transactor[ContextAwareTask], versionOpt = query.filter.version, tagOpt = query.filter.tag, offset = query.skip, - limit = query.limit + limit = query.limit, + resolutionMethod = query.filter.resolutionMethod ) .transactWallet(xa) .orDie @@ -96,7 +98,8 @@ case class JdbcCredentialDefinitionRepository(xa: Transactor[ContextAwareTask], authorOpt = query.filter.author, nameOpt = query.filter.name, versionOpt = query.filter.version, - tagOpt = query.filter.tag + tagOpt = query.filter.tag, + resolutionMethod = query.filter.resolutionMethod ) .transactWallet(xa) .orDie diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala index 1b3896e310..330d8039f2 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala @@ -97,7 +97,8 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: authorOpt = query.filter.author, nameOpt = query.filter.name, versionOpt = query.filter.version, - tagOpt = query.filter.tags + tagOpt = query.filter.tags, + resolutionMethod = query.filter.resolutionMethod ) .transactWallet(xa) .orDie From 1e3f2f2e8af42bd991268fd81dc3faeb8bdf283e Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Sun, 1 Sep 2024 21:44:04 +0400 Subject: [PATCH 07/12] Fix test source compilation for agent Signed-off-by: Shota Jolbordi --- .../CredentialDefinitionControllerImpl.scala | 20 +++++------- .../CredentialSchemaControllerImpl.scala | 21 ++++++------- .../identus/api/util/Tapir2StaticOAS.scala | 11 ++++++- .../controller/IssueControllerImplSpec.scala | 13 +++++++- .../controller/IssueControllerTestTools.scala | 2 +- .../CredentialDefinitionBasicSpec.scala | 4 ++- .../CredentialDefinitionFailureSpec.scala | 4 ++- ...ialDefinitionLookupAndPaginationSpec.scala | 10 +++--- .../CredentialDefinitionTestTools.scala | 29 ++++++++++++----- .../schema/CredentialSchemaAnoncredSpec.scala | 9 ++++-- .../schema/CredentialSchemaBasicSpec.scala | 4 ++- .../schema/CredentialSchemaFailureSpec.scala | 4 ++- ...dentialSchemaLookupAndPaginationSpec.scala | 9 ++++-- .../schema/CredentialSchemaTestTools.scala | 31 +++++++++++++------ .../model/schema/CredentialDefinition.scala | 2 +- .../core/model/schema/CredentialSchema.scala | 2 +- .../model/schema/CredentialSchemaSpec.scala | 5 ++- ...edentialDefinitionRepositoryInMemory.scala | 13 ++++++-- 18 files changed, 127 insertions(+), 66 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala index cff05360ac..af88bff44b 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala @@ -41,6 +41,10 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m .map(cs => CredentialDefinitionResponse.fromDomain(cs).withBaseUri(rc.request.uri)) } yield result } + + private def couldNotParseCredDefResponse(e: String) = ErrorResponse + .internalServerError(detail = + Some(s"Error occurred while parsing the credential definition response: $e")) override def createCredentialDefinitionDidUrl( in: CredentialDefinitionInput @@ -76,10 +80,7 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m cd <- service.getByGUID(guid, ResourceResolutionMethod.DID) response <- ZIO .fromEither(CredentialDefinitionDidUrlResponse.fromDomain(cd, baseUrlServiceName)) - .mapError(e => - ErrorResponse - .internalServerError(detail = Some(s"Error occurred while parsing a credential definition response: $e")) - ) + .mapError(couldNotParseCredDefResponse) } yield response @@ -107,12 +108,7 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m CredentialDefinitionInnerDefinitionDidUrlResponse .fromDomain(cd.definition, authorDid, cd.guid, baseUrlServiceName) ) - .mapError(e => - ErrorResponse - .internalServerError(detail = - Some(s"Error occurred while parsing inner definition of the credential definition response: $e") - ) - ) + .mapError(couldNotParseCredDefResponse) } yield response @@ -160,9 +156,7 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m entries <- ZIO .fromEither(entriesZio) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) + .mapError(couldNotParseCredDefResponse) page = CredentialDefinitionDidUrlResponsePage(entries) stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 5211dfe90f..32ed79f42e 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -31,6 +31,11 @@ import scala.language.implicitConversions class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDIDService: ManagedDIDService) extends CredentialSchemaController { + + private def parsingCredentialSchemaError(e: String) = ErrorResponse + .internalServerError(detail = + Some(s"Error occurred while parsing the credential schema response: $e")) + override def createSchema( in: CredentialSchemaInput )(implicit @@ -52,9 +57,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI result <- service.create(toDomain(in), ResourceResolutionMethod.DID) response <- ZIO .fromEither(CredentialSchemaDidUrlResponse.fromDomain(result, baseUrlServiceName)) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) + .mapError(parsingCredentialSchemaError) } yield response @@ -81,9 +84,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI .update(id, toDomain(in), ResourceResolutionMethod.DID) result <- ZIO .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) + .mapError(parsingCredentialSchemaError) } yield result res @@ -108,9 +109,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI cs <- service.getByGUID(guid, ResourceResolutionMethod.DID) response <- ZIO .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) + .mapError(parsingCredentialSchemaError) } yield response res @@ -136,9 +135,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI .mapError(_ => ErrorResponse.internalServerError(detail = Some("Invalid schema author DID"))) response <- ZIO .fromEither(CredentialSchemaInnerDidUrlResponse.fromDomain(cs.schema, authorDid, cs.id, baseUrlServiceName)) - .mapError(e => - ErrorResponse.internalServerError(detail = Some(s"Error occurred while parsing a schema response: $e")) - ) + .mapError(parsingCredentialSchemaError) } yield response res diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala index 42f55edbc5..508e497598 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala @@ -1,5 +1,7 @@ package org.hyperledger.identus.api.util +import com.typesafe.config.ConfigFactory +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.server.http.DocModels import org.hyperledger.identus.agent.server.AgentHttpServer import org.hyperledger.identus.castor.controller.{DIDController, DIDRegistrarController} @@ -22,6 +24,7 @@ import org.hyperledger.identus.verification.controller.VcVerificationController import org.scalatestplus.mockito.MockitoSugar.* import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter import zio.{Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer} +import zio.config.typesafe.TypesafeConfigProvider import java.nio.charset.StandardCharsets import java.nio.file.{Files, Path} @@ -41,6 +44,11 @@ object Tapir2StaticOAS extends ZIOAppDefault { val path = Path.of(args.head) Using(Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { writer => writer.write(yaml) } } + val configLayer = ZLayer.fromZIO( + TypesafeConfigProvider + .fromTypesafeConfig(ConfigFactory.load()) + .load(AppConfig.config) + ) effect.provideSomeLayer( ZLayer.succeed(mock[ConnectionController]) ++ ZLayer.succeed(mock[CredentialDefinitionController]) ++ @@ -58,7 +66,8 @@ object Tapir2StaticOAS extends ZIOAppDefault { ZLayer.succeed(mock[DefaultAuthenticator]) ++ ZLayer.succeed(mock[EventController]) ++ ZLayer.succeed(mock[CredentialIssuerController]) ++ - ZLayer.succeed(mock[Oid4vciAuthenticatorFactory]) + ZLayer.succeed(mock[Oid4vciAuthenticatorFactory]) ++ + configLayer ) } diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala index aa2710a01e..a5f24d9a60 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.issue.controller +import org.hyperledger.identus.agent.walletapi.memory.GenericSecretStorageInMemory import org.hyperledger.identus.agent.walletapi.model.{BaseEntity, ManagedDIDState, PublicationState} import org.hyperledger.identus.agent.walletapi.service.{ManagedDIDService, MockManagedDIDService} import org.hyperledger.identus.api.http.ErrorResponse @@ -21,7 +22,9 @@ import org.hyperledger.identus.mercury.protocol.connection.ConnectionResponse import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID, IssueCredentialRecord} import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.{ProtocolState, Role} -import org.hyperledger.identus.pollux.core.service.MockCredentialService +import org.hyperledger.identus.pollux.core.repository.CredentialDefinitionRepositoryInMemory +import org.hyperledger.identus.pollux.core.service.{CredentialDefinitionServiceImpl, MockCredentialService} +import org.hyperledger.identus.pollux.core.service.uriResolvers.ResourceUrlResolver import sttp.client3.{basicRequest, DeserializationException, UriContext} import sttp.client3.ziojson.* import sttp.model.StatusCode @@ -172,11 +175,19 @@ object IssueControllerImplSpec extends ZIOSpecDefault with IssueControllerTestTo ) ) ) + + private val credentialDefinitionServiceLayer = + CredentialDefinitionRepositoryInMemory.layer + >+> GenericSecretStorageInMemory.layer >+> + ResourceUrlResolver.layer >>> CredentialDefinitionServiceImpl.layer + + val baseLayer = MockManagedDIDService.empty >+> MockDIDService.empty >+> MockCredentialService.empty >+> MockConnectionService.empty + >+> credentialDefinitionServiceLayer def spec = (httpErrorResponses @@ migrate( schema = "public", diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerTestTools.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerTestTools.scala index bcc8e989ab..c7d1bd7a8f 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerTestTools.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerTestTools.scala @@ -61,7 +61,7 @@ trait IssueControllerTestTools extends PostgresTestContainerSupport { lazy val testEnvironmentLayer = ZLayer.makeSome[ - ManagedDIDService & DIDService & CredentialService & ConnectionService, + ManagedDIDService & DIDService & CredentialService & CredentialDefinitionService & ConnectionService, IssueController & AppConfig & PostgreSQLContainer & AuthenticatorWithAuthZ[BaseEntity] ](IssueControllerImpl.layer, configLayer, pgContainerLayer, DefaultEntityAuthenticator.layer) diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala index 3ffdc9d43a..ae15a7b190 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionBasicSpec.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.credentialdefinition +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.{BaseEntity, Entity} import org.hyperledger.identus.agent.walletapi.storage.GenericSecretStorage import org.hyperledger.identus.api.http.ErrorResponse @@ -69,7 +70,8 @@ object CredentialDefinitionBasicSpec extends ZIOSpecDefault with CredentialDefin for { controller <- ZIO.service[CredentialDefinitionController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - } yield httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + } yield httpBackend(config, controller, authenticator) def createCredentialDefinitionResponseZIO = for { backend <- backendZIO diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionFailureSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionFailureSpec.scala index 7eac221f70..735d49012d 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionFailureSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionFailureSpec.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.credentialdefinition +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.agent.walletapi.service.MockManagedDIDService import org.hyperledger.identus.api.http.ErrorResponse @@ -28,7 +29,8 @@ object CredentialDefinitionFailureSpec extends ZIOSpecDefault with CredentialDef for { credentialDefinitionRegistryService <- ZIO.service[CredentialDefinitionController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(credentialDefinitionRegistryService, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, credentialDefinitionRegistryService, authenticator) response: CredentialDefinitionBadRequestResponse <- basicRequest .post(credentialDefinitionUriBase) .body("""{"foo":"bar"}""") diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala index f4d844dd54..21a5b3181b 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala @@ -17,6 +17,7 @@ import zio.json.EncoderOps import zio.test.* import zio.test.Assertion.* import zio.test.TestAspect.* +import org.hyperledger.identus.agent.server.config.AppConfig object CredentialDefinitionLookupAndPaginationSpec extends ZIOSpecDefault @@ -25,13 +26,14 @@ object CredentialDefinitionLookupAndPaginationSpec def fetchAllPages( uri: Uri - ): ZIO[CredentialDefinitionController & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[ + ): ZIO[CredentialDefinitionController & AuthenticatorWithAuthZ[BaseEntity] & AppConfig, Throwable, List[ CredentialDefinitionResponsePage ]] = { for { controller <- ZIO.service[CredentialDefinitionController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) response: CredentialDefinitionResponsePageType <- for { response <- basicRequest @@ -81,8 +83,8 @@ object CredentialDefinitionLookupAndPaginationSpec _ <- deleteAllCredentialDefinitions controller <- ZIO.service[CredentialDefinitionController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) - + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) inputs <- Generator.credentialDefinitionInput.runCollectN(10) _ <- inputs .map(in => diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionTestTools.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionTestTools.scala index 1f659e5431..19636104a5 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionTestTools.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionTestTools.scala @@ -1,6 +1,8 @@ package org.hyperledger.identus.pollux.credentialdefinition import com.dimafeng.testcontainers.PostgreSQLContainer +import com.typesafe.config.ConfigFactory +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.server.http.CustomServerInterceptors import org.hyperledger.identus.agent.walletapi.memory.GenericSecretStorageInMemory import org.hyperledger.identus.agent.walletapi.model.{BaseEntity, ManagedDIDState, PublicationState} @@ -32,6 +34,7 @@ import sttp.tapir.server.interceptor.CustomiseInterceptors import sttp.tapir.server.stub.TapirStubInterpreter import sttp.tapir.ztapir.RIOMonadError import zio.* +import zio.config.typesafe.TypesafeConfigProvider import zio.json.EncoderOps import zio.mock.Expectation import zio.test.{Assertion, Gen, ZIOSpecDefault} @@ -73,14 +76,21 @@ trait CredentialDefinitionTestTools extends PostgresTestContainerSupport { val authenticatorLayer: TaskLayer[AuthenticatorWithAuthZ[BaseEntity]] = DefaultEntityAuthenticator.layer + val configLayer = ZLayer.fromZIO( + TypesafeConfigProvider + .fromTypesafeConfig(ConfigFactory.load()) + .load(AppConfig.config) + ) + lazy val testEnvironmentLayer = ZLayer.makeSome[ ManagedDIDService, CredentialDefinitionController & CredentialDefinitionRepository & CredentialDefinitionService & - PostgreSQLContainer & AuthenticatorWithAuthZ[BaseEntity] & GenericSecretStorage + PostgreSQLContainer & AuthenticatorWithAuthZ[BaseEntity] & GenericSecretStorage & AppConfig ]( controllerLayer, pgContainerLayer, - authenticatorLayer + authenticatorLayer, + configLayer ) val credentialDefinitionUriBase = uri"http://test.com/credential-definition-registry/definitions" @@ -93,23 +103,25 @@ trait CredentialDefinitionTestTools extends PostgresTestContainerSupport { } def httpBackend( + config: AppConfig, controller: CredentialDefinitionController, authenticator: AuthenticatorWithAuthZ[BaseEntity] ) = { + val credentialDefinitionRegistryEndpoints = - CredentialDefinitionRegistryServerEndpoints(controller, authenticator, authenticator) + CredentialDefinitionRegistryServerEndpoints(config, controller, authenticator, authenticator) val backend = TapirStubInterpreter( bootstrapOptions(new RIOMonadError[Any]), SttpBackendStub(new RIOMonadError[Any]) ) - .whenServerEndpoint(credentialDefinitionRegistryEndpoints.createCredentialDefinitionServerEndpoint) + .whenServerEndpoint(credentialDefinitionRegistryEndpoints.create.http) .thenRunLogic() - .whenServerEndpoint(credentialDefinitionRegistryEndpoints.getCredentialDefinitionByIdServerEndpoint) + .whenServerEndpoint(credentialDefinitionRegistryEndpoints.get.http) .thenRunLogic() .whenServerEndpoint( - credentialDefinitionRegistryEndpoints.lookupCredentialDefinitionsByQueryServerEndpoint + credentialDefinitionRegistryEndpoints.getMany.http ) .thenRunLogic() .backend() @@ -185,13 +197,14 @@ trait CredentialDefinitionGen { def generateCredentialDefinitionsN( count: Int - ): ZIO[CredentialDefinitionController & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[ + ): ZIO[CredentialDefinitionController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[ CredentialDefinitionInput ]] = for { controller <- ZIO.service[CredentialDefinitionController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) inputs <- Generator.credentialDefinitionInput.runCollectN(count) _ <- inputs .map(in => diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala index 29064e587f..c0c407ff62 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.schema +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.api.http.ErrorResponse import org.hyperledger.identus.container.util.MigrationAspects.* @@ -51,7 +52,7 @@ object CredentialSchemaAnoncredSpec extends ZIOSpecDefault with CredentialSchema + wrapSpec(unsupportedSchemaSpec) + wrapSpec(wrongSchemaSpec) - private def wrapSpec(spec: Spec[CredentialSchemaController & AuthenticatorWithAuthZ[BaseEntity], Throwable]) = { + private def wrapSpec(spec: Spec[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable]) = { (spec @@ nondeterministic @@ sequential @@ timed @@ migrateEach( schema = "public", @@ -65,7 +66,8 @@ object CredentialSchemaAnoncredSpec extends ZIOSpecDefault with CredentialSchema def getSchemaZIO(uuid: UUID) = for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) response <- basicRequest .get(credentialSchemaUriBase.addPath(uuid.toString)) .response(asJsonAlways[CredentialSchemaResponse]) @@ -142,7 +144,8 @@ object CredentialSchemaAnoncredSpec extends ZIOSpecDefault with CredentialSchema for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) response <- basicRequest .post(credentialSchemaUriBase) diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaBasicSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaBasicSpec.scala index ea7cda5e27..ce72b3c1f2 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaBasicSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaBasicSpec.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.pollux.schema import com.dimafeng.testcontainers.PostgreSQLContainer +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.ErrorResponse @@ -69,7 +70,8 @@ object CredentialSchemaBasicSpec extends ZIOSpecDefault with CredentialSchemaTes for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - } yield httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + } yield httpBackend(config, controller, authenticator) def createSchemaResponseZIO = for { backend <- backendZIO diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaFailureSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaFailureSpec.scala index 4069ee5715..73882dc2d5 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaFailureSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaFailureSpec.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.pollux.schema import com.dimafeng.testcontainers.PostgreSQLContainer +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.agent.walletapi.service.MockManagedDIDService import org.hyperledger.identus.api.http.ErrorResponse @@ -27,7 +28,8 @@ object CredentialSchemaFailureSpec extends ZIOSpecDefault with CredentialSchemaT for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) response: SchemaBadRequestResponse <- basicRequest .post(credentialSchemaUriBase) .body("""{"foo":"bar"}""") diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaLookupAndPaginationSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaLookupAndPaginationSpec.scala index a348ae5254..6806cc918b 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaLookupAndPaginationSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaLookupAndPaginationSpec.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.pollux.schema import com.dimafeng.testcontainers.PostgreSQLContainer +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.container.util.MigrationAspects.migrate import org.hyperledger.identus.iam.authentication.AuthenticatorWithAuthZ @@ -28,13 +29,14 @@ object CredentialSchemaLookupAndPaginationSpec def fetchAllPages( uri: Uri - ): ZIO[CredentialSchemaController & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[ + ): ZIO[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[ CredentialSchemaResponsePage ]] = { for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) response: SchemaPageResponse <- basicRequest .get(uri) .response(asJsonAlways[CredentialSchemaResponsePage]) @@ -77,7 +79,8 @@ object CredentialSchemaLookupAndPaginationSpec _ <- deleteAllCredentialSchemas controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) inputs <- Generator.schemaInput.runCollectN(101) _ <- inputs diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala index 2c3f148e54..1c984b84d9 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala @@ -1,6 +1,8 @@ package org.hyperledger.identus.pollux.schema import com.dimafeng.testcontainers.PostgreSQLContainer +import com.typesafe.config.ConfigFactory +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.server.http.CustomServerInterceptors import org.hyperledger.identus.agent.walletapi.model.{BaseEntity, ManagedDIDState, PublicationState} import org.hyperledger.identus.agent.walletapi.service.{ManagedDIDService, MockManagedDIDService} @@ -31,6 +33,7 @@ import sttp.tapir.server.interceptor.CustomiseInterceptors import sttp.tapir.server.stub.TapirStubInterpreter import sttp.tapir.ztapir.RIOMonadError import zio.* +import zio.config.typesafe.TypesafeConfigProvider import zio.json.{DecoderOps, EncoderOps} import zio.json.ast.Json import zio.json.ast.Json.* @@ -65,13 +68,19 @@ trait CredentialSchemaTestTools extends PostgresTestContainerSupport { ) ) + val configLayer = ZLayer.fromZIO( + TypesafeConfigProvider + .fromTypesafeConfig(ConfigFactory.load()) + .load(AppConfig.config) + ) + val authenticatorLayer: TaskLayer[AuthenticatorWithAuthZ[BaseEntity]] = DefaultEntityAuthenticator.layer lazy val testEnvironmentLayer = ZLayer.makeSome[ ManagedDIDService, CredentialSchemaController & CredentialSchemaRepository & CredentialSchemaService & PostgreSQLContainer & - AuthenticatorWithAuthZ[BaseEntity] + AuthenticatorWithAuthZ[BaseEntity] & AppConfig ]( CredentialSchemaControllerImpl.layer, CredentialSchemaServiceImpl.layer, @@ -79,7 +88,8 @@ trait CredentialSchemaTestTools extends PostgresTestContainerSupport { contextAwareTransactorLayer, systemTransactorLayer, pgContainerLayer, - authenticatorLayer + authenticatorLayer, + configLayer ) val credentialSchemaUriBase = uri"http://test.com/schema-registry/schemas" @@ -91,22 +101,22 @@ trait CredentialSchemaTestTools extends PostgresTestContainerSupport { .decodeFailureHandler(CustomServerInterceptors.decodeFailureHandler) } - def httpBackend(controller: CredentialSchemaController, authenticator: AuthenticatorWithAuthZ[BaseEntity]) = { - val schemaRegistryEndpoints = SchemaRegistryServerEndpoints(controller, authenticator, authenticator) + def httpBackend(config: AppConfig, controller: CredentialSchemaController, authenticator: AuthenticatorWithAuthZ[BaseEntity]) = { + val schemaRegistryEndpoints = SchemaRegistryServerEndpoints(config, controller, authenticator, authenticator) val backend = TapirStubInterpreter( bootstrapOptions(new RIOMonadError[Any]), SttpBackendStub(new RIOMonadError[Any]) ) - .whenServerEndpoint(schemaRegistryEndpoints.createSchemaServerEndpoint) + .whenServerEndpoint(schemaRegistryEndpoints.create.http) .thenRunLogic() - .whenServerEndpoint(schemaRegistryEndpoints.getSchemaByIdServerEndpoint) + .whenServerEndpoint(schemaRegistryEndpoints.get.http) .thenRunLogic() - .whenServerEndpoint(schemaRegistryEndpoints.getRawSchemaByIdServerEndpoint) + .whenServerEndpoint(schemaRegistryEndpoints.getRaw.http) .thenRunLogic() .whenServerEndpoint( - schemaRegistryEndpoints.lookupSchemasByQueryServerEndpoint + schemaRegistryEndpoints.getMany.http ) .thenRunLogic() .backend() @@ -179,11 +189,12 @@ trait CredentialSchemaGen { def generateSchemasN( count: Int - ): ZIO[CredentialSchemaController & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[CredentialSchemaInput]] = + ): ZIO[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[CredentialSchemaInput]] = for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] - backend = httpBackend(controller, authenticator) + config <- ZIO.service[AppConfig] + backend = httpBackend(config, controller, authenticator) inputs <- Generator.schemaInput.runCollectN(count) _ <- inputs .map(in => diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala index 1ac73512b5..363b0161b6 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialDefinition.scala @@ -149,7 +149,7 @@ object CredentialDefinition { name: Option[String] = None, version: Option[String] = None, tag: Option[String] = None, - resolutionMethod: ResourceResolutionMethod + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ) case class FilteredEntries(entries: Seq[CredentialDefinition], count: Long, totalCount: Long) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala index 1625c03348..8a205293fb 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala @@ -113,7 +113,7 @@ object CredentialSchema { name: Option[String] = None, version: Option[String] = None, tags: Option[String] = None, - resolutionMethod: ResourceResolutionMethod + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP ) case class FilteredEntries(entries: Seq[CredentialSchema], count: Long, totalCount: Long) diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala index 0d10160e21..4dbe8afdf9 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.core.model.schema +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.CredentialSchemaValidationError import org.hyperledger.identus.pollux.core.model.schema.`type`.{AnoncredSchemaType, CredentialJsonSchemaType} @@ -9,7 +10,7 @@ import org.hyperledger.identus.pollux.core.model.schema.AnoncredSchemaTypeSpec.t import zio.json.* import zio.json.ast.Json import zio.json.ast.Json.* -import zio.test.{assertZIO, Assertion, Spec, TestEnvironment, ZIOSpecDefault} +import zio.test.{Assertion, Spec, TestEnvironment, ZIOSpecDefault, assertZIO} import zio.test.Assertion.* import zio.Scope @@ -29,6 +30,7 @@ object CredentialSchemaSpec extends ZIOSpecDefault { tags = Seq("tag1", "tag2"), description = "Json Schema", `type` = CredentialJsonSchemaType.VC_JSON_SCHEMA_URI, + resolutionMethod = ResourceResolutionMethod.HTTP, schema = innerJsonSchema.fromJson[Json].getOrElse(Json.Null) ) } @@ -44,6 +46,7 @@ object CredentialSchemaSpec extends ZIOSpecDefault { tags = Seq("tag1", "tag2"), description = "Anoncred Schema", `type` = AnoncredSchemaSerDesV1.version, + resolutionMethod = ResourceResolutionMethod.HTTP, schema = innerJsonSchema.fromJson[Json].getOrElse(Json.Null) ) } diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala index 1b444499c4..31a4cd19b4 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala @@ -38,15 +38,22 @@ class CredentialDefinitionRepositoryInMemory( } yield record } - override def findByGuid(guid: UUID): UIO[Option[CredentialDefinition]] = { + override def findByGuid( + guid: UUID, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): UIO[Option[CredentialDefinition]] = { for { storeRefs <- walletRefs.get - storeRefOption <- ZIO.filter(storeRefs.values)(storeRef => storeRef.get.map(_.contains(guid))).map(_.headOption) + storeRefOption <- ZIO + .filter(storeRefs.values)(storeRef => storeRef.get.map(x => x.contains(guid))) + .map(_.headOption) record <- storeRefOption match { case Some(storeRef) => storeRef.get.map(_.get(guid)) case None => ZIO.none } - } yield record + } yield record.fold(None)(x => + if x.resolutionMethod == resolutionMethod then Some(x) else None + ) } override def update(cs: CredentialDefinition): URIO[WalletAccessContext, CredentialDefinition] = { From 5c3dfbaf183428cdc60820803994600b4fdf7848 Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Mon, 2 Sep 2024 16:09:53 +0400 Subject: [PATCH 08/12] Fix tests and format Signed-off-by: Shota Jolbordi --- .../controller/IssueControllerImpl.scala | 12 ++-- ...redentialDefinitionRegistryEndpoints.scala | 6 +- .../CredentialDefinitionControllerImpl.scala | 5 +- .../http/FilterInput.scala | 5 +- .../CredentialSchemaControllerImpl.scala | 3 +- .../http/CredentialSchemaDidUrlResponse.scala | 20 +++---- .../CredentialSchemaDidUrlResponsePage.scala | 57 +++++++++---------- .../http/CredentialSchemaResponsePage.scala | 2 - .../credentialschema/http/FilterInput.scala | 5 +- .../controller/IssueControllerImplSpec.scala | 1 - ...ialDefinitionLookupAndPaginationSpec.scala | 2 +- .../schema/CredentialSchemaAnoncredSpec.scala | 4 +- .../schema/CredentialSchemaTestTools.scala | 10 +++- .../core/model/schema/CredentialSchema.scala | 2 - .../CredentialSchemaRepository.scala | 8 ++- .../service/CredentialDefinitionService.scala | 12 +++- .../service/CredentialSchemaService.scala | 18 ++++-- .../model/schema/CredentialSchemaSpec.scala | 4 +- ...edentialDefinitionRepositoryInMemory.scala | 4 +- .../identus/pollux/sql/model/db/package.scala | 3 +- .../JdbcCredentialSchemaRepository.scala | 6 +- ...edentialDefinitionSqlIntegrationSpec.scala | 11 ++-- .../CredentialSchemaSqlIntegrationSpec.scala | 17 +++--- 23 files changed, 126 insertions(+), 91 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index 1d3eb05fba..ba44e8b431 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -21,13 +21,13 @@ import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID} import org.hyperledger.identus.pollux.core.model.CredentialFormat.{AnonCreds, JWT, SDJWT} import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.Role import org.hyperledger.identus.pollux.core.service.{CredentialDefinitionService, CredentialService} +import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.models.{KeyId, WalletAccessContext} import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import zio.* import zio.json.given -import org.hyperledger.identus.shared.crypto.Sha256Hash -import scala.collection.immutable.ListMap +import scala.collection.immutable.ListMap import scala.language.implicitConversions class IssueControllerImpl( @@ -113,13 +113,17 @@ class IssueControllerImpl( Seq(), ListMap( "resourceService" -> Seq(publicEndpointServiceName), - "resourcePath" -> Seq(s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition?resourceHash=$hash"), + "resourcePath" -> Seq( + s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition?resourceHash=$hash" + ), ), None ).toString } yield didUrl - ZIO.fromEither(didUrl).mapError(_ => ErrorResponse.badRequest(detail = Some("Could not parse credential definition"))) + ZIO + .fromEither(didUrl) + .mapError(_ => ErrorResponse.badRequest(detail = Some("Could not parse credential definition"))) } record <- credentialService diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala index 0aa3841e82..79debb14f4 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala @@ -261,11 +261,11 @@ object CredentialDefinitionRegistryEndpoints { val lookupCredentialDefinitionsByQueryDidUrlEndpoint: Endpoint[ (ApiKeyCredentials, JwtCredentials), ( - RequestContext, + RequestContext, FilterInput, PaginationInput, Option[Order] - ), + ), ErrorResponse, CredentialDefinitionDidUrlResponsePage, Any @@ -289,5 +289,5 @@ object CredentialDefinitionRegistryEndpoints { .description( "Lookup DID url resolvable credential definitions by `author`, `name`, `tag` parameters and control the pagination by `offset` and `limit` parameters " ) - .tag(tagName) + .tag(tagName) } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala index af88bff44b..c57be304ce 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala @@ -41,10 +41,9 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m .map(cs => CredentialDefinitionResponse.fromDomain(cs).withBaseUri(rc.request.uri)) } yield result } - + private def couldNotParseCredDefResponse(e: String) = ErrorResponse - .internalServerError(detail = - Some(s"Error occurred while parsing the credential definition response: $e")) + .internalServerError(detail = Some(s"Error occurred while parsing the credential definition response: $e")) override def createCredentialDefinitionDidUrl( in: CredentialDefinitionInput diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala index cf71c08f69..a906691d19 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/FilterInput.scala @@ -2,8 +2,8 @@ package org.hyperledger.identus.pollux.credentialdefinition.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.pollux.core.model -import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.credentialdefinition.http.FilterInput.annotations import sttp.tapir.EndpointIO.annotations.{example, query} import sttp.tapir.Validator.* @@ -22,7 +22,8 @@ case class FilterInput( @example(Option(annotations.tag.example)) tag: Option[String] = Option.empty[String] ) { - def toDomain(resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP) = CredentialDefinition.Filter(author, name, version, tag, resolutionMethod) + def toDomain(resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP) = + CredentialDefinition.Filter(author, name, version, tag, resolutionMethod) } object FilterInput { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 32ed79f42e..3291d909df 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -33,8 +33,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI extends CredentialSchemaController { private def parsingCredentialSchemaError(e: String) = ErrorResponse - .internalServerError(detail = - Some(s"Error occurred while parsing the credential schema response: $e")) + .internalServerError(detail = Some(s"Error occurred while parsing the credential schema response: $e")) override def createSchema( in: CredentialSchemaInput diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala index 3b36a62320..c2926f3781 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -116,18 +116,18 @@ object CredentialSchemaInnerDidUrlResponse { object annotations { object resource - extends Annotation[String]( - description = "JCS normalized and base64url encoded inner json schema (without metadata)", - example = - """eyIkaWQiOiJodHRwczovL2V4YW1wbGUuY29tL2RyaXZpbmctbGljZW5zZS0xLjAuMCIsIiRzY2hlbWEiOiJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYSIsImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjp0cnVlLCJkZXNjcmlwdGlvbiI6IkRyaXZpbmcgTGljZW5zZSIsInByb3BlcnRpZXMiOnsiZGF0ZU9mSXNzdWFuY2UiOnsiZm9ybWF0IjoiZGF0ZS10aW1lIiwidHlwZSI6InN0cmluZyJ9LCJkcml2aW5nQ2xhc3MiOnsidHlwZSI6ImludGVnZXIifSwiZHJpdmluZ0xpY2Vuc2VJRCI6eyJ0eXBlIjoic3RyaW5nIn0sImVtYWlsQWRkcmVzcyI6eyJmb3JtYXQiOiJlbWFpbCIsInR5cGUiOiJzdHJpbmcifSwiZmFtaWx5TmFtZSI6eyJ0eXBlIjoic3RyaW5nIn0sImdpdmVuTmFtZSI6eyJ0eXBlIjoic3RyaW5nIn19LCJyZXF1aXJlZCI6WyJlbWFpbEFkZHJlc3MiLCJmYW1pbHlOYW1lIiwiZGF0ZU9mSXNzdWFuY2UiLCJkcml2aW5nTGljZW5zZUlEIiwiZHJpdmluZ0NsYXNzIl0sInR5cGUiOiJvYmplY3QifQ==""" - ) + extends Annotation[String]( + description = "JCS normalized and base64url encoded inner json schema (without metadata)", + example = + """eyIkaWQiOiJodHRwczovL2V4YW1wbGUuY29tL2RyaXZpbmctbGljZW5zZS0xLjAuMCIsIiRzY2hlbWEiOiJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYSIsImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjp0cnVlLCJkZXNjcmlwdGlvbiI6IkRyaXZpbmcgTGljZW5zZSIsInByb3BlcnRpZXMiOnsiZGF0ZU9mSXNzdWFuY2UiOnsiZm9ybWF0IjoiZGF0ZS10aW1lIiwidHlwZSI6InN0cmluZyJ9LCJkcml2aW5nQ2xhc3MiOnsidHlwZSI6ImludGVnZXIifSwiZHJpdmluZ0xpY2Vuc2VJRCI6eyJ0eXBlIjoic3RyaW5nIn0sImVtYWlsQWRkcmVzcyI6eyJmb3JtYXQiOiJlbWFpbCIsInR5cGUiOiJzdHJpbmcifSwiZmFtaWx5TmFtZSI6eyJ0eXBlIjoic3RyaW5nIn0sImdpdmVuTmFtZSI6eyJ0eXBlIjoic3RyaW5nIn19LCJyZXF1aXJlZCI6WyJlbWFpbEFkZHJlc3MiLCJmYW1pbHlOYW1lIiwiZGF0ZU9mSXNzdWFuY2UiLCJkcml2aW5nTGljZW5zZUlEIiwiZHJpdmluZ0NsYXNzIl0sInR5cGUiOiJvYmplY3QifQ==""" + ) object schemaUrl - extends Annotation[String]( - description = "DID url that can be used to resolve this schema inner schema", - example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46/schema" - ) + extends Annotation[String]( + description = "DID url that can be used to resolve this schema inner schema", + example = + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46/schema" + ) } given encoder: JsonEncoder[CredentialSchemaInnerDidUrlResponse] = diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala index 3b115d1157..38cf03b71c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala @@ -29,7 +29,6 @@ case class CredentialSchemaDidUrlResponsePage( def withSelf(self: String) = copy(self = self) } - object CredentialSchemaDidUrlResponsePage { given encoder: JsonEncoder[CredentialSchemaDidUrlResponsePage] = DeriveJsonEncoder.gen[CredentialSchemaDidUrlResponsePage] @@ -49,43 +48,43 @@ object CredentialSchemaDidUrlResponsePage { object annotations { object contents - extends Annotation[Seq[CredentialSchemaDidUrlResponse]]( - description = - "A sequence of CredentialSchemaDidUrlResponse objects representing the list of credential schemas wrapped in an envelope", - example = Seq.empty - ) + extends Annotation[Seq[CredentialSchemaDidUrlResponse]]( + description = + "A sequence of CredentialSchemaDidUrlResponse objects representing the list of credential schemas wrapped in an envelope", + example = Seq.empty + ) object kind - extends Annotation[String]( - description = - "A string field indicating the type of the API response. In this case, it will always be set to `CredentialSchemaPage`", - example = "CredentialSchemaPage" - ) // TODO Tech Debt ticket - the kind in a collection should be collection, not the underlying record type + extends Annotation[String]( + description = + "A string field indicating the type of the API response. In this case, it will always be set to `CredentialSchemaPage`", + example = "CredentialSchemaPage" + ) // TODO Tech Debt ticket - the kind in a collection should be collection, not the underlying record type object self - extends Annotation[String]( - description = "A string field containing the URL of the current API endpoint", - example = "/cloud-agent/schema-registry/schemas/did-url?skip=10&limit=10" - ) + extends Annotation[String]( + description = "A string field containing the URL of the current API endpoint", + example = "/cloud-agent/schema-registry/schemas/did-url?skip=10&limit=10" + ) object pageOf - extends Annotation[String]( - description = "A string field indicating the type of resource that the contents field contains", - example = "/cloud-agent/schema-registry/schemas/did-url" - ) + extends Annotation[String]( + description = "A string field indicating the type of resource that the contents field contains", + example = "/cloud-agent/schema-registry/schemas/did-url" + ) object next - extends Annotation[String]( - description = "An optional string field containing the URL of the next page of results. " + - "If the API response does not contain any more pages, this field should be set to None.", - example = "/cloud-agent/schema-registry/schemas/did-url?skip=20&limit=10" - ) + extends Annotation[String]( + description = "An optional string field containing the URL of the next page of results. " + + "If the API response does not contain any more pages, this field should be set to None.", + example = "/cloud-agent/schema-registry/schemas/did-url?skip=20&limit=10" + ) object previous - extends Annotation[String]( - description = "An optional string field containing the URL of the previous page of results. " + - "If the API response is the first page of results, this field should be set to None.", - example = "/cloud-agent/schema-registry/schemas/did-url?skip=0&limit=10" - ) + extends Annotation[String]( + description = "An optional string field containing the URL of the previous page of results. " + + "If the API response is the first page of results, this field should be set to None.", + example = "/cloud-agent/schema-registry/schemas/did-url?skip=0&limit=10" + ) } } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala index 34f7692b93..b3f8174c69 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponsePage.scala @@ -89,5 +89,3 @@ object CredentialSchemaResponsePage { ) } } - - diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala index 4ca5f09935..c0e4b83aed 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/FilterInput.scala @@ -2,8 +2,8 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.pollux.core.model -import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.credentialschema.http.FilterInput.annotations import sttp.tapir.EndpointIO.annotations.{example, query} import sttp.tapir.Validator.* @@ -22,7 +22,8 @@ case class FilterInput( @example(annotations.tags.example.headOption) tags: Option[String] = Option.empty[String] ) { - def toDomain(resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP) = CredentialSchema.Filter(author, name, version, tags, resolutionMethod) + def toDomain(resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP) = + CredentialSchema.Filter(author, name, version, tags, resolutionMethod) } object FilterInput { diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala index a5f24d9a60..6f04c8cca9 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/issue/controller/IssueControllerImplSpec.scala @@ -181,7 +181,6 @@ object IssueControllerImplSpec extends ZIOSpecDefault with IssueControllerTestTo >+> GenericSecretStorageInMemory.layer >+> ResourceUrlResolver.layer >>> CredentialDefinitionServiceImpl.layer - val baseLayer = MockManagedDIDService.empty >+> MockDIDService.empty diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala index 21a5b3181b..86dfd53a37 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionLookupAndPaginationSpec.scala @@ -1,5 +1,6 @@ package org.hyperledger.identus.pollux.credentialdefinition +import org.hyperledger.identus.agent.server.config.AppConfig import org.hyperledger.identus.agent.walletapi.model.BaseEntity import org.hyperledger.identus.container.util.MigrationAspects.migrate import org.hyperledger.identus.iam.authentication.AuthenticatorWithAuthZ @@ -17,7 +18,6 @@ import zio.json.EncoderOps import zio.test.* import zio.test.Assertion.* import zio.test.TestAspect.* -import org.hyperledger.identus.agent.server.config.AppConfig object CredentialDefinitionLookupAndPaginationSpec extends ZIOSpecDefault diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala index c0c407ff62..a9c5fff060 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaAnoncredSpec.scala @@ -52,7 +52,9 @@ object CredentialSchemaAnoncredSpec extends ZIOSpecDefault with CredentialSchema + wrapSpec(unsupportedSchemaSpec) + wrapSpec(wrongSchemaSpec) - private def wrapSpec(spec: Spec[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable]) = { + private def wrapSpec( + spec: Spec[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable] + ) = { (spec @@ nondeterministic @@ sequential @@ timed @@ migrateEach( schema = "public", diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala index 1c984b84d9..5917f13f1c 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaTestTools.scala @@ -101,7 +101,11 @@ trait CredentialSchemaTestTools extends PostgresTestContainerSupport { .decodeFailureHandler(CustomServerInterceptors.decodeFailureHandler) } - def httpBackend(config: AppConfig, controller: CredentialSchemaController, authenticator: AuthenticatorWithAuthZ[BaseEntity]) = { + def httpBackend( + config: AppConfig, + controller: CredentialSchemaController, + authenticator: AuthenticatorWithAuthZ[BaseEntity] + ) = { val schemaRegistryEndpoints = SchemaRegistryServerEndpoints(config, controller, authenticator, authenticator) val backend = @@ -189,7 +193,9 @@ trait CredentialSchemaGen { def generateSchemasN( count: Int - ): ZIO[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[CredentialSchemaInput]] = + ): ZIO[CredentialSchemaController & AppConfig & AuthenticatorWithAuthZ[BaseEntity], Throwable, List[ + CredentialSchemaInput + ]] = for { controller <- ZIO.service[CredentialSchemaController] authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala index 8a205293fb..f77f4659c0 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala @@ -19,7 +19,6 @@ import java.net.URI import java.time.{OffsetDateTime, ZoneOffset} import java.util.UUID - type Schema = zio.json.ast.Json /** @param guid @@ -118,7 +117,6 @@ object CredentialSchema { case class FilteredEntries(entries: Seq[CredentialSchema], count: Long, totalCount: Long) - given JsonEncoder[CredentialSchema] = DeriveJsonEncoder.gen[CredentialSchema] given JsonDecoder[CredentialSchema] = DeriveJsonDecoder.gen[CredentialSchema] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala index 5e1cbaeffc..dfd2938e01 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/CredentialSchemaRepository.scala @@ -1,8 +1,8 @@ package org.hyperledger.identus.pollux.core.repository -import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.* +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.repository.Repository.SearchCapability import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{UIO, URIO} @@ -18,7 +18,11 @@ trait CredentialSchemaRepository def update(cs: CredentialSchema): URIO[WalletAccessContext, CredentialSchema] - def getAllVersions(id: UUID, author: String, resolutionMethod: ResourceResolutionMethod): URIO[WalletAccessContext, List[CredentialSchema]] + def getAllVersions( + id: UUID, + author: String, + resolutionMethod: ResourceResolutionMethod + ): URIO[WalletAccessContext, List[CredentialSchema]] def delete(guid: UUID): URIO[WalletAccessContext, CredentialSchema] diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala index 562ba20788..69c158c15c 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionService.scala @@ -1,9 +1,9 @@ package org.hyperledger.identus.pollux.core.service -import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.error.CredentialDefinitionServiceError import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.* +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{IO, ZIO} @@ -17,14 +17,20 @@ trait CredentialDefinitionService { * @return * Created instance of the Credential Definition */ - def create(in: Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): Result[CredentialDefinition] + def create( + in: Input, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): Result[CredentialDefinition] /** @param guid * Globally unique UUID of the credential definition * @return * The instance of the credential definition or credential service error */ - def getByGUID(guid: UUID, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): IO[CredentialDefinitionServiceError, CredentialDefinition] + def getByGUID( + guid: UUID, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): IO[CredentialDefinitionServiceError, CredentialDefinition] def lookup(filter: Filter, skip: Int, limit: Int): Result[FilteredEntries] } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala index 15445d69e1..5d371f78e5 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialSchemaService.scala @@ -1,9 +1,9 @@ package org.hyperledger.identus.pollux.core.service -import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaServiceError import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.* +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{IO, ZIO} @@ -16,16 +16,26 @@ trait CredentialSchemaService { * @return * Created instance of the Credential Schema */ - def create(in: Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): Result[CredentialSchema] + def create( + in: Input, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): Result[CredentialSchema] /** @param guid * Globally unique UUID of the credential schema * @return * The instance of the credential schema or credential service error */ - def getByGUID(guid: UUID, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): IO[CredentialSchemaServiceError, CredentialSchema] + def getByGUID( + guid: UUID, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): IO[CredentialSchemaServiceError, CredentialSchema] - def update(id: UUID, in: Input, resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP): Result[CredentialSchema] + def update( + id: UUID, + in: Input, + resolutionMethod: ResourceResolutionMethod = ResourceResolutionMethod.HTTP + ): Result[CredentialSchema] def lookup(filter: Filter, skip: Int, limit: Int): Result[FilteredEntries] } diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala index 4dbe8afdf9..5a3e2abb92 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala @@ -1,16 +1,16 @@ package org.hyperledger.identus.pollux.core.model.schema -import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.CredentialSchemaValidationError import org.hyperledger.identus.pollux.core.model.schema.`type`.{AnoncredSchemaType, CredentialJsonSchemaType} import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1 import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError.JsonValidationErrors import org.hyperledger.identus.pollux.core.model.schema.AnoncredSchemaTypeSpec.test +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import zio.json.* import zio.json.ast.Json import zio.json.ast.Json.* -import zio.test.{Assertion, Spec, TestEnvironment, ZIOSpecDefault, assertZIO} +import zio.test.{assertZIO, Assertion, Spec, TestEnvironment, ZIOSpecDefault} import zio.test.Assertion.* import zio.Scope diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala index 31a4cd19b4..8318af5233 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/CredentialDefinitionRepositoryInMemory.scala @@ -51,9 +51,7 @@ class CredentialDefinitionRepositoryInMemory( case Some(storeRef) => storeRef.get.map(_.get(guid)) case None => ZIO.none } - } yield record.fold(None)(x => - if x.resolutionMethod == resolutionMethod then Some(x) else None - ) + } yield record.fold(None)(x => if x.resolutionMethod == resolutionMethod then Some(x) else None) } override def update(cs: CredentialDefinition): URIO[WalletAccessContext, CredentialDefinition] = { diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala index 1ea1ad3abf..81251a5574 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/model/db/package.scala @@ -7,9 +7,10 @@ import io.getquill.{MappedEncoding, PostgresJdbcContext, SnakeCase} import io.getquill.doobie.DoobieContext import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.shared.models.WalletId -import java.util.UUID import org.postgresql.util.PGobject +import java.util.UUID + package object db { given MappedEncoding[WalletId, UUID] = MappedEncoding(_.toUUID) diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala index 330d8039f2..2c51771a68 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialSchemaRepository.scala @@ -50,7 +50,11 @@ case class JdbcCredentialSchemaRepository(xa: Transactor[ContextAwareTask], xb: ) } - def getAllVersions(id: UUID, author: String, resolutionMethod: ResourceResolutionMethod): URIO[WalletAccessContext, List[CredentialSchema]] = { + def getAllVersions( + id: UUID, + author: String, + resolutionMethod: ResourceResolutionMethod + ): URIO[WalletAccessContext, List[CredentialSchema]] = { CredentialSchemaSql .getAllVersions(id, author, resolutionMethod) .transactWallet(xa) diff --git a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala index 87f290aee7..5bbdaa8bf4 100644 --- a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala +++ b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala @@ -4,6 +4,7 @@ import com.dimafeng.testcontainers.PostgreSQLContainer import doobie.* import doobie.util.transactor.Transactor import io.getquill.* +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.sql.model.db.{CredentialDefinition, CredentialDefinitionSql} import org.hyperledger.identus.shared.db.ContextAwareTask import org.hyperledger.identus.shared.db.Implicits.* @@ -93,6 +94,7 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr signatureType <- credentialDefinitionSignatureType supportRevocation <- credentialDefinitionSupportRevocation walletId <- Gen.fromZIO(ZIO.serviceWith[WalletAccessContext](_.walletId)) + resolutionMethod <- Gen.fromIterable(ResourceResolutionMethod.values) } yield CredentialDefinition( guid = id, id = id, @@ -109,6 +111,7 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr schemaId = schemaId, signatureType = signatureType, supportRevocation = supportRevocation, + resolutionMethod = resolutionMethod, walletId = walletId ).withTruncatedTimestamp() @@ -136,7 +139,7 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr expected <- Generators.credentialDefinition.runCollectN(1).map(_.head) _ <- CredentialDefinitionSql.insert(expected).transactWallet(tx) actual <- CredentialDefinitionSql - .findByGUID(expected.guid) + .findByGUID(expected.guid, expected.resolutionMethod) .transactWallet(tx) .map(_.headOption) @@ -147,7 +150,7 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr .update(updatedExpected) .transactWallet(tx) updatedActual2 <- CredentialDefinitionSql - .findByGUID(expected.id) + .findByGUID(expected.id, expected.resolutionMethod) .transactWallet(tx) .map(_.headOption) @@ -157,7 +160,7 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr deleted <- CredentialDefinitionSql.delete(expected.guid).transactWallet(tx) notFound <- CredentialDefinitionSql - .findByGUID(expected.guid) + .findByGUID(expected.guid, expected.resolutionMethod) .transactWallet(tx) .map(_.headOption) @@ -196,7 +199,7 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr firstActual = generatedCredentialDefinitions.head firstExpected <- CredentialDefinitionSql - .findByGUID(firstActual.guid) + .findByGUID(firstActual.guid, firstActual.resolutionMethod) .transactWallet(tx) .map(_.headOption) diff --git a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala index e26b6d5193..c18f446222 100644 --- a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala +++ b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala @@ -4,6 +4,7 @@ import com.dimafeng.testcontainers.PostgreSQLContainer import doobie.* import doobie.util.transactor.Transactor import io.getquill.* +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.sql.model.db.{CredentialSchema, CredentialSchemaSql} import org.hyperledger.identus.shared.db.ContextAwareTask import org.hyperledger.identus.shared.db.Implicits.* @@ -75,6 +76,7 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo authored = OffsetDateTime.now(ZoneOffset.UTC) id = UUID.randomUUID() walletId <- Gen.fromZIO(ZIO.serviceWith[WalletAccessContext](_.walletId)) + resolutionMethod <- Gen.fromIterable(ResourceResolutionMethod.values) } yield CredentialSchema( guid = id, id = id, @@ -86,7 +88,8 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo author = author, authored = authored, tags = tags, - walletId = walletId + walletId = walletId, + resolutionMethod = resolutionMethod ).withTruncatedTimestamp() private val unique = mutable.Set.empty[String] @@ -123,12 +126,12 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo record <- Generators.schema.runCollectN(1).map(_.head).provide(wallet1) _ <- CredentialSchemaSql.insert(record).transactWallet(tx).provide(wallet1) ownRecord <- CredentialSchemaSql - .findByGUID(record.guid) + .findByGUID(record.guid, record.resolutionMethod) .transactWallet(tx) .map(_.headOption) .provide(wallet1) crossRecord <- CredentialSchemaSql - .findByGUID(record.guid) + .findByGUID(record.guid, record.resolutionMethod) .transactWallet(tx) .map(_.headOption) .provide(wallet2) @@ -173,7 +176,7 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo expected <- Generators.schema.runCollectN(1).map(_.head) _ <- CredentialSchemaSql.insert(expected).transactWallet(tx) actual <- CredentialSchemaSql - .findByGUID(expected.guid) + .findByGUID(expected.guid, expected.resolutionMethod) .transactWallet(tx) .map(_.headOption) @@ -184,7 +187,7 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo .update(updatedExpected) .transactWallet(tx) updatedActual2 <- CredentialSchemaSql - .findByGUID(expected.id) + .findByGUID(expected.id, expected.resolutionMethod) .transactWallet(tx) .map(_.headOption) @@ -194,7 +197,7 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo deleted <- CredentialSchemaSql.delete(expected.guid).transactWallet(tx) notFound <- CredentialSchemaSql - .findByGUID(expected.guid) + .findByGUID(expected.guid, expected.resolutionMethod) .transactWallet(tx) .map(_.headOption) @@ -231,7 +234,7 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo firstActual = generatedSchemas.head firstExpected <- CredentialSchemaSql - .findByGUID(firstActual.guid) + .findByGUID(firstActual.guid, firstActual.resolutionMethod) .transactWallet(tx) .map(_.headOption) From 528b678c7b88fe820ca8c6660128b5aefe1f32c0 Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Thu, 5 Sep 2024 23:23:46 +0400 Subject: [PATCH 09/12] Add prismEnvelope generic response type Signed-off-by: Shota Jolbordi --- .../pollux/PrismEnvelopeResponse.scala | 42 +++++++++ ...redentialDefinitionRegistryEndpoints.scala | 31 ++----- .../CredentialDefinitionController.scala | 15 +--- .../CredentialDefinitionControllerImpl.scala | 22 ++--- .../CredentialDefinitionDidUrlResponse.scala | 80 ++--------------- ...edentialDefinitionDidUrlResponsePage.scala | 7 +- .../SchemaRegistryEndpoints.scala | 27 +++--- .../CredentialSchemaController.scala | 19 ++-- .../CredentialSchemaControllerImpl.scala | 24 ++--- .../http/CredentialSchemaDidUrlResponse.scala | 87 ++----------------- .../CredentialSchemaDidUrlResponsePage.scala | 7 +- .../service/uriResolvers/DidUrlResolver.scala | 21 ++++- .../uriResolvers/DidUrlResolverSpec.scala | 42 +++++++-- .../shared/http/GenericUriResolver.scala | 19 +++- .../identus/shared/models/PrismEnvelope.scala | 16 ++++ 15 files changed, 200 insertions(+), 259 deletions(-) create mode 100644 cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/PrismEnvelopeResponse.scala create mode 100644 shared/core/src/main/scala/org/hyperledger/identus/shared/models/PrismEnvelope.scala diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/PrismEnvelopeResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/PrismEnvelopeResponse.scala new file mode 100644 index 0000000000..d32219ef52 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/PrismEnvelopeResponse.scala @@ -0,0 +1,42 @@ +package org.hyperledger.identus.pollux + +import org.hyperledger.identus.api.http.* +import org.hyperledger.identus.pollux.PrismEnvelopeResponse.annotations +import org.hyperledger.identus.shared.models.PrismEnvelope +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName} +import zio.json.* + +case class PrismEnvelopeResponse( + @description(annotations.resource.description) + @encodedExample(annotations.resource.example) + resource: String, + @description(annotations.resource.description) + @encodedExample(annotations.url.example) + url: String +) extends PrismEnvelope + +object PrismEnvelopeResponse { + given encoder: JsonEncoder[PrismEnvelopeResponse] = + DeriveJsonEncoder.gen[PrismEnvelopeResponse] + + given decoder: JsonDecoder[PrismEnvelopeResponse] = + DeriveJsonDecoder.gen[PrismEnvelopeResponse] + + given schema: Schema[PrismEnvelopeResponse] = Schema.derived + + object annotations { + object resource + extends Annotation[String]( + description = "JCS normalized and base64url encoded json of the resource", + example = "" // TODO Add example + ) + + object url + extends Annotation[String]( + description = "DID url that can be used to resolve this resource", + example = + "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=credential-definition-registry/definitions/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" + ) + } +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala index 79debb14f4..7b5d425fca 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala @@ -8,28 +8,11 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyCredentials import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityLogic.apiKeyHeader import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader -import org.hyperledger.identus.pollux.credentialdefinition.http.{ - CredentialDefinitionDidUrlResponse, - CredentialDefinitionDidUrlResponsePage, - CredentialDefinitionInnerDefinitionDidUrlResponse, - CredentialDefinitionInput, - CredentialDefinitionResponse, - CredentialDefinitionResponsePage, - FilterInput -} +import org.hyperledger.identus.pollux.PrismEnvelopeResponse +import org.hyperledger.identus.pollux.credentialdefinition.http.{CredentialDefinitionDidUrlResponse, CredentialDefinitionDidUrlResponsePage, CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput} import sttp.apispec.{ExternalDocumentation, Tag} import sttp.model.StatusCode -import sttp.tapir.{ - endpoint, - extractFromRequest, - path, - query, - statusCode, - stringToPath, - Endpoint, - EndpointInput, - PublicEndpoint -} +import sttp.tapir.{Endpoint, EndpointInput, PublicEndpoint, endpoint, extractFromRequest, path, query, statusCode, stringToPath} import sttp.tapir.json.zio.{jsonBody, schemaForZioJsonValue} import java.util.UUID @@ -152,7 +135,7 @@ object CredentialDefinitionRegistryEndpoints { val getCredentialDefinitionByIdDidUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - CredentialDefinitionDidUrlResponse, + PrismEnvelopeResponse, Any ] = endpoint.get @@ -163,7 +146,7 @@ object CredentialDefinitionRegistryEndpoints { ) ) .out( - jsonBody[CredentialDefinitionDidUrlResponse].description( + jsonBody[PrismEnvelopeResponse].description( "CredentialDefinition found by `guid`, wrapped in an envelope" ) ) @@ -200,7 +183,7 @@ object CredentialDefinitionRegistryEndpoints { val getCredentialDefinitionInnerDefinitionByIdDidUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - CredentialDefinitionInnerDefinitionDidUrlResponse, + PrismEnvelopeResponse, Any ] = endpoint.get @@ -211,7 +194,7 @@ object CredentialDefinitionRegistryEndpoints { ) ) .out( - jsonBody[CredentialDefinitionInnerDefinitionDidUrlResponse].description("CredentialDefinition found by `guid`") + jsonBody[PrismEnvelopeResponse].description("CredentialDefinition found by `guid`") ) .errorOut(basicFailuresAndNotFound) .name("getCredentialDefinitionInnerDefinitionByIdDidUrl") diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala index 9641911409..8965a42060 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala @@ -2,15 +2,8 @@ package org.hyperledger.identus.pollux.credentialdefinition.controller import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} -import org.hyperledger.identus.pollux.credentialdefinition.http.{ - CredentialDefinitionDidUrlResponse, - CredentialDefinitionDidUrlResponsePage, - CredentialDefinitionInnerDefinitionDidUrlResponse, - CredentialDefinitionInput, - CredentialDefinitionResponse, - CredentialDefinitionResponsePage, - FilterInput -} +import org.hyperledger.identus.pollux.PrismEnvelopeResponse +import org.hyperledger.identus.pollux.credentialdefinition.http.{CredentialDefinitionDidUrlResponse, CredentialDefinitionDidUrlResponsePage, CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput} import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* @@ -32,7 +25,7 @@ trait CredentialDefinitionController { def getCredentialDefinitionByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialDefinitionDidUrlResponse] + ): IO[ErrorResponse, PrismEnvelopeResponse] def getCredentialDefinitionInnerDefinitionByGuid(id: UUID)(implicit rc: RequestContext @@ -40,7 +33,7 @@ trait CredentialDefinitionController { def getCredentialDefinitionInnerDefinitionByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialDefinitionInnerDefinitionDidUrlResponse] + ): IO[ErrorResponse, PrismEnvelopeResponse] def lookupCredentialDefinitions( filter: FilterInput, diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala index c57be304ce..d47701570c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala @@ -9,16 +9,8 @@ import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.FilteredEntries import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.service.CredentialDefinitionService -import org.hyperledger.identus.pollux.credentialdefinition -import org.hyperledger.identus.pollux.credentialdefinition.http.{ - CredentialDefinitionDidUrlResponse, - CredentialDefinitionDidUrlResponsePage, - CredentialDefinitionInnerDefinitionDidUrlResponse, - CredentialDefinitionInput, - CredentialDefinitionResponse, - CredentialDefinitionResponsePage, - FilterInput -} +import org.hyperledger.identus.pollux.{PrismEnvelopeResponse, credentialdefinition} +import org.hyperledger.identus.pollux.credentialdefinition.http.{CredentialDefinitionDidUrlResponse, CredentialDefinitionDidUrlResponsePage, CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput} import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionInput.toDomain import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* @@ -73,12 +65,12 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m override def getCredentialDefinitionByGuidDidUrl( baseUrlServiceName: String, guid: UUID - )(implicit rc: RequestContext): IO[ErrorResponse, CredentialDefinitionDidUrlResponse] = { + )(implicit rc: RequestContext): IO[ErrorResponse, PrismEnvelopeResponse] = { val res = for { cd <- service.getByGUID(guid, ResourceResolutionMethod.DID) response <- ZIO - .fromEither(CredentialDefinitionDidUrlResponse.fromDomain(cd, baseUrlServiceName)) + .fromEither(CredentialDefinitionDidUrlResponse.asPrismEnvelopeResponse(cd, baseUrlServiceName)) .mapError(couldNotParseCredDefResponse) } yield response @@ -96,7 +88,7 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m override def getCredentialDefinitionInnerDefinitionByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialDefinitionInnerDefinitionDidUrlResponse] = { + ): IO[ErrorResponse, PrismEnvelopeResponse] = { val res = for { cd <- service.getByGUID(guid, ResourceResolutionMethod.DID) authorDid <- ZIO @@ -105,7 +97,7 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m response <- ZIO .fromEither( CredentialDefinitionInnerDefinitionDidUrlResponse - .fromDomain(cd.definition, authorDid, cd.guid, baseUrlServiceName) + .asPrismEnvelopeResponse(cd.definition, authorDid, cd.guid, baseUrlServiceName) ) .mapError(couldNotParseCredDefResponse) @@ -151,7 +143,7 @@ class CredentialDefinitionControllerImpl(service: CredentialDefinitionService, m ) entriesZio = filteredEntries.entries - .traverse(cd => CredentialDefinitionDidUrlResponse.fromDomain(cd, baseUrlServiceName)) + .traverse(cd => CredentialDefinitionDidUrlResponse.asPrismEnvelopeResponse(cd, baseUrlServiceName)) entries <- ZIO .fromEither(entriesZio) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala index 2c42c23b33..b921ecce63 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala @@ -2,10 +2,9 @@ package org.hyperledger.identus.pollux.credentialdefinition.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition -import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionDidUrlResponse.annotations as credentialDefinitionResponseAnnotations -import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionInnerDefinitionDidUrlResponse.annotations as credentialDefinitionInnerResponseAnnotations import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import sttp.model.Uri @@ -18,18 +17,9 @@ import zio.json.ast.Json import java.util.UUID import scala.collection.immutable.ListMap -case class CredentialDefinitionDidUrlResponse( - @description(credentialDefinitionResponseAnnotations.resource.description) - @encodedExample(credentialDefinitionResponseAnnotations.resource.example) - resource: String, - @description(credentialDefinitionResponseAnnotations.credentialDefinitionUrl.description) - @encodedExample(credentialDefinitionResponseAnnotations.credentialDefinitionUrl.example) - credentialDefinitionUrl: String, -) - object CredentialDefinitionDidUrlResponse { - def fromDomain(cd: CredentialDefinition, serviceName: String): Either[String, CredentialDefinitionDidUrlResponse] = { + def asPrismEnvelopeResponse(cd: CredentialDefinition, serviceName: String): Either[String, PrismEnvelopeResponse] = { for { authorDid <- PrismDID.fromString(cd.author) canonicalized <- JsonUtils.canonicalizeToJcs(cd.toJson).left.map(_.toString) @@ -44,54 +34,21 @@ object CredentialDefinitionDidUrlResponse { ), None ).toString - } yield CredentialDefinitionDidUrlResponse( + } yield PrismEnvelopeResponse( resource = encoded, - credentialDefinitionUrl = didUrl + url = didUrl ) } - - given encoder: zio.json.JsonEncoder[CredentialDefinitionDidUrlResponse] = - DeriveJsonEncoder.gen[CredentialDefinitionDidUrlResponse] - - given decoder: zio.json.JsonDecoder[CredentialDefinitionDidUrlResponse] = - DeriveJsonDecoder.gen[CredentialDefinitionDidUrlResponse] - - given schema: Schema[CredentialDefinitionDidUrlResponse] = Schema.derived - - object annotations { - object resource - extends Annotation[String]( - description = "JCS normalized and base64url encoded json credential definition", - example = "" // TODO Add example - ) - - object credentialDefinitionUrl - extends Annotation[String]( - description = "DID url that can be used to resolve this credential definition", - example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=credential-definition-registry/definitions/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" - ) - } - } -case class CredentialDefinitionInnerDefinitionDidUrlResponse( - @description(credentialDefinitionInnerResponseAnnotations.resource.description) - @encodedExample(credentialDefinitionInnerResponseAnnotations.resource.example) - resource: String, - @description(credentialDefinitionInnerResponseAnnotations.credentialDefinitionUrl.description) - @encodedExample(credentialDefinitionInnerResponseAnnotations.credentialDefinitionUrl.example) - credentialDefinitionUrl: String, -) - object CredentialDefinitionInnerDefinitionDidUrlResponse { - def fromDomain( + def asPrismEnvelopeResponse( innerDefinition: Json, authorDid: PrismDID, definitionGuid: UUID, serviceName: String - ): Either[String, CredentialDefinitionInnerDefinitionDidUrlResponse] = { + ): Either[String, PrismEnvelopeResponse] = { for { canonicalized <- JsonUtils.canonicalizeToJcs(innerDefinition.toJson).left.map(_.toString) encoded = Base64Utils.encodeURL(canonicalized.getBytes) @@ -107,31 +64,10 @@ object CredentialDefinitionInnerDefinitionDidUrlResponse { ), None ).toString - } yield CredentialDefinitionInnerDefinitionDidUrlResponse( + } yield PrismEnvelopeResponse( resource = encoded, - credentialDefinitionUrl = didUrl + url = didUrl ) } - object annotations { - object resource - extends Annotation[String]( - description = - "JCS normalized and base64url encoded inner json definition of the credential definition (without metadata)", - example = "" // TODO Add example - ) - - object credentialDefinitionUrl - extends Annotation[String]( - description = "DID url that can be used to resolve this schema inner schema", - example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=credential-definition-registry/definitions/did-url/definition/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" - ) - } - - given encoder: JsonEncoder[CredentialDefinitionInnerDefinitionDidUrlResponse] = - DeriveJsonEncoder.gen[CredentialDefinitionInnerDefinitionDidUrlResponse] - given decoder: JsonDecoder[CredentialDefinitionInnerDefinitionDidUrlResponse] = - DeriveJsonDecoder.gen[CredentialDefinitionInnerDefinitionDidUrlResponse] - given schema: Schema[CredentialDefinitionInnerDefinitionDidUrlResponse] = Schema.derived } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala index 4c7214261a..5e5c7646fb 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.pollux.credentialdefinition.http import org.hyperledger.identus.api.http.Annotation +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionResponsePage.annotations import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{description, encodedExample} @@ -9,7 +10,7 @@ import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} case class CredentialDefinitionDidUrlResponsePage( @description(annotations.contents.description) @encodedExample(annotations.contents.example) - contents: Seq[CredentialDefinitionDidUrlResponse], + contents: Seq[PrismEnvelopeResponse], @description(annotations.kind.description) @encodedExample(annotations.kind.example) kind: String = "CredentialDefinitionDidUrlPage", @@ -50,9 +51,9 @@ object CredentialDefinitionDidUrlResponsePage { object annotations { object contents - extends Annotation[Seq[CredentialDefinitionDidUrlResponse]]( + extends Annotation[Seq[PrismEnvelopeResponse]]( description = - "A sequence of CredentialDefinitionDidUrlResponse objects representing the list of credential definitions that the API response contains", + "A sequence of PrismEnvelopeResponse objects representing the list of credential definitions that the API response contains", example = Seq.empty ) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala index 5f831dbb29..71790caba5 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -8,15 +8,8 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyCredentials import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityLogic.apiKeyHeader import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader -import org.hyperledger.identus.pollux.credentialschema.http.{ - CredentialSchemaDidUrlResponse, - CredentialSchemaDidUrlResponsePage, - CredentialSchemaInnerDidUrlResponse, - CredentialSchemaInput, - CredentialSchemaResponse, - CredentialSchemaResponsePage, - FilterInput -} +import org.hyperledger.identus.pollux.PrismEnvelopeResponse +import org.hyperledger.identus.pollux.credentialschema.http.{CredentialSchemaDidUrlResponse, CredentialSchemaDidUrlResponsePage, CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, FilterInput} import sttp.apispec.{ExternalDocumentation, Tag} import sttp.model.StatusCode import sttp.tapir.* @@ -100,7 +93,7 @@ object SchemaRegistryEndpoints { (ApiKeyCredentials, JwtCredentials), (RequestContext, CredentialSchemaInput), ErrorResponse, - CredentialSchemaDidUrlResponse, + PrismEnvelopeResponse, Any ] = endpoint.post @@ -120,7 +113,7 @@ object SchemaRegistryEndpoints { "The new credential schema record is successfully created" ) ) - .out(jsonBody[CredentialSchemaDidUrlResponse]) + .out(jsonBody[PrismEnvelopeResponse]) .description("Credential schema record") .errorOut(basicFailureAndNotFoundAndForbidden) .name("createSchemaDidUrl") @@ -174,7 +167,7 @@ object SchemaRegistryEndpoints { (ApiKeyCredentials, JwtCredentials), (RequestContext, UUID, CredentialSchemaInput), ErrorResponse, - CredentialSchemaDidUrlResponse, + PrismEnvelopeResponse, Any ] = endpoint.put @@ -198,7 +191,7 @@ object SchemaRegistryEndpoints { "The credential schema record is successfully updated" ) ) - .out(jsonBody[CredentialSchemaDidUrlResponse]) + .out(jsonBody[PrismEnvelopeResponse]) .description("Credential schema record wrapped in an envelope") .errorOut(basicFailureAndNotFoundAndForbidden) .name("updateSchemaDidUrl") @@ -234,7 +227,7 @@ object SchemaRegistryEndpoints { val getSchemaByIdDidUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - CredentialSchemaDidUrlResponse, + PrismEnvelopeResponse, Any ] = endpoint.get @@ -245,7 +238,7 @@ object SchemaRegistryEndpoints { ) ) .out( - jsonBody[CredentialSchemaDidUrlResponse].description( + jsonBody[PrismEnvelopeResponse].description( "CredentialSchema found by `guid`, wrapped in an envelope" ) ) @@ -280,7 +273,7 @@ object SchemaRegistryEndpoints { val getRawSchemaByIdDidUrlEndpoint: PublicEndpoint[ (RequestContext, UUID), ErrorResponse, - CredentialSchemaInnerDidUrlResponse, // returns an envelope, where resource is a json of wrapped schema + PrismEnvelopeResponse, // returns an envelope, where resource is a json of wrapped schema Any ] = endpoint.get @@ -291,7 +284,7 @@ object SchemaRegistryEndpoints { ) ) .out( - jsonBody[CredentialSchemaInnerDidUrlResponse].description("Raw JSON response of the CredentialSchema") + jsonBody[PrismEnvelopeResponse].description("Raw JSON response of the CredentialSchema") ) .errorOut(basicFailuresAndNotFound) .name("getRawSchemaByIdDidUrl") diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala index 02ebfcdaea..b32e857aa1 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -2,15 +2,8 @@ package org.hyperledger.identus.pollux.credentialschema.controller import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} -import org.hyperledger.identus.pollux.credentialschema.http.{ - CredentialSchemaDidUrlResponse, - CredentialSchemaDidUrlResponsePage, - CredentialSchemaInnerDidUrlResponse, - CredentialSchemaInput, - CredentialSchemaResponse, - CredentialSchemaResponsePage, - FilterInput -} +import org.hyperledger.identus.pollux.PrismEnvelopeResponse +import org.hyperledger.identus.pollux.credentialschema.http.{CredentialSchemaDidUrlResponse, CredentialSchemaDidUrlResponsePage, CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, FilterInput} import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* import zio.json.ast.Json @@ -25,7 +18,7 @@ trait CredentialSchemaController { def createSchemaDidUrl(baseUrlServiceName: String, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] + ): ZIO[WalletAccessContext, ErrorResponse, PrismEnvelopeResponse] def updateSchema(id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext @@ -33,7 +26,7 @@ trait CredentialSchemaController { def updateSchemaDidUrl(baseUrlServiceName: String, id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] + ): ZIO[WalletAccessContext, ErrorResponse, PrismEnvelopeResponse] def getSchemaByGuid(id: UUID)(implicit rc: RequestContext @@ -41,7 +34,7 @@ trait CredentialSchemaController { def getSchemaByGuidDidUrl(baseUrlServiceName: String, id: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaDidUrlResponse] + ): IO[ErrorResponse, PrismEnvelopeResponse] def getSchemaJsonByGuid(id: UUID)(implicit rc: RequestContext @@ -49,7 +42,7 @@ trait CredentialSchemaController { def getSchemaJsonByGuidDidUrl(baseUrlServiceName: String, id: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaInnerDidUrlResponse] + ): IO[ErrorResponse, PrismEnvelopeResponse] def lookupSchemas( filter: FilterInput, diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index 3291d909df..dc7d9ed1ed 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -6,7 +6,6 @@ import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{CollectionStats, Order, Pagination} import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID} -import org.hyperledger.identus.pollux.core import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema.FilteredEntries import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod @@ -21,6 +20,7 @@ import org.hyperledger.identus.pollux.credentialschema.http.{ FilterInput } import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaInput.toDomain +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* import zio.json.* @@ -50,12 +50,12 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI def createSchemaDidUrl(baseUrlServiceName: String, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] = { + ): ZIO[WalletAccessContext, ErrorResponse, PrismEnvelopeResponse] = { val res = for { validated <- validatePrismDID(in.author) result <- service.create(toDomain(in), ResourceResolutionMethod.DID) response <- ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(result, baseUrlServiceName)) + .fromEither(CredentialSchemaDidUrlResponse.asPrismEnvelopeResponse(result, baseUrlServiceName)) .mapError(parsingCredentialSchemaError) } yield response @@ -76,13 +76,13 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI override def updateSchemaDidUrl(baseUrlServiceName: String, id: UUID, in: CredentialSchemaInput)(implicit rc: RequestContext - ): ZIO[WalletAccessContext, ErrorResponse, CredentialSchemaDidUrlResponse] = { + ): ZIO[WalletAccessContext, ErrorResponse, PrismEnvelopeResponse] = { val res = for { _ <- validatePrismDID(in.author) cs <- service .update(id, toDomain(in), ResourceResolutionMethod.DID) result <- ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) + .fromEither(CredentialSchemaDidUrlResponse.asPrismEnvelopeResponse(cs, baseUrlServiceName)) .mapError(parsingCredentialSchemaError) } yield result @@ -103,11 +103,11 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI override def getSchemaByGuidDidUrl(baseUrlServiceName: String, guid: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaDidUrlResponse] = { - val res: IO[ErrorResponse, CredentialSchemaDidUrlResponse] = for { + ): IO[ErrorResponse, PrismEnvelopeResponse] = { + val res: IO[ErrorResponse, PrismEnvelopeResponse] = for { cs <- service.getByGUID(guid, ResourceResolutionMethod.DID) response <- ZIO - .fromEither(CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) + .fromEither(CredentialSchemaDidUrlResponse.asPrismEnvelopeResponse(cs, baseUrlServiceName)) .mapError(parsingCredentialSchemaError) } yield response @@ -126,14 +126,16 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI override def getSchemaJsonByGuidDidUrl(baseUrlServiceName: String, id: UUID)(implicit rc: RequestContext - ): IO[ErrorResponse, CredentialSchemaInnerDidUrlResponse] = { + ): IO[ErrorResponse, PrismEnvelopeResponse] = { val res = for { cs <- service.getByGUID(id, ResourceResolutionMethod.DID) authorDid <- ZIO .fromEither(PrismDID.fromString(cs.author)) .mapError(_ => ErrorResponse.internalServerError(detail = Some("Invalid schema author DID"))) response <- ZIO - .fromEither(CredentialSchemaInnerDidUrlResponse.fromDomain(cs.schema, authorDid, cs.id, baseUrlServiceName)) + .fromEither( + CredentialSchemaInnerDidUrlResponse.asPrismEnvelopeResponse(cs.schema, authorDid, cs.id, baseUrlServiceName) + ) .mapError(parsingCredentialSchemaError) } yield response @@ -176,7 +178,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService, managedDI pagination.limit ) entriesZio = filteredEntries.entries - .traverse(cs => CredentialSchemaDidUrlResponse.fromDomain(cs, baseUrlServiceName)) + .traverse(cs => CredentialSchemaDidUrlResponse.asPrismEnvelopeResponse(cs, baseUrlServiceName)) entries <- ZIO .fromEither(entriesZio) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala index c2926f3781..b812cc4a8e 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -1,40 +1,21 @@ package org.hyperledger.identus.pollux.credentialschema.http -import org.hyperledger.identus.api.http.* import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema -import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponse.annotations as SchemaResponseAnnotations -import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaInnerDidUrlResponse.annotations as SchemaResponseInnerAnnotations import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} -import sttp.model.Uri -import sttp.model.Uri.* -import sttp.tapir.Schema -import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName} import zio.json.* import zio.json.ast.Json import java.util.UUID import scala.collection.immutable.ListMap -case class CredentialSchemaDidUrlResponse( - @description(SchemaResponseAnnotations.resource.description) - @encodedExample(SchemaResponseAnnotations.resource.example) - resource: String, - @description(SchemaResponseAnnotations.schemaUrl.description) - @encodedExample(SchemaResponseAnnotations.schemaUrl.example) - schemaUrl: String, -) - object CredentialSchemaDidUrlResponse { - def fromDomain(cs: CredentialSchema, serviceName: String): Either[String, CredentialSchemaDidUrlResponse] = { + def asPrismEnvelopeResponse(cs: CredentialSchema, serviceName: String): Either[String, PrismEnvelopeResponse] = { -// Url : TODO: change in codebase with DIDUrl usage -// .parse(s"${jwtIssuer.did}?resourceService=$statusListRegistryServiceName&resourcePath=$resourcePath#$segment") -// .toString -// , for { authorDid <- PrismDID.fromString(cs.author) canonicalized <- JsonUtils.canonicalizeToJcs(cs.toJson).left.map(_.toString) @@ -49,52 +30,22 @@ object CredentialSchemaDidUrlResponse { ), None ).toString - } yield CredentialSchemaDidUrlResponse( + } yield PrismEnvelopeResponse( resource = encoded, - schemaUrl = didUrl + url = didUrl ) } - given encoder: JsonEncoder[CredentialSchemaDidUrlResponse] = - DeriveJsonEncoder.gen[CredentialSchemaDidUrlResponse] - given decoder: JsonDecoder[CredentialSchemaDidUrlResponse] = - DeriveJsonDecoder.gen[CredentialSchemaDidUrlResponse] - given schema: Schema[CredentialSchemaDidUrlResponse] = Schema.derived - - object annotations { - object resource - extends Annotation[String]( - description = "JCS normalized and base64url encoded json schema", - example = - """ewogICJndWlkIjogIjNmODZhNzNmLTViNzgtMzljNy1hZjc3LTBjMTYxMjNmYTljMiIsCiAgImlkIjogImYyYmZiZjc4LThiZDYtNGNjNi04YjM5LWIzYTI1ZTAxZThlYSIsCiAgImxvbmdJZCI6ICJkaWQ6cHJpc206YWdlbnQvZjJiZmJmNzgtOGJkNi00Y2M2LThiMzktYjNhMjVlMDFlOGVhP3ZlcnNpb249MS4wLjAiLAogICJuYW1lIjogImRyaXZpbmctbGljZW5zZSIsCiAgInZlcnNpb24iOiAiMS4wLjAiLAogICJkZXNjcmlwdGlvbiI6ICJEcml2aW5nIExpY2Vuc2UgU2NoZW1hIiwKICAidHlwZSI6ICJodHRwczovL3czYy1jY2cuZ2l0aHViLmlvL3ZjLWpzb24tc2NoZW1hcy9zY2hlbWEvMi4wL3NjaGVtYS5qc29uIiwKICAiYXV0aG9yIjogImRpZDpwcmlzbTo0YTViNWNmMGE1MTNlODNiNTk4YmJlYTI1Y2Q2MTk2NzQ2NzQ3ZjM2MWE3M2VmNzcwNjgyNjhiYzliZDczMmZmIiwKICAiYXV0aG9yZWQiOiAiMjAyMy0wMy0xNFQxNDo0MTo0Ni43MTM5NDNaIiwKICAidGFncyI6IFsKICAgICJkcml2aW5nIiwKICAgICJsaWNlbnNlIgogIF0sCiAgInNjaGVtYSI6IHsKICAgICIkaWQiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9kcml2aW5nLWxpY2Vuc2UtMS4wLjAiLAogICAgIiRzY2hlbWEiOiAiaHR0cHM6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQvMjAyMC0xMi9zY2hlbWEiLAogICAgImRlc2NyaXB0aW9uIjogIkRyaXZpbmcgTGljZW5zZSIsCiAgICAidHlwZSI6ICJvYmplY3QiLAogICAgInByb3BlcnRpZXMiOiB7CiAgICAgICJlbWFpbEFkZHJlc3MiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImVtYWlsIgogICAgICB9LAogICAgICAiZ2l2ZW5OYW1lIjogewogICAgICAgICJ0eXBlIjogInN0cmluZyIKICAgICAgfSwKICAgICAgImZhbWlseU5hbWUiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZGF0ZU9mSXNzdWFuY2UiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAiZm9ybWF0IjogImRhdGUtdGltZSIKICAgICAgfSwKICAgICAgImRyaXZpbmdMaWNlbnNlSUQiOiB7CiAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICB9LAogICAgICAiZHJpdmluZ0NsYXNzIjogewogICAgICAgICJ0eXBlIjogImludGVnZXIiCiAgICAgIH0KICAgIH0sCiAgICAicmVxdWlyZWQiOiBbCiAgICAgICJlbWFpbEFkZHJlc3MiLAogICAgICAiZmFtaWx5TmFtZSIsCiAgICAgICJkYXRlT2ZJc3N1YW5jZSIsCiAgICAgICJkcml2aW5nTGljZW5zZUlEIiwKICAgICAgImRyaXZpbmdDbGFzcyIKICAgIF0sCiAgICAiYWRkaXRpb25hbFByb3BlcnRpZXMiOiB0cnVlCiAgfQp9""" - ) - - object schemaUrl - extends Annotation[String]( - description = "DID url that can be used to resolve this schema", - example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6?resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46" - ) - } } -case class CredentialSchemaInnerDidUrlResponse( - @description(SchemaResponseInnerAnnotations.resource.description) - @encodedExample(SchemaResponseInnerAnnotations.resource.example) - resource: String, - @description(SchemaResponseInnerAnnotations.schemaUrl.description) - @encodedExample(SchemaResponseInnerAnnotations.schemaUrl.example) - schemaUrl: String, -) - object CredentialSchemaInnerDidUrlResponse { - def fromDomain( + def asPrismEnvelopeResponse( innerSchema: Json, authorDid: PrismDID, schemaGuid: UUID, serviceName: String - ): Either[String, CredentialSchemaInnerDidUrlResponse] = { + ): Either[String, PrismEnvelopeResponse] = { for { canonicalized <- JsonUtils.canonicalizeToJcs(innerSchema.toJson).left.map(_.toString) encoded = Base64Utils.encodeURL(canonicalized.getBytes) @@ -108,31 +59,9 @@ object CredentialSchemaInnerDidUrlResponse { ), None ).toString - } yield CredentialSchemaInnerDidUrlResponse( + } yield PrismEnvelopeResponse( resource = encoded, - schemaUrl = didUrl + url = didUrl ) } - - object annotations { - object resource - extends Annotation[String]( - description = "JCS normalized and base64url encoded inner json schema (without metadata)", - example = - """eyIkaWQiOiJodHRwczovL2V4YW1wbGUuY29tL2RyaXZpbmctbGljZW5zZS0xLjAuMCIsIiRzY2hlbWEiOiJodHRwczovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC8yMDIwLTEyL3NjaGVtYSIsImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjp0cnVlLCJkZXNjcmlwdGlvbiI6IkRyaXZpbmcgTGljZW5zZSIsInByb3BlcnRpZXMiOnsiZGF0ZU9mSXNzdWFuY2UiOnsiZm9ybWF0IjoiZGF0ZS10aW1lIiwidHlwZSI6InN0cmluZyJ9LCJkcml2aW5nQ2xhc3MiOnsidHlwZSI6ImludGVnZXIifSwiZHJpdmluZ0xpY2Vuc2VJRCI6eyJ0eXBlIjoic3RyaW5nIn0sImVtYWlsQWRkcmVzcyI6eyJmb3JtYXQiOiJlbWFpbCIsInR5cGUiOiJzdHJpbmcifSwiZmFtaWx5TmFtZSI6eyJ0eXBlIjoic3RyaW5nIn0sImdpdmVuTmFtZSI6eyJ0eXBlIjoic3RyaW5nIn19LCJyZXF1aXJlZCI6WyJlbWFpbEFkZHJlc3MiLCJmYW1pbHlOYW1lIiwiZGF0ZU9mSXNzdWFuY2UiLCJkcml2aW5nTGljZW5zZUlEIiwiZHJpdmluZ0NsYXNzIl0sInR5cGUiOiJvYmplY3QifQ==""" - ) - - object schemaUrl - extends Annotation[String]( - description = "DID url that can be used to resolve this schema inner schema", - example = - "did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=4074bb1a8e0ea45437ad86763cd7e12de3fe8349ef19113df773b0d65c8a9c46/schema" - ) - } - - given encoder: JsonEncoder[CredentialSchemaInnerDidUrlResponse] = - DeriveJsonEncoder.gen[CredentialSchemaInnerDidUrlResponse] - given decoder: JsonDecoder[CredentialSchemaInnerDidUrlResponse] = - DeriveJsonDecoder.gen[CredentialSchemaInnerDidUrlResponse] - given schema: Schema[CredentialSchemaInnerDidUrlResponse] = Schema.derived } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala index 38cf03b71c..dc67a0d452 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala @@ -1,6 +1,7 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.api.http.Annotation +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponsePage.annotations import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{description, encodedExample} @@ -9,7 +10,7 @@ import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} case class CredentialSchemaDidUrlResponsePage( @description(annotations.contents.description) @encodedExample(annotations.contents.example) - contents: Seq[CredentialSchemaDidUrlResponse], + contents: Seq[PrismEnvelopeResponse], @description(annotations.kind.description) @encodedExample(annotations.kind.example) kind: String = "CredentialSchemaDidUrlPage", @@ -48,9 +49,9 @@ object CredentialSchemaDidUrlResponsePage { object annotations { object contents - extends Annotation[Seq[CredentialSchemaDidUrlResponse]]( + extends Annotation[Seq[PrismEnvelopeResponse]]( description = - "A sequence of CredentialSchemaDidUrlResponse objects representing the list of credential schemas wrapped in an envelope", + "A sequence of PrismEnvelopeResponse objects representing the list of credential schemas wrapped in an envelope", example = Seq.empty ) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala index 25f9212a1f..cd2bedb22e 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala @@ -5,8 +5,11 @@ import org.hyperledger.identus.pollux.vc.jwt import org.hyperledger.identus.pollux.vc.jwt.* import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.http.{GenericUriResolverError, UriResolver} +import org.hyperledger.identus.shared.models.PrismEnvelopeData import org.hyperledger.identus.shared.models.StatusCode import zio.* +import zio.json.* +import zio.json.ast.Json class DidUrlResolver(httpUrlResolver: HttpUrlResolver, didResolver: DidResolver) extends UriResolver { import DidUrlResolver.* @@ -54,9 +57,15 @@ class DidUrlResolver(httpUrlResolver: HttpUrlResolver, didResolver: DidResolver) .mapError(_ => InvalidURI(baseUrl)) result <- httpUrlResolver.resolve(finalUrl) - validatedResult <- maybeResourceHash.fold(ZIO.succeed(result)) { hash => - val computedHash = Sha256Hash.compute(result.getBytes()).hexEncoded - if (computedHash == hash) ZIO.succeed(result) + envelope <- ZIO + .fromEither(result.fromJson[PrismEnvelopeData]) + .mapError(_ => InvalidResponseFromResourceServer(finalUrl)) + + envelopeAsStr = envelope.toString + + validatedResult <- maybeResourceHash.fold(ZIO.succeed(envelopeAsStr)) { hash => + val computedHash = Sha256Hash.compute(envelope.resource.getBytes()).hexEncoded + if (computedHash == hash) ZIO.succeed(envelopeAsStr) else ZIO.fail(InvalidHash(hash, computedHash)) } @@ -107,6 +116,12 @@ object DidUrlResolver { s"Invalid hash, expected: $expectedHash, computed: $computedHash" ) + final case class InvalidResponseFromResourceServer(resourceUrl: String) + extends DidUrlResolverError( + StatusCode.UnprocessableContent, + s"Invalid Json response, expected an envelope, resourceUrl: $resourceUrl" + ) + val layer: URLayer[HttpUrlResolver & DidResolver, DidUrlResolver] = ZLayer.fromFunction(DidUrlResolver(_, _)) } diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala index bce33d0dcf..35ee78d368 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala @@ -4,8 +4,10 @@ import io.circe.* import io.lemonlabs.uri.Url import org.hyperledger.identus.pollux.vc.jwt.* import org.hyperledger.identus.shared.crypto.Sha256Hash +import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import zio.* import zio.test.* +import zio.json.* import zio.test.Assertion.* import java.time.Instant @@ -68,9 +70,41 @@ object DidUrlResolverSpec extends ZIOSpecDefault { |} |""".stripMargin + private val normalizedSchema = JsonUtils.canonicalizeToJcs(schema).toOption.get + private val encodedSchema = Base64Utils.encodeURL(normalizedSchema.getBytes) + + private val schemaHash = Sha256Hash.compute(encodedSchema.getBytes()).hexEncoded + + + private val testDidUrl = Url + .parse( + s"did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=$schemaHash" + ) + .toString + + case class ResponseEnvelope(resource: String, schemaUrl: String) + object ResponseEnvelope { + given encoder: JsonEncoder[ResponseEnvelope] = + DeriveJsonEncoder.gen[ResponseEnvelope] + + given decoder: JsonDecoder[ResponseEnvelope] = + DeriveJsonDecoder.gen[ResponseEnvelope] + } + class MockHttpUrlResolver extends HttpUrlResolver(null) { // Mock implementation, always resolves some schema - override def resolve(uri: String) = ZIO.succeed(schema) + override def resolve(uri: String) = { + + + val responseEnvelope = ResponseEnvelope( + resource = encodedSchema, + schemaUrl = uri + ) + + + ZIO.succeed(responseEnvelope.toJson) + + } } private val didResolverLayer = ZLayer.succeed(new DidResolver { @@ -145,13 +179,7 @@ object DidUrlResolverSpec extends ZIOSpecDefault { }) private val httpUrlResolver = ZLayer.succeed(new MockHttpUrlResolver) - private val schemaHash = Sha256Hash.compute(schema.getBytes()).hexEncoded - private val testDidUrl = Url - .parse( - s"did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=$schemaHash" - ) - .toString override def spec = { suite("DidUrlResolverSpec")( diff --git a/shared/core/src/main/scala/org/hyperledger/identus/shared/http/GenericUriResolver.scala b/shared/core/src/main/scala/org/hyperledger/identus/shared/http/GenericUriResolver.scala index 72d6d613bc..f25f6b37f3 100644 --- a/shared/core/src/main/scala/org/hyperledger/identus/shared/http/GenericUriResolver.scala +++ b/shared/core/src/main/scala/org/hyperledger/identus/shared/http/GenericUriResolver.scala @@ -2,7 +2,10 @@ package org.hyperledger.identus.shared.http import io.lemonlabs.uri.{Uri, Url, Urn} import org.hyperledger.identus.shared.models.{Failure, StatusCode} +import org.hyperledger.identus.shared.models.PrismEnvelopeData +import org.hyperledger.identus.shared.utils.Base64Utils import zio.* +import zio.json.* import scala.util @@ -21,7 +24,15 @@ class GenericUriResolver(resolvers: Map[String, UriResolver]) extends UriResolve case url: Url => url.schemeOption.fold(ZIO.fail(InvalidUri(uri)))(schema => resolvers.get(schema).fold(ZIO.fail(UnsupportedUriSchema(schema))) { resolver => - resolver.resolve(uri) + resolver.resolve(uri).flatMap { res => + schema match + case "did" => + val envelope = res.fromJson[PrismEnvelopeData].left.map(_ => DidUriResponseNotEnvelope(url.toString)) + ZIO.fromEither( + envelope.map(env => Base64Utils.decodeUrlToString(env.resource)) + ) + case _ => ZIO.succeed(res) + } } ) @@ -41,6 +52,12 @@ trait GenericUriResolverError(val statusCode: StatusCode, val userFacingMessage: } } +case class DidUriResponseNotEnvelope(uri: String) + extends GenericUriResolverError( + StatusCode.UnprocessableContent, + s"The response of DID uri resolution was not prism envelope: uri=[$uri]" + ) + case class InvalidUri(uri: String) extends GenericUriResolverError(StatusCode.UnprocessableContent, s"The URI to dereference is invalid: uri=[$uri]") diff --git a/shared/core/src/main/scala/org/hyperledger/identus/shared/models/PrismEnvelope.scala b/shared/core/src/main/scala/org/hyperledger/identus/shared/models/PrismEnvelope.scala new file mode 100644 index 0000000000..c02df829e3 --- /dev/null +++ b/shared/core/src/main/scala/org/hyperledger/identus/shared/models/PrismEnvelope.scala @@ -0,0 +1,16 @@ +package org.hyperledger.identus.shared.models +import zio.json.* + +trait PrismEnvelope { + val resource: String + val url: String +} + +case class PrismEnvelopeData(resource: String, url: String) extends PrismEnvelope +object PrismEnvelopeData { + given encoder: JsonEncoder[PrismEnvelopeData] = + DeriveJsonEncoder.gen[PrismEnvelopeData] + + given decoder: JsonDecoder[PrismEnvelopeData] = + DeriveJsonDecoder.gen[PrismEnvelopeData] +} From ba276a8a4ed3638ff845141c6cd229756a91f27d Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Fri, 6 Sep 2024 15:28:57 +0400 Subject: [PATCH 10/12] Fix tests Signed-off-by: Shota Jolbordi --- ...redentialDefinitionRegistryEndpoints.scala | 22 +++++++++++-- .../CredentialDefinitionController.scala | 10 +++++- .../CredentialDefinitionControllerImpl.scala | 12 +++++-- .../CredentialDefinitionDidUrlResponse.scala | 2 +- ...edentialDefinitionDidUrlResponsePage.scala | 2 +- .../SchemaRegistryEndpoints.scala | 10 +++++- .../CredentialSchemaController.scala | 10 +++++- .../http/CredentialSchemaDidUrlResponse.scala | 2 +- .../CredentialSchemaDidUrlResponsePage.scala | 2 +- .../CredentialSchemaMultiTenancySpec.scala | 6 ++-- .../service/uriResolvers/DidUrlResolver.scala | 3 +- .../uriResolvers/DidUrlResolverSpec.scala | 33 +++++++------------ ...edentialDefinitionSqlIntegrationSpec.scala | 11 +++++-- .../CredentialSchemaSqlIntegrationSpec.scala | 9 +++-- 14 files changed, 92 insertions(+), 42 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala index 7b5d425fca..3bc3821c95 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala @@ -8,11 +8,29 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyCredentials import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityLogic.apiKeyHeader import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader +import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponse, + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionInnerDefinitionDidUrlResponse, + CredentialDefinitionInput, + CredentialDefinitionResponse, + CredentialDefinitionResponsePage, + FilterInput +} import org.hyperledger.identus.pollux.PrismEnvelopeResponse -import org.hyperledger.identus.pollux.credentialdefinition.http.{CredentialDefinitionDidUrlResponse, CredentialDefinitionDidUrlResponsePage, CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput} import sttp.apispec.{ExternalDocumentation, Tag} import sttp.model.StatusCode -import sttp.tapir.{Endpoint, EndpointInput, PublicEndpoint, endpoint, extractFromRequest, path, query, statusCode, stringToPath} +import sttp.tapir.{ + endpoint, + extractFromRequest, + path, + query, + statusCode, + stringToPath, + Endpoint, + EndpointInput, + PublicEndpoint +} import sttp.tapir.json.zio.{jsonBody, schemaForZioJsonValue} import java.util.UUID diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala index 8965a42060..4fa919195c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionController.scala @@ -2,8 +2,16 @@ package org.hyperledger.identus.pollux.credentialdefinition.controller import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} +import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponse, + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionInnerDefinitionDidUrlResponse, + CredentialDefinitionInput, + CredentialDefinitionResponse, + CredentialDefinitionResponsePage, + FilterInput +} import org.hyperledger.identus.pollux.PrismEnvelopeResponse -import org.hyperledger.identus.pollux.credentialdefinition.http.{CredentialDefinitionDidUrlResponse, CredentialDefinitionDidUrlResponsePage, CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput} import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala index d47701570c..cccef90ddb 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/controller/CredentialDefinitionControllerImpl.scala @@ -6,11 +6,19 @@ import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{CollectionStats, Order, Pagination} import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID} +import org.hyperledger.identus.pollux.{credentialdefinition, PrismEnvelopeResponse} import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.FilteredEntries import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.core.service.CredentialDefinitionService -import org.hyperledger.identus.pollux.{PrismEnvelopeResponse, credentialdefinition} -import org.hyperledger.identus.pollux.credentialdefinition.http.{CredentialDefinitionDidUrlResponse, CredentialDefinitionDidUrlResponsePage, CredentialDefinitionInnerDefinitionDidUrlResponse, CredentialDefinitionInput, CredentialDefinitionResponse, CredentialDefinitionResponsePage, FilterInput} +import org.hyperledger.identus.pollux.credentialdefinition.http.{ + CredentialDefinitionDidUrlResponse, + CredentialDefinitionDidUrlResponsePage, + CredentialDefinitionInnerDefinitionDidUrlResponse, + CredentialDefinitionInput, + CredentialDefinitionResponse, + CredentialDefinitionResponsePage, + FilterInput +} import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionInput.toDomain import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala index b921ecce63..6ea8cef333 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponse.scala @@ -2,9 +2,9 @@ package org.hyperledger.identus.pollux.credentialdefinition.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} -import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import sttp.model.Uri diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala index 5e5c7646fb..bfcb77011e 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/http/CredentialDefinitionDidUrlResponsePage.scala @@ -1,8 +1,8 @@ package org.hyperledger.identus.pollux.credentialdefinition.http import org.hyperledger.identus.api.http.Annotation -import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.credentialdefinition.http.CredentialDefinitionResponsePage.annotations +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{description, encodedExample} import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala index 71790caba5..1841561f30 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -8,8 +8,16 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyCredentials import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityLogic.apiKeyHeader import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader +import org.hyperledger.identus.pollux.credentialschema.http.{ + CredentialSchemaDidUrlResponse, + CredentialSchemaDidUrlResponsePage, + CredentialSchemaInnerDidUrlResponse, + CredentialSchemaInput, + CredentialSchemaResponse, + CredentialSchemaResponsePage, + FilterInput +} import org.hyperledger.identus.pollux.PrismEnvelopeResponse -import org.hyperledger.identus.pollux.credentialschema.http.{CredentialSchemaDidUrlResponse, CredentialSchemaDidUrlResponsePage, CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, FilterInput} import sttp.apispec.{ExternalDocumentation, Tag} import sttp.model.StatusCode import sttp.tapir.* diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala index b32e857aa1..38cf3f02b3 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -2,8 +2,16 @@ package org.hyperledger.identus.pollux.credentialschema.controller import org.hyperledger.identus.api.http.* import org.hyperledger.identus.api.http.model.{Order, Pagination} +import org.hyperledger.identus.pollux.credentialschema.http.{ + CredentialSchemaDidUrlResponse, + CredentialSchemaDidUrlResponsePage, + CredentialSchemaInnerDidUrlResponse, + CredentialSchemaInput, + CredentialSchemaResponse, + CredentialSchemaResponsePage, + FilterInput +} import org.hyperledger.identus.pollux.PrismEnvelopeResponse -import org.hyperledger.identus.pollux.credentialschema.http.{CredentialSchemaDidUrlResponse, CredentialSchemaDidUrlResponsePage, CredentialSchemaInnerDidUrlResponse, CredentialSchemaInput, CredentialSchemaResponse, CredentialSchemaResponsePage, FilterInput} import org.hyperledger.identus.shared.models.WalletAccessContext import zio.* import zio.json.ast.Json diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala index b812cc4a8e..e210b0bcce 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponse.scala @@ -1,9 +1,9 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.castor.core.model.did.{DIDUrl, PrismDID} -import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.shared.crypto.Sha256Hash import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import zio.json.* diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala index dc67a0d452..e18de3c60c 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaDidUrlResponsePage.scala @@ -1,8 +1,8 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.api.http.Annotation -import org.hyperledger.identus.pollux.PrismEnvelopeResponse import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaDidUrlResponsePage.annotations +import org.hyperledger.identus.pollux.PrismEnvelopeResponse import sttp.tapir.Schema import sttp.tapir.Schema.annotations.{description, encodedExample} import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaMultiTenancySpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaMultiTenancySpec.scala index 8737c4a1a5..58effc456f 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaMultiTenancySpec.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/pollux/schema/CredentialSchemaMultiTenancySpec.scala @@ -3,7 +3,7 @@ package org.hyperledger.identus.pollux.schema import com.dimafeng.testcontainers.PostgreSQLContainer import org.hyperledger.identus.agent.walletapi.model.Entity import org.hyperledger.identus.container.util.MigrationAspects.* -import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaGuidNotFoundError +import org.hyperledger.identus.pollux.core.model.error.{CredentialSchemaGuidNotFoundError, CredentialSchemaUpdateError} import org.hyperledger.identus.pollux.core.model.schema.`type`.CredentialJsonSchemaType import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema import org.hyperledger.identus.pollux.core.service.{CredentialSchemaService, CredentialSchemaServiceImpl} @@ -106,10 +106,10 @@ object CredentialSchemaMultiTenancySpec extends ZIOSpecDefault with CredentialSc .exit aliceCannotUpdateBobsVCSchema = assert(notFoundSchemaAError)( - fails(isSubtype[CredentialSchemaGuidNotFoundError](anything)) + fails(isSubtype[CredentialSchemaUpdateError](anything)) ) bobCannotUpdateAlicesVCSchema = assert(notFoundSchemaBError)( - fails(isSubtype[CredentialSchemaGuidNotFoundError](anything)) + fails(isSubtype[CredentialSchemaUpdateError](anything)) ) fetchedSchemaAbyB <- service.getByGUID(updatedSchemaA.guid).provideLayer(Bob.wacLayer) diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala index cd2bedb22e..29b4024d3d 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolver.scala @@ -9,7 +9,6 @@ import org.hyperledger.identus.shared.models.PrismEnvelopeData import org.hyperledger.identus.shared.models.StatusCode import zio.* import zio.json.* -import zio.json.ast.Json class DidUrlResolver(httpUrlResolver: HttpUrlResolver, didResolver: DidResolver) extends UriResolver { import DidUrlResolver.* @@ -61,7 +60,7 @@ class DidUrlResolver(httpUrlResolver: HttpUrlResolver, didResolver: DidResolver) .fromEither(result.fromJson[PrismEnvelopeData]) .mapError(_ => InvalidResponseFromResourceServer(finalUrl)) - envelopeAsStr = envelope.toString + envelopeAsStr = envelope.toJson validatedResult <- maybeResourceHash.fold(ZIO.succeed(envelopeAsStr)) { hash => val computedHash = Sha256Hash.compute(envelope.resource.getBytes()).hexEncoded diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala index 35ee78d368..4f72980cc1 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/uriResolvers/DidUrlResolverSpec.scala @@ -4,10 +4,11 @@ import io.circe.* import io.lemonlabs.uri.Url import org.hyperledger.identus.pollux.vc.jwt.* import org.hyperledger.identus.shared.crypto.Sha256Hash +import org.hyperledger.identus.shared.models.PrismEnvelopeData import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils} import zio.* -import zio.test.* import zio.json.* +import zio.test.* import zio.test.Assertion.* import java.time.Instant @@ -75,40 +76,28 @@ object DidUrlResolverSpec extends ZIOSpecDefault { private val schemaHash = Sha256Hash.compute(encodedSchema.getBytes()).hexEncoded - private val testDidUrl = Url .parse( s"did:prism:462c4811bf61d7de25b3baf86c5d2f0609b4debe53792d297bf612269bf8593a?resourceService=agent-base-url&resourcePath=schema-registry/schemas/did-url/ef3e4135-8fcf-3ce7-b5bb-df37defc13f6&resourceHash=$schemaHash" ) .toString - case class ResponseEnvelope(resource: String, schemaUrl: String) - object ResponseEnvelope { - given encoder: JsonEncoder[ResponseEnvelope] = - DeriveJsonEncoder.gen[ResponseEnvelope] - - given decoder: JsonDecoder[ResponseEnvelope] = - DeriveJsonDecoder.gen[ResponseEnvelope] - } - class MockHttpUrlResolver extends HttpUrlResolver(null) { // Mock implementation, always resolves some schema override def resolve(uri: String) = { - - val responseEnvelope = ResponseEnvelope( + val responseEnvelope = PrismEnvelopeData( resource = encodedSchema, - schemaUrl = uri + url = uri ) - ZIO.succeed(responseEnvelope.toJson) } } private val didResolverLayer = ZLayer.succeed(new DidResolver { - // mock implementation, alwasys resolves some DID + // mock implementation, always resolves the same DID override def resolve(didUrl: String) = ZIO.succeed( DIDResolutionSucceeded( DIDDocument( @@ -179,8 +168,6 @@ object DidUrlResolverSpec extends ZIOSpecDefault { }) private val httpUrlResolver = ZLayer.succeed(new MockHttpUrlResolver) - - override def spec = { suite("DidUrlResolverSpec")( test("Should resolve a DID url correctly") { @@ -189,11 +176,13 @@ object DidUrlResolverSpec extends ZIOSpecDefault { httpUrlResolver <- ZIO.service[HttpUrlResolver] didUrlResolver = new DidUrlResolver(httpUrlResolver, didResolver) response <- didUrlResolver.resolve(testDidUrl) - responseJson = Json.fromString(response) + responseEnvelope <- ZIO.fromEither(response.fromJson[PrismEnvelopeData]) } yield { - val schemaJson = Json.fromString(schema) - assert(responseJson.noSpaces)( - equalTo(schemaJson.noSpaces) + assert(responseEnvelope.url)( + equalTo(testDidUrl) + ) + assert(responseEnvelope.resource)( + equalTo(encodedSchema) ) } } diff --git a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala index 5bbdaa8bf4..61c3bf6acd 100644 --- a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala +++ b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialDefinitionSqlIntegrationSpec.scala @@ -206,10 +206,17 @@ object CredentialDefinitionSqlIntegrationSpec extends ZIOSpecDefault with Postgr credentialDefinitionCreated = assert(firstActual)(equalTo(firstExpected.get)) totalCount <- CredentialDefinitionSql.totalCount.transactWallet(tx) - lookupCount <- CredentialDefinitionSql.lookupCount().transactWallet(tx) + lookupCountHttpCredDef <- CredentialDefinitionSql + .lookupCount(resolutionMethod = ResourceResolutionMethod.HTTP) + .transactWallet(tx) + lookupCountDidCredDef <- CredentialDefinitionSql + .lookupCount(resolutionMethod = ResourceResolutionMethod.DID) + .transactWallet(tx) totalCountIsN = assert(totalCount)(equalTo(generatedCredentialDefinitions.length)) - lookupCountIsN = assert(lookupCount)(equalTo(generatedCredentialDefinitions.length)) + lookupCountIsN = assert(lookupCountHttpCredDef + lookupCountDidCredDef)( + equalTo(generatedCredentialDefinitions.length) + ) } yield allCredentialDefinitionsHaveUniqueId && allCredentialDefinitionsHaveUniqueConstraint && diff --git a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala index c18f446222..60df17a778 100644 --- a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala +++ b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/CredentialSchemaSqlIntegrationSpec.scala @@ -241,10 +241,15 @@ object CredentialSchemaSqlIntegrationSpec extends ZIOSpecDefault, PostgresTestCo schemaCreated = assert(firstActual)(equalTo(firstExpected.get)) totalCount <- CredentialSchemaSql.totalCount.transactWallet(tx) - lookupCount <- CredentialSchemaSql.lookupCount().transactWallet(tx) + lookupCountHttpSchemas <- CredentialSchemaSql + .lookupCount(resolutionMethod = ResourceResolutionMethod.HTTP) + .transactWallet(tx) + lookupCountDidSchemas <- CredentialSchemaSql + .lookupCount(resolutionMethod = ResourceResolutionMethod.DID) + .transactWallet(tx) totalCountIsN = assert(totalCount)(equalTo(generatedSchemas.length)) - lookupCountIsN = assert(lookupCount)(equalTo(generatedSchemas.length)) + lookupCountIsN = assert(lookupCountHttpSchemas + lookupCountDidSchemas)(equalTo(generatedSchemas.length)) } yield allSchemasHaveUniqueId && allSchemasHaveUniqueConstraint && From 607a3339cf1952493ed105668a7faf57794c575e Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Fri, 6 Sep 2024 15:31:21 +0400 Subject: [PATCH 11/12] Minor grammar fix Signed-off-by: Shota Jolbordi --- .../CredentialDefinitionRegistryEndpoints.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala index 3bc3821c95..bc6bf1d523 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala @@ -123,7 +123,7 @@ object CredentialDefinitionRegistryEndpoints { .name("createCredentialDefinitionDidUrl") .summary("Publish new definition to the definition registry, resolvable by DID url") .description( - "Create the new credential definition record with metadata and internal JSON Schema on behalf of Cloud Agent. " + + "Create the new credential definition record with metadata and internal JSON Schema on behalf of the Cloud Agent. " + "The credential definition will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it." ) .tag(tagName) From 6f280efc66ffe7ab03bafd1a3744a778fba6337e Mon Sep 17 00:00:00 2001 From: Shota Jolbordi Date: Mon, 9 Sep 2024 15:32:17 +0400 Subject: [PATCH 12/12] Fix major integration tests issues Signed-off-by: Shota Jolbordi --- .../controller/IssueControllerImpl.scala | 55 +++++++++++-------- ...redentialDefinitionRegistryEndpoints.scala | 2 +- .../http/CredentialSchemaResponse.scala | 14 +++++ 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index ba44e8b431..3bd225c7fd 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -17,7 +17,7 @@ import org.hyperledger.identus.issue.controller.http.{ IssueCredentialRecord, IssueCredentialRecordPage } -import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID} +import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID, ResourceResolutionMethod} import org.hyperledger.identus.pollux.core.model.CredentialFormat.{AnonCreds, JWT, SDJWT} import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.Role import org.hyperledger.identus.pollux.core.service.{CredentialDefinitionService, CredentialService} @@ -99,31 +99,38 @@ class IssueControllerImpl( credentialDefinition <- credentialDefinitionService.getByGUID(credentialDefinitionGUID) credentialDefinitionId <- { - // TODO: this needs to be either DID or HTTP url based on credentialDefinition.resolutionMethod - val publicEndpointServiceName = appConfig.agent.httpEndpoint.serviceName - val resourcePath = - s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition" + credentialDefinition.resolutionMethod match + case ResourceResolutionMethod.DID => + val publicEndpointServiceName = appConfig.agent.httpEndpoint.serviceName + val didUrlResourcePath = + s"credential-definition-registry/definitions/did-url/${credentialDefinitionGUID.toString}/definition" + val didUrl = for { + canonicalized <- JsonUtils.canonicalizeToJcs(credentialDefinition.definition.toJson) + encoded = Base64Utils.encodeURL(canonicalized.getBytes) + hash = Sha256Hash.compute(encoded.getBytes).hexEncoded + didUrl = DIDUrl( + issuingDID.did, + Seq(), + ListMap( + "resourceService" -> Seq(publicEndpointServiceName), + "resourcePath" -> Seq( + s"$didUrlResourcePath?resourceHash=$hash" + ), + ), + None + ).toString + } yield didUrl - val didUrl = for { - canonicalized <- JsonUtils.canonicalizeToJcs(credentialDefinition.definition.toJson) - encoded = Base64Utils.encodeURL(canonicalized.getBytes) - hash = Sha256Hash.compute(encoded.getBytes).hexEncoded - didUrl = DIDUrl( - issuingDID.did, - Seq(), - ListMap( - "resourceService" -> Seq(publicEndpointServiceName), - "resourcePath" -> Seq( - s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition?resourceHash=$hash" - ), - ), - None - ).toString - } yield didUrl + ZIO + .fromEither(didUrl) + .mapError(_ => ErrorResponse.badRequest(detail = Some("Could not parse credential definition"))) - ZIO - .fromEither(didUrl) - .mapError(_ => ErrorResponse.badRequest(detail = Some("Could not parse credential definition"))) + case ResourceResolutionMethod.HTTP => + val publicEndpointUrl = appConfig.agent.httpEndpoint.publicEndpointUrl.toExternalForm + val httpUrlSuffix = + s"credential-definition-registry/definitions/${credentialDefinitionGUID.toString}/definition" + val urlPrefix = if (publicEndpointUrl.endsWith("/")) publicEndpointUrl else publicEndpointUrl + "/" + ZIO.succeed(s"$urlPrefix$httpUrlSuffix") } record <- credentialService diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala index bc6bf1d523..4482efe4c5 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialdefinition/CredentialDefinitionRegistryEndpoints.scala @@ -207,7 +207,7 @@ object CredentialDefinitionRegistryEndpoints { endpoint.get .in(extractFromRequest[RequestContext](RequestContext.apply)) .in( - "credential-definition-registry" / "definitions" / path[UUID]("guid") / "definition".description( + "credential-definition-registry" / "definitions" / "did-url" / path[UUID]("guid") / "definition".description( "Globally unique identifier of the credential definition record" ) ) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponse.scala index ccb7e5f3cd..2c4fd9dcd6 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponse.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/pollux/credentialschema/http/CredentialSchemaResponse.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.pollux.credentialschema.http import org.hyperledger.identus.api.http.* import org.hyperledger.identus.pollux.core.model import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.core.model.ResourceResolutionMethod import org.hyperledger.identus.pollux.credentialschema.http.CredentialSchemaResponse.annotations import sttp.model.Uri import sttp.model.Uri.* @@ -52,6 +53,9 @@ case class CredentialSchemaResponse( @description(annotations.proof.description) @encodedExample(annotations.proof.example.toJson) proof: Option[Proof], + @description(annotations.resolutionMethod.description) + @encodedExample(annotations.resolutionMethod.example) + resolutionMethod: ResourceResolutionMethod, @description(annotations.kind.description) @encodedExample(annotations.kind.example) kind: String = "CredentialSchema", @@ -78,6 +82,7 @@ object CredentialSchemaResponse { schema = cs.schema, author = cs.author, authored = cs.authored, + resolutionMethod = cs.resolutionMethod, proof = None ) @@ -87,6 +92,8 @@ object CredentialSchemaResponse { DeriveJsonEncoder.gen[CredentialSchemaResponse] given decoder: zio.json.JsonDecoder[CredentialSchemaResponse] = DeriveJsonDecoder.gen[CredentialSchemaResponse] + + given resourceResolutionMethodSchema: Schema[ResourceResolutionMethod] = Schema.derived given schema: Schema[CredentialSchemaResponse] = Schema.derived object annotations { @@ -134,6 +141,13 @@ object CredentialSchemaResponse { description = "A string that identifies the type of resource being returned in the response.", example = "CredentialSchema" ) + + object resolutionMethod + extends Annotation[ResourceResolutionMethod]( + description = s"The method used to resolve the schema. It can be either HTTP or DID.", + example = ResourceResolutionMethod.HTTP + ) + object proof extends Annotation[Proof]( description = "A digital signature over the Credential Schema for the sake of asserting authorship. " +