diff --git a/prism-agent/service/api/http/pollux/schemas.yaml b/prism-agent/service/api/http/pollux/schemas.yaml index b446ef701d..243025625d 100644 --- a/prism-agent/service/api/http/pollux/schemas.yaml +++ b/prism-agent/service/api/http/pollux/schemas.yaml @@ -8,102 +8,333 @@ components: - verificationMethod - proofPurpose - proofValue + - jws type: object properties: type: type: string + description: + The type of cryptographic signature algorithm used to generate + the proof + example: Ed25519Signature2018 created: type: string + description: + The date and time at which the proof was created, in UTC format. This + field is used to ensure that the proof was generated before or at the + same time as the credential schema itself format: date-time + example: "2022-03-10T12:00:00Z" verificationMethod: type: string + description: The verification method used to generate the proof. This + is usually a DID and key ID combination that can be used to look up the + public key needed to verify the proof. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 proofPurpose: type: string + description: + "The purpose of the proof (for example: `assertionMethod`). This + indicates that the proof is being used to assert that the issuer really + issued this credential schema instance" + example: assertionMethod proofValue: type: string + description: + The cryptographic signature value that was generated using + the private key associated with the verification method, and which can + be used to verify the proof + example: FiPfjknHikKmZ... + jws: + type: string + description: The JSON Web Signature (JWS) that contains the proof information + example: eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik... domain: type: string + description: + It specifies the domain context within which the credential + schema and proof are being used + example: prims.atala.com + description: + A digital signature over the Credential Schema for the sake of + asserting authorship. A piece of Metadata. + example: + type: Ed25519Signature2018 + created: "2022-03-10T12:00:00Z" + verificationMethod: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 + proofPurpose: assertionMethod + proofValue: FiPfjknHikKmZ... + jws: eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik... + domain: prims.atala.com - VerifiableCredentialSchema: + CredentialSchemaInput: required: - - id - name - version - - author - - authored + - type + - schema type: object properties: - id: - type: string - format: uuid name: type: string + description: + A human-readable name for the credential schema. A piece of + Metadata. + example: DrivingLicense version: type: string - tags: - type: array - items: - type: string + description: + Denotes the revision of a given Credential Schema. It should + follow semantic version convention to describe the impact of the schema + evolution + example: 1.0.0 + pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ description: type: string - attributes: + description: A human-readable description of the credential schema + example: + Simple credential schema for the driving licence verifiable credential. + This field is not a part of W3C specification + type: + type: string + description: + This field resolves to a JSON schema with details about the + schema metadata that applies to the schema. A piece of Metadata. + example: https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json + schema: + type: object + description: |- + Valid JSON Schema where the Credential Schema data fields are defined + A piece of Metadata + example: + example: + $id: driving-license-1.0 + $schema: https://json-schema.org/draft/2020-12/schema + description: Driving License + type: object + properties: + credentialSubject: + type: object + properties: + emailAddress: + type: string + format: email + givenName: + type: string + familyName: + type: string + dateOfIssuance: + type: datetime + drivingLicenseID: + type: string + drivingClass: + type: integer + required: + - emailAddress + - familyName + - dateOfIssuance + - drivingLicenseID + - drivingClass + additionalProperties: true + tags: type: array items: type: string - author: - type: string - authored: - type: string - format: date-time - proof: - $ref: "#/components/schemas/Proof" - - VerifiableCredentialSchemaPage: - allOf: - - $ref: "../shared/schemas.yaml#/components/schemas/Pagination" - - type: object - required: [contents] - properties: - contents: - type: array - items: - $ref: "#/components/schemas/VerifiableCredentialSchema" + description: + Tokens that allow to lookup and filter the credential schema + records. This field is not a part of W3C specification + example: + - driving + - licence + - id - VerificationPolicyConstraint: + CredentialSchemaResponsePage: required: - - schemaId + - kind + - self + - pageOf type: object properties: - schemaId: - type: string - trustedIssuers: + contents: type: array items: - type: string + $ref: "#/components/schemas/CredentialSchemaResponse" + description: + A sequence of CredentialSchemaResponse objects representing + the list of credential schemas that the API response contains + kind: + type: string + description: + A string field indicating the type of the API response. In + this case, it will always be set to `CredentialSchemaPage` + example: CredentialSchemaPage + self: + type: string + description: A string field containing the URL of the current API endpoint + example: /prism-agent/schema-registry/schemas?skip=10&limit=10 + pageOf: + type: string + description: + A string field indicating the type of resource that the contents + field contains + example: /prism-agent/schema-registry/schemas + next: + type: 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: /prism-agent/schema-registry/schemas?skip=20&limit=10 + previous: + type: 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: /prism-agent/schema-registry/schemas?skip=0&limit=10 - VerificationCredentialSchemaInput: + CredentialSchemaResponse: required: + - guid + - id - name - version + - description + - type + - schema + - author + - authored + - kind + - self type: object properties: - id: + guid: type: string + description: | + Globally unique id of the credential schema. It's composed + from the bytes of the string that contain the `authored`, `name`, and + `version` values. The string format looks like the resource identifier: `author`/`id`?version=`version` format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + id: + type: string + description: | + A locally unique identifier to address the schema. UUID is + generated by the backend + example: 0527aea1-d131-3948-a34d-03af39aba8b5 + longId: + type: string + description: + Resource id of the credential schema. Contains the `author`'s + DID, `id` and `version` fields. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff/0527aea1-d131-3948-a34d-03af39aba8b4?version=1.0.0 name: type: string + description: + A human-readable name for the credential schema. A piece of + Metadata. + example: DrivingLicense version: type: string - description: - type: string - attributes: + description: + Denotes the revision of a given Credential Schema. It should + follow semantic version convention to describe the impact of the schema + evolution + example: 1.0.0 + tags: type: array items: type: string + description: + Tokens that allow to lookup and filter the credential schema + records. This field is not a part of W3C specification + example: + - driving + - licence + - id + description: + type: string + description: A human-readable description of the credential schema + example: + Simple credential schema for the driving licence verifiable credential. + This field is not a part of W3C specification + type: + type: string + description: + This field resolves to a JSON schema with details about the + schema metadata that applies to the schema. A piece of Metadata. + example: https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json + schema: + type: object + description: |- + Valid JSON Schema where the Credential Schema data fields are defined + A piece of Metadata + example: + example: + $id: driving-license-1.0 + $schema: https://json-schema.org/draft/2020-12/schema + description: Driving License + type: object + properties: + credentialSubject: + type: object + properties: + emailAddress: + type: string + format: email + givenName: + type: string + familyName: + type: string + dateOfIssuance: + type: datetime + drivingLicenseID: + type: string + drivingClass: + type: integer + required: + - emailAddress + - familyName + - dateOfIssuance + - drivingLicenseID + - drivingClass + additionalProperties: true + author: + type: string + description: + DID of the identity which authored the credential schema. A + piece of Metadata. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff authored: type: string + description: + "[RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date on + which the credential schema was created. A piece of Metadata." format: date-time - tags: + example: "2022-03-10T12:00:00Z" + proof: + $ref: "#/components/schemas/Proof" + kind: + type: string + description: + A string that identifies the type of resource being returned + in the response + example: CredentialSchema + self: + type: string + description: + The URL that uniquely identifies the resource being returned + in the response + example: /prism-agent/schema-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 + + VerificationPolicyConstraint: + required: + - schemaId + type: object + properties: + schemaId: + type: string + trustedIssuers: type: array items: type: string diff --git a/prism-agent/service/api/http/prism-agent-openapi-spec.yaml b/prism-agent/service/api/http/prism-agent-openapi-spec.yaml index 212150f923..c4948d72b6 100644 --- a/prism-agent/service/api/http/prism-agent-openapi-spec.yaml +++ b/prism-agent/service/api/http/prism-agent-openapi-spec.yaml @@ -207,29 +207,50 @@ paths: # ---------------------------------- /schema-registry/schemas: get: - operationId: lookupSchemasByQuery tags: - Schema Registry summary: Lookup schemas by indexed fields - description: "Lookup schemas by `author`, `name`, `tags` parameters" + description: + "Lookup schemas by `author`, `name`, `tags` parameters and control + the pagination by `offset` and `limit` parameters" + operationId: lookupSchemasByQuery parameters: - - $ref: "./shared/parameters.yaml#/components/parameters/offset" - - $ref: "./shared/parameters.yaml#/components/parameters/limit" - name: author in: query required: false schema: type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff - name: name in: query required: false schema: type: string + example: DrivingLicense + - name: version + in: query + required: false + schema: + type: string + example: 1.0.0 - name: tags in: query required: false schema: type: string + example: driving + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 - name: order in: query required: false @@ -237,72 +258,73 @@ paths: type: string responses: "200": - description: "List of all schemas managed by the Agent" + description: Collection of CredentialSchema records. content: application/json: schema: - $ref: "./pollux/schemas.yaml#/components/schemas/VerifiableCredentialSchemaPage" + $ref: "./pollux/schemas.yaml#/components/schemas/CredentialSchemaResponsePage" "400": $ref: "./shared/responses.yaml#/components/responses/BadRequest" "500": $ref: "./shared/responses.yaml#/components/responses/InternalServerError" + post: - operationId: createSchema tags: - Schema Registry summary: Publish new schema to the schema registry description: - Publish the new schema with attributes to the schema registry on - behalf of Cloud Agent. Schema will be signed by the keys of Cloud Agent and - issued by the DID that corresponds to it + 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 + operationId: createSchema requestBody: - description: Create schema input object with the metadata and attributes + description: JSON object required for the credential schema creation content: application/json: schema: - $ref: ./pollux/schemas.yaml#/components/schemas/VerificationCredentialSchemaInput + $ref: "./pollux/schemas.yaml#/components/schemas/CredentialSchemaInput" required: true responses: "201": - description: "Schema successfully created" + description: "The new credential schema record is successfully created" content: application/json: schema: - $ref: ./pollux/schemas.yaml#/components/schemas/VerifiableCredentialSchema + $ref: "./pollux/schemas.yaml#/components/schemas/CredentialSchemaResponse" "400": $ref: "./shared/responses.yaml#/components/responses/BadRequest" "500": $ref: "./shared/responses.yaml#/components/responses/InternalServerError" - /schema-registry/schemas/{id}: + + /schema-registry/schemas/{guid}: get: - operationId: getSchemaById tags: - Schema Registry - summary: Fetch the schema from the registry by id - description: - Fetch the schema by the unique identifier. Verifiable Credential - Schema in json format is returned. + summary: Fetch the schema from the registry by `guid` + description: Fetch the credential schema by the unique identifier + operationId: getSchemaById parameters: - - name: id + - name: guid in: path - description: Get the schema by id + description: Globally unique identifier of the credential schema record required: true schema: type: string format: uuid responses: "200": - description: "Verifiable credential Schema" + description: CredentialSchema found by `guid` content: application/json: schema: - $ref: ./pollux/schemas.yaml#/components/schemas/VerifiableCredentialSchema + $ref: "./pollux/schemas.yaml#/components/schemas/CredentialSchemaResponse" "404": $ref: "./shared/responses.yaml#/components/responses/NotFound" "400": $ref: "./shared/responses.yaml#/components/responses/BadRequest" "500": $ref: "./shared/responses.yaml#/components/responses/InternalServerError" + # ---------------------------------- # Verification Policies # ---------------------------------- diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala index f799d98bdf..b796f479e9 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala @@ -7,13 +7,17 @@ import sttp.tapir.json.zio.jsonBody object EndpointOutputs { val basicFailures: EndpointOutput[FailureResponse] = oneOf( - oneOfVariant(statusCode(StatusCode.BadRequest).and(jsonBody[BadRequest])), - oneOfVariant(statusCode(StatusCode.InternalServerError).and(jsonBody[InternalServerError])) + oneOfVariant(statusCode(StatusCode.BadRequest).and(jsonBody[BadRequest].description("Bad request"))), + oneOfVariant( + statusCode(StatusCode.InternalServerError).and(jsonBody[InternalServerError].description("Internal server error")) + ) ) val basicFailuresAndNotFound = oneOf( - oneOfVariant(statusCode(StatusCode.NotFound).and(jsonBody[NotFound])), - oneOfVariant(statusCode(StatusCode.BadRequest).and(jsonBody[BadRequest])), - oneOfVariant(statusCode(StatusCode.InternalServerError).and(jsonBody[InternalServerError])) + oneOfVariant(statusCode(StatusCode.NotFound).and(jsonBody[NotFound].description("Entity not found"))), + oneOfVariant(statusCode(StatusCode.BadRequest).and(jsonBody[BadRequest].description("Bad request"))), + oneOfVariant( + statusCode(StatusCode.InternalServerError).and(jsonBody[InternalServerError].description("Internal server error")) + ) ) } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/model/PaginationInput.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/model/PaginationInput.scala index 550e33c20f..38257a2462 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/model/PaginationInput.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/model/PaginationInput.scala @@ -1,6 +1,9 @@ package io.iohk.atala.api.http.model import sttp.tapir.Codec.PlainCodec +import sttp.tapir.EndpointIO.annotations.{description, query} +import sttp.tapir.Schema.annotations.{encodedExample, validateEach} +import sttp.tapir.Validator import sttp.tapir.generic.auto.* import sttp.tapir.{Codec, DecodeResult, Schema} import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder} @@ -8,9 +11,15 @@ import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder} import java.time.ZonedDateTime import java.util.{Base64, UUID} import scala.util.Try +import io.iohk.atala.api.http.Annotation +import io.iohk.atala.api.http.model.Pagination.annotations case class PaginationInput( + @query + @validateEach(Validator.positiveOrZero[Int]) offset: Option[Int] = None, + @query + @validateEach(Validator.positive[Int]) limit: Option[Int] = None ) { def toPagination = Pagination.apply(this) @@ -22,6 +31,21 @@ case class Pagination(offset: Int, limit: Int) { } object Pagination { + + object annotations { + object offset + extends Annotation[Int]( + description = "The number of items to skip before returning results. Default is 0 if not specified", + example = 0 + ) + + object limit + extends Annotation[Int]( + description = "The maximum number of items to return. Defaults to 100 if not specified.", + example = 100 + ) + } + def apply(in: PaginationInput): Pagination = Pagination(in.offset.getOrElse(0), in.limit.getOrElse(100)) } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/package.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/package.scala new file mode 100644 index 0000000000..e7538422f3 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/package.scala @@ -0,0 +1,10 @@ +package io.iohk.atala.api + +package object http { + case class Annotation[E](description: String, example: E) + + val DIDRefRegex = """^did:(?[a-z0-9]+(:[a-z0-9]+)*)\:(?[^#?]*)$""" + val DIDRegex = """^did:(?[a-z0-9]+(:[a-z0-9]+)*)\:(?[^#?]*)?(?\?[^#]*)?(?\#.*)?$""" + val SemVerRegex = + """^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$""" +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryEndpoints.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryEndpoints.scala index 08d3b32465..ce10a9d836 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryEndpoints.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryEndpoints.scala @@ -4,9 +4,10 @@ import io.iohk.atala.api.http.* import io.iohk.atala.api.http.EndpointOutputs.* import io.iohk.atala.api.http.codec.OrderCodec.* import io.iohk.atala.api.http.model.{Order, PaginationInput} +import io.iohk.atala.api.http.* import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse, FilterInput } @@ -16,6 +17,7 @@ import sttp.tapir.json.zio.jsonBody import sttp.tapir.{ Endpoint, EndpointInfo, + EndpointInput, PublicEndpoint, endpoint, extractFromRequest, @@ -45,16 +47,23 @@ object SchemaRegistryEndpoints { .in( jsonBody[CredentialSchemaInput] .description( - "Create schema input object with the metadata and attributes" + "JSON object required for the credential schema creation" + ) + ) + .out( + statusCode(StatusCode.Created) + .description( + "The new credential schema record is successfully created" ) ) - .out(statusCode(StatusCode.Created)) .out(jsonBody[CredentialSchemaResponse]) + .description("Credential schema record") .errorOut(basicFailures) .name("createSchema") .summary("Publish new schema to the schema registry") .description( - "Publish the new schema with attributes to the schema registry on behalf of Cloud Agent. Schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it" + "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("Schema Registry") @@ -68,10 +77,10 @@ object SchemaRegistryEndpoints { .in(extractFromRequest[RequestContext](RequestContext.apply)) .in( "schema-registry" / "schemas" / path[UUID]("guid").description( - "Globally unique identifier of the credential schema object" + "Globally unique identifier of the credential schema record" ) ) - .out(jsonBody[CredentialSchemaResponse]) + .out(jsonBody[CredentialSchemaResponse].description("CredentialSchema found by `guid`")) .errorOut(basicFailuresAndNotFound) .name("getSchemaById") .summary("Fetch the schema from the registry by `guid`") @@ -80,6 +89,8 @@ object SchemaRegistryEndpoints { ) .tag("Schema Registry") + private val credentialSchemaFilterInput: EndpointInput[FilterInput] = EndpointInput.derived[FilterInput] + private val paginationInput: EndpointInput[PaginationInput] = EndpointInput.derived[PaginationInput] val lookupSchemasByQueryEndpoint: PublicEndpoint[ ( RequestContext, @@ -88,32 +99,16 @@ object SchemaRegistryEndpoints { Option[Order] ), FailureResponse, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, Any ] = endpoint.get .in(extractFromRequest[RequestContext](RequestContext.apply)) .in("schema-registry" / "schemas".description("Lookup schemas by query")) - .in( - query[Option[String]]("author") - .and( - query[Option[String]]("name") - .and( - query[Option[String]]("version") - .and( - query[Option[String]]("tags") - ) - ) - ) - .mapTo[FilterInput] - ) - .in( - query[Option[Int]]("offset") - .and(query[Option[Int]]("limit")) - .mapTo[PaginationInput] - ) + .in(credentialSchemaFilterInput) + .in(paginationInput) .in(query[Option[Order]]("order")) - .out(jsonBody[CredentialSchemaPageResponse]) + .out(jsonBody[CredentialSchemaResponsePage].description("Collection of CredentialSchema records.")) .errorOut(basicFailures) .name("lookupSchemasByQuery") .summary("Lookup schemas by indexed fields") diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryServerEndpoints.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryServerEndpoints.scala index 97ad164a1c..9e9f910516 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryServerEndpoints.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/SchemaRegistryServerEndpoints.scala @@ -11,7 +11,7 @@ import io.iohk.atala.pollux.credentialschema.SchemaRegistryEndpoints.{ import io.iohk.atala.pollux.credentialschema.controller.{CredentialSchemaController, CredentialSchemaControllerLogic} import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse, FilterInput } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaController.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaController.scala index af6a2f8b9f..33148d018f 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaController.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaController.scala @@ -7,7 +7,7 @@ import io.iohk.atala.pollux.core.service.CredentialSchemaService import io.iohk.atala.pollux.core.service.CredentialSchemaService.Error.* import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse, FilterInput } @@ -33,7 +33,7 @@ trait CredentialSchemaController { order: Option[Order] )(implicit rc: RequestContext - ): IO[FailureResponse, CredentialSchemaPageResponse] + ): IO[FailureResponse, CredentialSchemaResponsePage] } object CredentialSchemaController { diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala index bf9bd82d7c..8a8d29934e 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerImpl.scala @@ -10,7 +10,7 @@ import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaInput.toDomain import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaResponse.fromDomain import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse, FilterInput } @@ -57,7 +57,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService) extends C order: Option[Order] )(implicit rc: RequestContext - ): IO[FailureResponse, CredentialSchemaPageResponse] = { + ): IO[FailureResponse, CredentialSchemaResponsePage] = { for { filteredEntries: FilteredEntries <- service.lookup( filter.toDomain, @@ -67,7 +67,7 @@ class CredentialSchemaControllerImpl(service: CredentialSchemaService) extends C entries = filteredEntries.entries .map(fromDomain(_).withBaseUri(rc.request.uri)) .toList - page = CredentialSchemaPageResponse(entries) + page = CredentialSchemaResponsePage(entries) stats = CollectionStats(filteredEntries.totalCount, filteredEntries.count) } yield CredentialSchemaControllerLogic(rc, pagination, page, stats).result } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala index 5069d2443c..15d6e8eb50 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/controller/CredentialSchemaControllerLogic.scala @@ -2,7 +2,7 @@ package io.iohk.atala.pollux.credentialschema.controller import io.iohk.atala.api.http.RequestContext import io.iohk.atala.api.http.model.{CollectionStats, Pagination} -import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaPageResponse +import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaResponsePage import sttp.model.Uri import sttp.model.Uri.QuerySegment import sttp.model.Uri.QuerySegment.KeyValue @@ -13,7 +13,7 @@ import io.iohk.atala.api.util.PaginationUtils case class CredentialSchemaControllerLogic( ctx: RequestContext, pagination: Pagination, - page: CredentialSchemaPageResponse, + page: CredentialSchemaResponsePage, stats: CollectionStats ) { @@ -23,7 +23,7 @@ case class CredentialSchemaControllerLogic( def composePreviousUri(uri: Uri): Option[Uri] = PaginationUtils.composePreviousUri(uri, page.contents, pagination, stats) - def result: CredentialSchemaPageResponse = { + 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) diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaInput.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaInput.scala new file mode 100644 index 0000000000..fe7248c127 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaInput.scala @@ -0,0 +1,53 @@ +package io.iohk.atala.pollux.credentialschema.http + +import io.iohk.atala.pollux.core.model.CredentialSchema.Input +import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaResponse.annotations +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample, validate, validateEach} +import sttp.tapir.json.zio.schemaForZioJsonValue +import zio.json.* +import zio.json.ast.Json +import sttp.tapir.Validator.* +import io.iohk.atala.api.http.* + +case class CredentialSchemaInput( + @description(annotations.name.description) + @encodedExample(annotations.name.example) + @validate(nonEmptyString) + name: String, + @description(annotations.version.description) + @encodedExample(annotations.version.example) + @validate(pattern(SemVerRegex)) + version: String, + @description(annotations.description.description) + @encodedExample(annotations.description.example) + @validateEach(nonEmptyString) + description: Option[String], + @description(annotations.`type`.description) + @encodedExample(annotations.`type`.example) + `type`: String, + @description(annotations.schema.description) + @encodedExample(annotations.schema.example.toJson) + schema: zio.json.ast.Json, + @description(annotations.tags.description) + @encodedExample(annotations.tags.example) + tags: Seq[String], +) +object CredentialSchemaInput { + def toDomain(in: CredentialSchemaInput): Input = + Input( + name = in.name, + version = in.version, + tags = in.tags, + description = in.description.getOrElse(""), + `type` = in.`type`, + schema = in.schema, + author = "did:prism:agent", + authored = None + ) + given encoder: JsonEncoder[CredentialSchemaInput] = + DeriveJsonEncoder.gen[CredentialSchemaInput] + given decoder: JsonDecoder[CredentialSchemaInput] = + DeriveJsonDecoder.gen[CredentialSchemaInput] + given schema: Schema[CredentialSchemaInput] = Schema.derived +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponse.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponse.scala index bb9bbabf21..705877fd64 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponse.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponse.scala @@ -2,31 +2,65 @@ package io.iohk.atala.pollux.credentialschema.http import io.iohk.atala.pollux.core.model import io.iohk.atala.pollux.core.model.CredentialSchema.Input +import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaInput +import io.iohk.atala.pollux.credentialschema.http.Proof import sttp.model.Uri import sttp.model.Uri.* +import sttp.tapir.EndpointIO.annotations.{example, query} import sttp.tapir.Schema -import sttp.tapir.Schema.annotations.{description, encodedName} +import sttp.tapir.Schema.annotations.{default, description, encodedExample, encodedName} import sttp.tapir.json.zio.schemaForZioJsonValue import zio.json.ast.Json +import zio.json.* import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder} +import io.iohk.atala.api.http.* import java.time.{OffsetDateTime, ZoneOffset} import java.util.UUID +import CredentialSchemaResponse.annotations case class CredentialSchemaResponse( + @description(annotations.guid.description) + @encodedExample(annotations.guid.example) guid: UUID, + @description(annotations.id.description) + @encodedExample(annotations.id.example) id: String, + @description(annotations.longId.description) + @encodedExample(annotations.longId.example) longId: Option[String], + @description(annotations.name.description) + @encodedExample(annotations.name.example) name: String, + @description(annotations.version.description) + @encodedExample(annotations.version.example) version: String, + @description(annotations.tags.description) + @encodedExample(annotations.tags.example) tags: Seq[String], + @description(annotations.description.description) + @encodedExample(annotations.description.example) description: String, + @description(annotations.`type`.description) + @encodedExample(annotations.`type`.example) `type`: String, + @description(annotations.schema.description) + @encodedExample(annotations.schema.example.toJson) schema: Json, + @description(annotations.author.description) + @encodedExample(annotations.author.example) author: String, + @description(annotations.authored.description) + @encodedExample(annotations.authored.example) authored: OffsetDateTime, + @description(annotations.proof.description) + @encodedExample(annotations.proof.example.toJson) proof: Option[Proof], + @description(annotations.kind.description) + @encodedExample(annotations.kind.example) kind: String = "CredentialSchema", + @description(annotations.self.description) + @encodedExample(annotations.self.example) self: String = "" ) { def withBaseUri(base: Uri) = withSelf(base.addPath(guid.toString).toString) @@ -58,60 +92,138 @@ object CredentialSchemaResponse { given decoder: zio.json.JsonDecoder[CredentialSchemaResponse] = DeriveJsonDecoder.gen[CredentialSchemaResponse] given schema: Schema[CredentialSchemaResponse] = Schema.derived -} -case class FilterInput( - author: Option[String] = None, - name: Option[String] = None, - version: Option[String] = None, - tags: Option[String] = None -) { - def toDomain = model.CredentialSchema.Filter(author, name, version, tags) -} + object annotations { + object guid + extends Annotation[UUID]( + description = "Globally unique id of the credential schema." + + "It's composed from the bytes of the string that contain the `author`, `name`, and `version` values." + + "The string format looks like the resource identifier: " + + "`author`/`id`?version=`version`", + example = UUID.fromString("0527aea1-d131-3948-a34d-03af39aba8b4") + ) -case class CredentialSchemaInput( - name: String, - version: String, - description: Option[String], - `type`: String, - schema: zio.json.ast.Json, - tags: Seq[String] -) - -object CredentialSchemaInput { - def toDomain(in: CredentialSchemaInput): Input = - Input( - name = in.name, - version = in.version, - tags = in.tags, - description = in.description.getOrElse(""), - `type` = in.`type`, - schema = in.schema, - author = "did:prism:agent", - authored = None - ) - given encoder: zio.json.JsonEncoder[CredentialSchemaInput] = - DeriveJsonEncoder.gen[CredentialSchemaInput] - given decoder: zio.json.JsonDecoder[CredentialSchemaInput] = - DeriveJsonDecoder.gen[CredentialSchemaInput] - given schema: Schema[CredentialSchemaInput] = Schema.derived -} + object id + extends Annotation[UUID]( + description = "A locally unique identifier to address the schema. UUID is generated by the backend.", + example = UUID.fromString("0527aea1-d131-3948-a34d-03af39aba8b5") + ) + object schema + extends Annotation[Json]( + description = "Valid JSON Schema where the Credential Schema data fields are defined. " + + "A piece of Metadata", + example = DrivingLicenseSchemaExample.fromJson[Json].toOption.getOrElse(Json.Null) + ) -case class CredentialSchemaPageResponse( - contents: List[CredentialSchemaResponse], - kind: String = "CredentialSchemaPage", - self: String = "", - pageOf: String = "", - next: Option[String] = None, - previous: Option[String] = None -) { - def withSelf(self: String) = copy(self = self) -} + object `type` + extends Annotation[String]( + description = + "This field resolves to a JSON schema with details about the schema metadata that applies to the schema. " + + "A piece of Metadata.", + example = "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json" + ) + object longId + extends Annotation[String]( + description = "Resource id of the credential schema. Contains the `author`'s DID, `id` and `version` fields.", + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + + "/0527aea1-d131-3948-a34d-03af39aba8b4?version=1.0.0" + ) + object self + extends Annotation[String]( + description = "The URL that uniquely identifies the resource being returned in the response.", + example = "/prism-agent/schema-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4" + ) + object kind + extends Annotation[String]( + description = "A string that identifies the type of resource being returned in the response.", + example = "CredentialSchema" + ) + object proof + extends Annotation[Proof]( + description = "A digital signature over the Credential Schema for the sake of asserting authorship. " + + "A piece of Metadata.", + example = Proof.Example + ) + + object name + extends Annotation[String]( + description = "A human-readable name for the credential schema. A piece of Metadata.", + example = "DrivingLicense" + ) + + object version + extends Annotation[String]( + description = "Denotes the revision of a given Credential Schema. " + + "It should follow semantic version convention to describe the impact of the schema evolution.", + example = "1.0.0" + ) + object tags + extends Annotation[Seq[String]]( + description = "Tokens that allow to lookup and filter the credential schema records.", + example = Seq("driving", "licence", "id") + ) + + object description + extends Annotation[String]( + description = "A human-readable description of the credential schema", + example = "Simple credential schema for the driving licence verifiable credential." + ) + + object author + extends Annotation[String]( + description = "DID of the identity which authored the credential schema. " + + "A piece of Metadata.", + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + ) + + object authored + extends Annotation[OffsetDateTime]( + description = + "[RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date on which the credential schema was created. " + + "A piece of Metadata.", + example = OffsetDateTime.parse("2022-03-10T12:00:00Z") + ) -object CredentialSchemaPageResponse { - given encoder: zio.json.JsonEncoder[CredentialSchemaPageResponse] = - DeriveJsonEncoder.gen[CredentialSchemaPageResponse] - given decoder: zio.json.JsonDecoder[CredentialSchemaPageResponse] = - DeriveJsonDecoder.gen[CredentialSchemaPageResponse] - given schema: sttp.tapir.Schema[CredentialSchemaPageResponse] = Schema.derived + val DrivingLicenseSchemaExample = + """{ + | "$id": "driving-license-1.0", + | "$schema": "https://json-schema.org/draft/2020-12/schema", + | "description": "Driving License", + | "type": "object", + | "properties": { + | "credentialSubject": { + | "type": "object", + | "properties": { + | "emailAddress": { + | "type": "string", + | "format": "email" + | }, + | "givenName": { + | "type": "string" + | }, + | "familyName": { + | "type": "string" + | }, + | "dateOfIssuance": { + | "type": "datetime" + | }, + | "drivingLicenseID": { + | "type": "string" + | }, + | "drivingClass": { + | "type": "integer" + | }, + | "required": [ + | "emailAddress", + | "familyName", + | "dateOfIssuance", + | "drivingLicenseID", + | "drivingClass" + | ], + | "additionalProperties": true + | } + | } + | } + |}""".stripMargin + } } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponsePage.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponsePage.scala new file mode 100644 index 0000000000..37e1368578 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/CredentialSchemaResponsePage.scala @@ -0,0 +1,90 @@ +package io.iohk.atala.pollux.credentialschema.http + +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} +import io.iohk.atala.pollux.credentialschema.http.CredentialSchemaResponsePage.annotations +import io.iohk.atala.api.http.Annotation + +case class CredentialSchemaResponsePage( + @description(annotations.contents.description) + @encodedExample(annotations.contents.example) + contents: Seq[CredentialSchemaResponse], + @description(annotations.kind.description) + @encodedExample(annotations.kind.example) + kind: String = "CredentialSchemaPage", + @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 CredentialSchemaResponsePage { + given encoder: JsonEncoder[CredentialSchemaResponsePage] = + DeriveJsonEncoder.gen[CredentialSchemaResponsePage] + given decoder: JsonDecoder[CredentialSchemaResponsePage] = + DeriveJsonDecoder.gen[CredentialSchemaResponsePage] + given schema: Schema[CredentialSchemaResponsePage] = Schema.derived + + val Example = CredentialSchemaResponsePage( + 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[CredentialSchemaResponse]]( + description = + "A sequence of CredentialSchemaResponse objects representing the list of credential schemas 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 `CredentialSchemaPage`", + example = "CredentialSchemaPage" + ) + + object self + extends Annotation[String]( + description = "A string field containing the URL of the current API endpoint", + example = "/prism-agent/schema-registry/schemas?skip=10&limit=10" + ) + + object pageOf + extends Annotation[String]( + description = "A string field indicating the type of resource that the contents field contains", + example = "/prism-agent/schema-registry/schemas" + ) + + 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 = "/prism-agent/schema-registry/schemas?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 = "/prism-agent/schema-registry/schemas?skip=0&limit=10" + ) + } +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/FilterInput.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/FilterInput.scala new file mode 100644 index 0000000000..ebc81e6e31 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/FilterInput.scala @@ -0,0 +1,58 @@ +package io.iohk.atala.pollux.credentialschema.http + +import io.iohk.atala.api.http.* +import io.iohk.atala.pollux.core.model +import sttp.tapir.EndpointIO.annotations.{example, query} +import io.iohk.atala.pollux.credentialschema.http.FilterInput.annotations +import sttp.tapir.Schema.annotations.validateEach +import sttp.tapir.Validator.* +import io.iohk.atala.api.http.* + +case class FilterInput( + @query + @example(Option(annotations.author.example)) + author: Option[String] = Option.empty[String], + @query + @example(Option(annotations.name.example)) + name: Option[String] = Option.empty[String], + @query + @example(Option(annotations.version.example)) + version: Option[String] = Option.empty[String], + @query + @example(annotations.tags.example.headOption) + tags: Option[String] = Option.empty[String] +) { + def toDomain = model.CredentialSchema.Filter(author, name, version, tags) +} + +object FilterInput { + // TODO: for some reason @description attribute doesn't work together with @query in tapir + // need to invest more time and probably report the issue + object annotations { + + object author + extends Annotation[String]( + description = + "An optional field that can be used to filter the credential schema collection by `author`'s DID", + example = CredentialSchemaResponse.annotations.author.example + ) + + object name + extends Annotation[String]( + description = "An optional field that can be used to filter the credential schema records by `name`", + example = CredentialSchemaResponse.annotations.name.example + ) + + object version + extends Annotation[String]( + description = "An optional string field that can be used to filter resources by version", + example = CredentialSchemaResponse.annotations.version.example + ) + + object tags + extends Annotation[Seq[String]]( + description = "An optional string field that can be used to filter resources by tags", + example = CredentialSchemaResponse.annotations.tags.example + ) + } +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/Proof.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/Proof.scala index d1df194e2e..7e1288ff1b 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/Proof.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/pollux/credentialschema/http/Proof.scala @@ -1,22 +1,100 @@ package io.iohk.atala.pollux.credentialschema.http +import sttp.tapir.EndpointIO.annotations.example import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} import sttp.tapir.generic.auto.* -import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} +import io.iohk.atala.pollux.credentialschema.http.Proof.annotations +import io.iohk.atala.api.http.* -import java.time.ZonedDateTime +import java.time.{OffsetDateTime, ZonedDateTime} case class Proof( + @description(annotations.`type`.description) + @encodedExample(annotations.`type`.example) `type`: String, - created: ZonedDateTime, + @description(annotations.created.description) + @encodedExample(annotations.created.example) + created: OffsetDateTime, + @description(annotations.verificationMethod.description) + @encodedExample(annotations.verificationMethod.example) verificationMethod: String, + @description(annotations.proofPurpose.description) + @encodedExample(annotations.proofPurpose.example) proofPurpose: String, + @description(annotations.proofValue.description) + @encodedExample(annotations.proofValue.example) proofValue: String, + @description(annotations.jws.description) + @encodedExample(annotations.jws.example) + jws: String, + @description(annotations.domain.description) + @encodedExample(annotations.domain.example) domain: Option[String] ) object Proof { - given encoder: zio.json.JsonEncoder[Proof] = DeriveJsonEncoder.gen[Proof] - given decoder: zio.json.JsonDecoder[Proof] = DeriveJsonDecoder.gen[Proof] + given encoder: JsonEncoder[Proof] = DeriveJsonEncoder.gen[Proof] + given decoder: JsonDecoder[Proof] = DeriveJsonDecoder.gen[Proof] given schema: Schema[Proof] = Schema.derived + + object annotations { + object `type` + extends Annotation[String]( + description = "The type of cryptographic signature algorithm used to generate the proof.", + example = "Ed25519Signature2018" + ) + + object created + extends Annotation[OffsetDateTime]( + description = "The date and time at which the proof was created, in UTC format. " + + "This field is used to ensure that the proof was generated before or at the same time as the credential schema itself.", + example = OffsetDateTime.parse("2022-03-10T12:00:00Z") + ) + + object proofPurpose + extends Annotation[String]( + description = "The purpose of the proof (for example: `assertionMethod`). " + + "This indicates that the proof is being used to assert that the issuer really issued this credential schema instance.", + example = "assertionMethod" + ) + + object verificationMethod + extends Annotation[String]( + description = "The verification method used to generate the proof. " + + "This is usually a DID and key ID combination that can be used to look up the public key needed to verify the proof.", + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1" + ) + + object jws + extends Annotation[String]( + description = "The JSON Web Signature (JWS) that contains the proof information.", + example = "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik..." + ) + + object proofValue + extends Annotation[String]( + description = + "The cryptographic signature value that was generated using the private key associated with the verification method, " + + "and which can be used to verify the proof.", + example = "FiPfjknHikKmZ..." + ) + + object domain + extends Annotation[String]( + description = "It specifies the domain context within which the credential schema and proof are being used", + example = "prims.atala.com" + ) + } + + val Example = Proof( + `type` = annotations.`type`.example, + created = annotations.created.example, + verificationMethod = annotations.verificationMethod.example, + proofPurpose = annotations.proofPurpose.example, + proofValue = annotations.proofValue.example, + jws = annotations.jws.example, + domain = Some(annotations.domain.example) + ) } diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaBasicSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaBasicSpec.scala index f52d802f89..8e039d36ef 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaBasicSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaBasicSpec.scala @@ -7,7 +7,7 @@ import io.iohk.atala.pollux.credentialschema.* import io.iohk.atala.pollux.credentialschema.controller.{CredentialSchemaController, CredentialSchemaControllerImpl} import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse } import io.iohk.atala.pollux.sql.repository.JdbcCredentialSchemaRepository @@ -43,7 +43,7 @@ object CredentialSchemaBasicSpec extends ZIOSpecDefault with CredentialSchemaTes private val schemaInput = CredentialSchemaInput( name = "TestSchema", - version = "1.0", + version = "1.0.0", description = Option("schema description"), `type` = "json", schema = """{"first_name": "string", "dob": "datetime"}""" diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaFailureSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaFailureSpec.scala index 40e8d6ec60..0568fe0969 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaFailureSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaFailureSpec.scala @@ -6,7 +6,7 @@ import io.iohk.atala.pollux.credentialschema.* import io.iohk.atala.pollux.credentialschema.controller.CredentialSchemaController import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse } import io.iohk.atala.pollux.test.container.MigrationAspects.migrate diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaLookupAndPaginationSpec.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaLookupAndPaginationSpec.scala index 4b6065143c..247edc0f1c 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaLookupAndPaginationSpec.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaLookupAndPaginationSpec.scala @@ -6,7 +6,7 @@ import io.iohk.atala.pollux.credentialschema.* import io.iohk.atala.pollux.credentialschema.controller.CredentialSchemaController import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse } import io.iohk.atala.pollux.test.container.MigrationAspects.migrate @@ -37,34 +37,34 @@ object CredentialSchemaLookupAndPaginationSpec with CredentialSchemaTestTools with CredentialSchemaGen: - def fetchAllPages(uri: Uri): ZIO[CredentialSchemaController, Throwable, List[CredentialSchemaPageResponse]] = { + def fetchAllPages(uri: Uri): ZIO[CredentialSchemaController, Throwable, List[CredentialSchemaResponsePage]] = { for { controller <- ZIO.service[CredentialSchemaController] backend = httpBackend(controller) response: SchemaPageResponse <- basicRequest .get(uri) - .response(asJsonAlways[CredentialSchemaPageResponse]) + .response(asJsonAlways[CredentialSchemaResponsePage]) .send(backend) firstPage <- ZIO.fromEither(response.body) otherPagesStream = zio.stream.ZStream - .unfoldZIO[Any, Throwable, CredentialSchemaPageResponse, CredentialSchemaPageResponse](firstPage)(page => + .unfoldZIO[Any, Throwable, CredentialSchemaResponsePage, CredentialSchemaResponsePage](firstPage)(page => page.next .map(n => uri"$n") .fold( - ZIO.succeed(Option.empty[(CredentialSchemaPageResponse, CredentialSchemaPageResponse)]) + ZIO.succeed(Option.empty[(CredentialSchemaResponsePage, CredentialSchemaResponsePage)]) )(nextPageUri => for { nextPageResponse: SchemaPageResponse <- basicRequest .get(nextPageUri) - .response(asJsonAlways[CredentialSchemaPageResponse]) + .response(asJsonAlways[CredentialSchemaResponsePage]) .send(backend) nextPage <- ZIO.fromEither(nextPageResponse.body) } yield Some((nextPage, nextPage)) ) ) otherPages <- otherPagesStream.runCollect - .fold(_ => List.empty[CredentialSchemaPageResponse], success => success.toList) + .fold(_ => List.empty[CredentialSchemaResponsePage], success => success.toList) } yield List(firstPage) ++ otherPages } @@ -97,7 +97,7 @@ object CredentialSchemaLookupAndPaginationSpec response: SchemaPageResponse <- basicRequest .get(credentialSchemaUriBase) - .response(asJsonAlways[CredentialSchemaPageResponse]) + .response(asJsonAlways[CredentialSchemaResponsePage]) .send(backend) schemaPage <- ZIO.fromEither(response.body) diff --git a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaTestTools.scala b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaTestTools.scala index 346fa77bb3..63bec33076 100644 --- a/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaTestTools.scala +++ b/prism-agent/service/server/src/test/scala/io/iohk/atala/pollux/CredentialSchemaTestTools.scala @@ -7,7 +7,7 @@ import io.iohk.atala.pollux.credentialschema.SchemaRegistryServerEndpoints import io.iohk.atala.pollux.credentialschema.controller.{CredentialSchemaController, CredentialSchemaControllerImpl} import io.iohk.atala.pollux.credentialschema.http.{ CredentialSchemaInput, - CredentialSchemaPageResponse, + CredentialSchemaResponsePage, CredentialSchemaResponse } import io.iohk.atala.pollux.sql.repository.JdbcCredentialSchemaRepository @@ -41,7 +41,7 @@ trait CredentialSchemaTestTools { Response[Either[DeserializationException[String], CredentialSchemaResponse]] type SchemaPageResponse = Response[ - Either[DeserializationException[String], CredentialSchemaPageResponse] + Either[DeserializationException[String], CredentialSchemaResponsePage] ] private val pgLayer = postgresLayer(verbose = false) @@ -96,7 +96,10 @@ trait CredentialSchemaGen { self: ZIOSpecDefault with CredentialSchemaTestTools => object Generator { val schemaName = Gen.alphaNumericStringBounded(4, 12) - val schemaVersion = Gen.int(1, 5).map(i => s"$i.0") + val majorVersion = Gen.int(1, 9) + val minorVersion = Gen.int(0, 9) + val patchVersion = Gen.int(0, 9) + val schemaVersion = majorVersion <*> minorVersion <*> patchVersion map (v => s"${v._1}.${v._2}.${v._3}") val schemaDescription = Gen.alphaNumericStringBounded(5, 30) val schemaAttribute = Gen.alphaNumericStringBounded(3, 9) val schemaAttributes = Gen.setOfBounded(1, 4)(schemaAttribute).map(_.toList) diff --git a/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt b/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt index 7540105a7d..c296ce86eb 100644 --- a/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt +++ b/tests/e2e-tests/src/test/kotlin/common/CredentialSchemas.kt @@ -23,7 +23,8 @@ object CredentialSchemas { "type": "integer" } } - }""".trimIndent() + } + """.trimIndent() fun generate_with_name_suffix(suffix: String): CredentialSchema { return CredentialSchema( @@ -33,7 +34,7 @@ object CredentialSchemas { type = CREDENTIAL_SCHEMA_TYPE, schema = ObjectMapper().readTree(JSON_SCHEMA), tags = listOf("school", "students"), - version = "1.0", + version = "1.0.0", ) } @@ -44,6 +45,6 @@ object CredentialSchemas { type = CREDENTIAL_SCHEMA_TYPE, schema = ObjectMapper().readTree(JSON_SCHEMA), tags = listOf("school", "students"), - version = "1.0", + version = "1.0.0", ) }