diff --git a/E2E/Package.swift b/E2E/Package.swift new file mode 100644 index 00000000..db79c4b6 --- /dev/null +++ b/E2E/Package.swift @@ -0,0 +1,40 @@ +// swift-tools-version: 5.7 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "e2e", + platforms: [.iOS(.v15), .macOS(.v12)], + products: [ + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-openapi-generator", .upToNextMinor(from: "0.3.4")), + .package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.6")), + .package(url: "https://github.com/apple/swift-openapi-urlsession", .upToNextMinor(from: "0.3.0")), + .package(url: "https://github.com/nschum/SwiftHamcrest", .upToNextMajor(from: "2.2.1")), + .package(path: "../") + ], + targets: [ + .testTarget( + name: "e2e", + dependencies: [ + .product(name: "PrismAgent", package: "atala-prism-wallet-sdk-swift"), + .product(name: "Domain", package: "atala-prism-wallet-sdk-swift"), + .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"), + .product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession"), + .product(name: "SwiftHamcrest", package: "SwiftHamcrest") + ], + path: "e2eTests", + resources: [ + .copy("Resources") + ], + plugins: [ + .plugin( + name: "OpenAPIGenerator", + package: "swift-openapi-generator" + ) + ] + ) + ] +) diff --git a/E2E/e2eTests/.gitignore b/E2E/e2eTests/.gitignore new file mode 100644 index 00000000..37e88c80 --- /dev/null +++ b/E2E/e2eTests/.gitignore @@ -0,0 +1,2 @@ +Target +properties.plist \ No newline at end of file diff --git a/E2E/e2eTests/README.md b/E2E/e2eTests/README.md new file mode 100644 index 00000000..4a718801 --- /dev/null +++ b/E2E/e2eTests/README.md @@ -0,0 +1,313 @@ +# Swift library test automation + +## Description + +Due the lack of BDD frameworks that allows swift libraries to be tested this framework was built using concepts of: + +- Cucumber +- SerenityBDD + +## Getting started + +The lifecycle of the framework is based on XCTest lifecycle. + +The `TestFramework` provides a base configuration class named `TestConfiguration` which must be extended. + +For now, only `1` child class is supported and we'll refer to that class as `Configuration`. + +```swift +class MyConfig: TestConfiguration { + // ... +} +``` + +### Defining the actors + +To create the `actors`, in the `Configuration` file you should override the base class method `createActors()` + +```swift + override func createActors() async throws -> [Actor] { + let actor1 = Actor("Bob").whoCanUse(MyCustomAbility.self) + let actor2 = Actor("Alice").whoCanUse(OtherCustomAbility.self ) + return [actor1, actor2] + } +``` + +### Defining parsers + +Parsers are a method that enable us to convert the data provided in step to parameterized arguments in steps + +To parameterize a step we use curly brackets and the type inside (`{type}`). For example: `{actor} counts to {int}` + +If no `type` is inside the curly brackets, the framework will use `string` as default. + +Parsers that are native to the framework: + +- String: converts the value to `string` +- Int: converts the value to `int` +- Actor: converts the value to an existing `actor`. Note: the actor must exist. + +To declare new parsers in the `Configuration` class you can add a new parser as the following example: + +```swift + @ParameterParser + var myCustomParser = { (data: String) in + return MyCustomType(data: data) + } +``` + +### Defining reporter + +In the `Configuration` file you have to setup the reporters you want. + +By default it's `JunitReporter`, `HtmlReporter` and `DotReporter`. + +Example: + +```swift + override func createReporters() async throws -> [Reporter] { + return [ConsoleReporter(), HtmlReporter()] + } +``` + +#### Available reporters: + +- ConsoleReporter: pretty print bdd in console +- DotReporter: prints `.` for each action and in the end prints a summary +- HtmlReporter: generates a HTML file. Note: for now it's only creating a .txt file +- JunitReporter: generates a XML file in junit format + +### Defining the steps + +To create steps using the framework you should extend the `Steps` class and define the `Step` variables through annotation. + +Here's an example: + +```swift +class MyCustomSteps: Steps { + @Step("The {actor} counts to {int}") + var theActorCountsToANumber = { (myActor: Actor, number: Int) in + <#code#> + } + // ... +} +``` + +### Defining the feature + +Extends `Feature` class and override the `title` and optionally the `description`. + +```swift +class MyFeature: Feature { + override func title() -> String { + "My custom feature" + } + + override func description() -> String { + "My custom feature description" + } + + // ... +} +``` + +### Defining the scenario + +Inside the `Feature` we define the scenarios that will be executed. + +```swift +class MyFeature: Feature { + // ... + + func myTestCase() async throws { + currentScenario = Scenario("My custom scenario") + .given("Bob knows how to count") + .when("Bob counts to 10") + .then("The system should have waited at least 10 seconds") + } +} +``` + +For now it's not possible to parameterize the scenarios. + +## Additional information + +### Defining the abilities + +Abilities are a concept inherited from `Serenity BDD`. This allows the `actors` to execute commands only if they have the ability to. + +#### Adding the ability to the actor + +You can add an ability to the `Actor` using the method `whoCanUse` + +```swift + let myActor = Actor("My Actor").whoCanUse(MyAbility.self) +``` + +or adding to an existing `Actor`: + +```swift + myActor.whoCanUse(MyAbility.self) +``` + +### Defining reporters + +#### Console reporter + +Prints in a readable way all the actions taken during the execution. + +#### Dot reporter + +Simple reporter that prints `.` for the actions taken during the test and prints the overall results. + +#### Junit reporter + +Generates the result in a `junit.xml` format. That can be used in CI/CD tools. + +The result is available in the path provided by `Configuration` method `targetDirectory`. + +#### HTML reporter + +Generates a readable report with the steps executed and its results. + +The result is available in the path provided by `Configuration` method `targetDirectory`. + +Note: For now it's a plain txt file. + +#### Creating a custom reporter + +To create a custom reporter you have to create the class and extend the `Reporter` base class and implement the protocol methods. + +```swift +class MyCustomReporter: Reporter { + func beforeFeature(_ feature: Feature) async throws { + <#code#> + } + + // ... +} +``` + +And add the reporter to the reporters list. + +## Assertions + +### Hamcrest + +This framework enable the usage of `Hamcrest`. + +Usage: + +```swift +import SwiftHamcrest + +class MySteps: Steps { + @Step("{actor} should see the calculator shows {int}") + var bobShouldSeeTheCalculatorShowsExpectedNumber = { (bob: Actor, expectedNumber: Int) in + assertThat(expectedNumber, equalTo(result)) + } +} +``` + +### Wait + +There's an assertion method to wait for an asynchronous verification. The method accepts an optional timeout (seconds) - default: 30. + +It expects a boolean response for the assertion result. + +Example: + +```swift +func myTest() { + try await Wait.until(timeout: 60) { + let response = try await api.getSomething() + return response.data == "EXPECTED" + } +} +``` + +## Full example + +```swift +import Foundation +import XCTest +import SwiftHamcrest + +class MyTest: Feature { + override func title() -> String { + "My custom title" + } + + override class func description() -> String { + "My custom title" + } + + func testMyCustomScenario1() { + currentScenario = Scenario("My custom scenario") + .given("Bob has a calculator") + .when("Bob sums 1 + 1") + .then("Bob should see the calculator shows 2") + } + + func testMyCustomScenario2() { + currentScenario = Scenario("My custom scenario") + .given("Bob has a calculator") + .when("Bob sums 1 + 2") + .then("Bob should see the calculator shows 2") + } +} + +class MySteps: Steps { + static var result = 0 + + @Step("{actor} has a calculator") + var bobHasCalculator = { (bob: Actor) in + + } + + @Step("{actor} sums {int} + {int}") + var bobSumsOnePlusOne = { (bob: Actor, n1: Int, n2: Int) in + result = n1 + n2 + } + + @Step("{actor} should see the calculator shows {int}") + var bobShouldSeeTheCalculatorShowsExpectedNumber = { (bob: Actor, expectedNumber: Int) in + assertThat(expectedNumber, equalTo(result)) + } +} + +class MyConfig: TestConfiguration { + override class func createInstance() -> ITestConfiguration { + MyConfig() + } + + override func createActors() async throws -> [Actor] { + let bob = Actor("Bob") + return [bob] + } + + override func setUp() async throws { + + } + + override func tearDown() async throws { + + } + + override func targetDirectory() -> URL { + return URL(fileURLWithPath: #file) + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("Target") + } +} +``` + +## Disclaimer + +The framework is still under development. + +## References + +- Cucumber - https://cucumber.io/docs/cucumber/ +- Serenity screenplay - https://serenity-bdd.github.io/docs/screenplay/screenplay_fundamentals diff --git a/E2E/e2eTests/Resources/properties.plist.example.plist b/E2E/e2eTests/Resources/properties.plist.example.plist new file mode 100644 index 00000000..8475f149 --- /dev/null +++ b/E2E/e2eTests/Resources/properties.plist.example.plist @@ -0,0 +1,18 @@ + + + + + MEDIATOR_OOB_URL + + PRISM_AGENT_URL + + APIKEY + + PUBLISHED_DID + + JWT_SCHEMA_GUID + + ANONCRED_DEFINITION_GUID + + + diff --git a/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift b/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift new file mode 100644 index 00000000..6f4189e6 --- /dev/null +++ b/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift @@ -0,0 +1,487 @@ +import Foundation +import OpenAPIRuntime +import OpenAPIURLSession +import HTTPTypes + +class OpenEnterpriseAPI: Ability { + typealias AbilityInstanceType = API + private var api: AbilityInstanceType? = nil + + let actor: Actor + let abilityName: String = "OEA API" + + required init(_ actor: Actor) { + self.actor = actor + } + + func instance() -> AbilityInstanceType { + return api! + } + + func setUp(_ actor: Actor) async throws { + api = API(StepReporterMiddleware(actor.name)) + } + + func tearDown() async throws { + } + + class API { + private var transport: URLSessionTransport? = nil + private var client: Client? = nil + + init(_ middlewares: ClientMiddleware...) { + transport = URLSessionTransport() + client = Client( + serverURL: URL(string: Config.agentUrl)!, + configuration: .init(dateTranscoder: MyDateTranscoder()), + transport: transport!, + middlewares: [APITokenMiddleware(apikey: Config.apiKey)] + middlewares + ) + } + + func isDidPresent(_ did: String) async throws -> Bool { + if (did.isEmpty) { + return false + } + + do { + _ = try await getDid(did) + return true + } catch { + return false + } + } + + func getDid(_ did: String) async throws -> Components.Schemas.ManagedDID { + let response = try await client!.getDid_hyphen_registrarDidsDidref(.init(path: .init(didRef: did))) + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func createUnpublishedDid() async throws -> Components.Schemas.CreateManagedDIDResponse { + let createManagedDidRequest = Components.Schemas.CreateManagedDidRequest( + documentTemplate: .init( + publicKeys: .init( + arrayLiteral: .init( + id: "key-1", + purpose: .assertionMethod + ) + ), + services: [] + ) + ) + + let response = try await client!.postDid_hyphen_registrarDids(body: .json(createManagedDidRequest)) + + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func publishDid(_ longFormDid: String) async throws -> Components.Schemas.DIDOperationResponse { + let response = try await client!.postDid_hyphen_registrarDidsDidrefPublications(path: .init(didRef: longFormDid)) + switch (response) { + case .accepted(let acceptedResponse): + switch(acceptedResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func isJwtSchemaGuidPresent(_ guid: String) async throws -> Bool { + if (guid.isEmpty) { + return false + } + + do { + _ = try await getJwtSchema(guid) + return true + } catch { + return false + } + } + + func getJwtSchema(_ guid: String) async throws -> Components.Schemas.CredentialSchemaResponse { + let response = try await client!.getSchemaById(path: .init(guid: guid)) + + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func createJwtSchema(author: String) async throws -> Components.Schemas.CredentialSchemaResponse { + let schemaName = "automation-jwt-schema-" + UUID().uuidString + var schema = try OpenAPIValueContainer() + schema.value = [ + "$id": "https://example.com/\(schemaName)", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Automation schema description", + "type": "object", + "properties": [ + "automation-required": [ + "type": "string" + ], + "automation-optional": [ + "type": "string" + ] + ] + ] + + let credentialSchemaInput = Components.Schemas.CredentialSchemaInput( + name: schemaName, + version: "0.0.1", + description: "Some description to automation generated schema", + _type: "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json", + schema: schema, + tags: ["automation"], + author: author + ) + + let response = try await client!.createSchema(body: .json(credentialSchemaInput)) + switch (response) { + case .created(let createdResponse): + switch (createdResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func isAnoncredDefinitionPresent(_ anoncredDefinitionGuid: String) async throws -> Bool { + if (anoncredDefinitionGuid.isEmpty) { + return false + } + + do { + _ = try await getAnoncredDefinition(anoncredDefinitionGuid) + return true + } catch { + return false + } + } + + func getAnoncredDefinition(_ anoncredDefinitionGuid: String) async throws -> Components.Schemas.CredentialDefinitionResponse { + let response = try await client!.getCredentialDefinitionById(path: .init(guid: anoncredDefinitionGuid)) + + switch(response) { + case .ok(let okResponse): + switch(okResponse.body){ + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func createAnoncredSchema(_ issuerId: String) async throws -> Components.Schemas.CredentialSchemaResponse { + let schemaName = "automation-anoncred-schema-" + UUID().uuidString + + var schema = try OpenAPIValueContainer() + schema.value = [ + "name": "Automation Anoncred", + "version": "1.0", + "issuerId": issuerId, + "attrNames": [ + "name", + "age" + ] + ] + + let credentialSchemaInput = Components.Schemas.CredentialSchemaInput( + name: schemaName, + version: "2.0.0", + description: "Some description to automation generated schema", + _type: "AnoncredSchemaV1", + schema: schema, + tags: ["automation"], + author: issuerId + ) + + let response = try await client!.createSchema(body: .json(credentialSchemaInput)) + switch (response) { + case .created(let createdResponse): + switch (createdResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func createAnoncredDefinition(_ issuerId: String, _ anoncredSchemaGuid: String) async throws -> Components.Schemas.CredentialDefinitionResponse { + let definitionName = "automation-anoncred-definition-" + UUID().uuidString + let anoncredDefinition = Components.Schemas.CredentialDefinitionInput.init( + name: definitionName, + description: "Test Automation Auto-Generated", + version: "1.0.0", + tag: "automation-test", + author: issuerId, + schemaId: "\(Config.agentUrl)/schema-registry/schemas/\(anoncredSchemaGuid)", + signatureType: "CL", + supportRevocation: true + ) + + let response = try await client!.createCredentialDefinition(body: .json(anoncredDefinition)) + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func getConnections() async throws -> Components.Schemas.ConnectionsPage { + let response = try await client!.getConnections(.init()) + + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let response): + return response + } + default: + throw Error.WrongResponse + } + } + + func getConnection(_ connectionId: String) async throws -> Components.Schemas.Connection { + let response = try await client!.getConnection(path: .init(connectionId: connectionId)) + + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let response): + return response + } + default: + throw Error.WrongResponse + } + } + + func createConnection() async throws -> Components.Schemas.Connection { + let body = Components.Schemas.CreateConnectionRequest(label: "Alice") + let response = try await client!.createConnection(.init(body: .json(body))) + + switch(response) { + case .created(let okResponse): + switch(okResponse.body) { + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func offerCredential(_ connectionId: String) async throws -> Components.Schemas.IssueCredentialRecord { + var claims: OpenAPIValueContainer = try OpenAPIValueContainer() + claims.value = [ + "automation-required" : UUID().uuidString + ] + + let body = Components.Schemas.CreateIssueCredentialRecordRequest( + schemaId: "\(Config.agentUrl)/schema-registry/schemas/\(Config.jwtSchemaGuid)", + claims: claims, + issuingDID: Config.publishedDid, + connectionId: connectionId + ) + + let response = try await client!.createCredentialOffer(body: .json(body)) + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func offerAnonymousCredential(_ connectionId: String) async throws -> Components.Schemas.IssueCredentialRecord { + var claims: OpenAPIValueContainer = try OpenAPIValueContainer() + claims.value = [ + "name" : "automation", + "age" : "99" + ] + + let body = Components.Schemas.CreateIssueCredentialRecordRequest( + credentialDefinitionId: Config.anoncredDefinitionGuid, + credentialFormat: "AnonCreds", + claims: claims, + automaticIssuance: true, + issuingDID: Config.publishedDid, + connectionId: connectionId + ) + + let response = try await client!.createCredentialOffer(body: .json(body)) + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func getCredentialRecord(_ recordId: String) async throws -> Components.Schemas.IssueCredentialRecord { + let response = try await client!.getCredentialRecord(path: .init(recordId: recordId)) + switch(response){ + case .ok(let okResponse): + switch(okResponse.body){ + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func requestPresentProof(_ connectionId: String) async throws -> Components.Schemas.PresentationStatus { + let options = Components.Schemas.Options( + challenge: UUID().uuidString, + domain: Config.agentUrl + ) + + let proof = Components.Schemas.ProofRequestAux( + schemaId: Config.jwtSchemaGuid, + trustIssuers: [] + ) + + let body = Components.Schemas.RequestPresentationInput( + connectionId: connectionId, + options: options, + proofs: [proof], + credentialFormat: "JWT" + ) + + let response = try await client!.requestPresentation(body: .json(body)) + + switch(response){ + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + func getPresentation(_ presentationId: String) async throws -> Components.Schemas.PresentationStatus { + let response = try await client!.getPresentation(path: .init(presentationId: presentationId)) + switch(response){ + case .ok(let okResponse): + switch(okResponse.body){ + case .json(let body): + return body + } + default: + throw Error.WrongResponse + } + } + + enum Error: Swift.Error, Equatable { + case WrongResponse + } + } +} + +// https://github.com/apple/swift-openapi-generator/issues/84 +struct MyDateTranscoder: DateTranscoder { + private var dateFormatters: [DateFormatter] = [] + + func encode(_ date: Date) throws -> String { + return dateFormatters[0].string(from: date) + } + + func decode(_ string: String) throws -> Date { + for formatter in dateFormatters { + if let result = formatter.date(from: string) { + return result + } + } + throw DecodingError.dataCorrupted(.init( + codingPath: [], + debugDescription: "Date string does not match any of the expected formats")) + } + + init() { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'" + dateFormatters.append(dateFormatter) + } +} + +extension HTTPField.Name { + static let apikey = Self("apikey")! +} + +struct APITokenMiddleware: ClientMiddleware { + let apikey: String + + func intercept( + _ request: HTTPRequest, + body: HTTPBody?, + baseURL: URL, + operationID: String, + next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?) + ) async throws -> (HTTPResponse, HTTPBody?) { + var request = request + request.headerFields[.apikey] = apikey + return try await next(request, body, baseURL) + } + + init(apikey: String) { + self.apikey = apikey + } +} + +struct StepReporterMiddleware: ClientMiddleware { + private var actor: String + + init(_ actor: String) { + self.actor = actor + } + + func intercept( + _ request: HTTPRequest, + body: HTTPBody?, + baseURL: URL, + operationID: String, + next: @Sendable (HTTPRequest, HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?) + ) async throws -> (HTTPResponse, HTTPBody?) { + return try await next(request, body, baseURL) + } +} diff --git a/E2E/e2eTests/Source/Abilities/Sdk.swift b/E2E/e2eTests/Source/Abilities/Sdk.swift new file mode 100644 index 00000000..89110cd3 --- /dev/null +++ b/E2E/e2eTests/Source/Abilities/Sdk.swift @@ -0,0 +1,221 @@ +import Foundation +import PrismAgent +import Builders +import Combine +import Domain +import Pluto +import Core + +class Sdk: Ability { + typealias AbilityInstanceType = Client + let actor: Actor + let abilityName: String = "Swift SDK" + private var client: Client? = nil + + required init(_ actor: Actor) { + self.actor = actor + } + + func instance() -> AbilityInstanceType { + return client! + } + + func setUp(_ actor: Actor) async throws { + client = try await Client() + try await client!.initialize() + } + + func tearDown() async throws { + try await client?.tearDown() + } + + class Client { + var credentialOfferStack: [Message] = [] + var issueCredentialStack: [Message] = [] + var proofOfRequestStack: [Message] = [] + var receivedMessages: [String] = [] + var cancellables = Set() + + let prismAgent: PrismAgent + + init() async throws { + let mediatorDID = try await Client.getPrismMediatorDid() +// let mediatorDID = try await Client.getRootsMediatorDid() + + let apollo = ApolloBuilder().build() + let castor = CastorBuilder(apollo: apollo).build() + let pluto = PlutoBuilder(setup: .init( + coreDataSetup: .init( + modelPath: .storeName("PrismPluto"), + storeType: .memory + ), + keychain: KeychainMock() + )).build() + let pollux = PolluxBuilder(pluto: pluto).build() + let mercury = MercuryBuilder( + castor: castor, + secretsStream: Client.createSecretsStream( + keyRestoration: apollo, + pluto: pluto, + castor: castor + ) + ).build() + + PrismAgent.setupLogging(logLevels: [ + .prismAgent: .warning + ]) + + prismAgent = PrismAgent( + apollo: apollo, + castor: castor, + pluto: pluto, + pollux: pollux, + mercury: mercury, + mediationHandler: BasicMediatorHandler( + mediatorDID: mediatorDID, + mercury: mercury, + store: BasicMediatorHandler.PlutoMediatorStoreImpl(pluto: pluto) + ) + ) + } + + func initialize() async throws { + try await prismAgent.start() + + prismAgent.handleReceivedMessagesEvents() + .sink( + receiveCompletion: { completion in + switch completion { + case .finished: + print("Publisher completed successfully.") + case .failure(let error): + print("Publisher failed with error: \(error)") + } + }, + receiveValue: { message in + if (self.receivedMessages.contains(message.id)) { + return + } + self.receivedMessages.append(message.id) + switch(message.piuri) { + case ProtocolTypes.didcommOfferCredential3_0.rawValue: + self.credentialOfferStack.append(message) + case ProtocolTypes.didcommIssueCredential3_0.rawValue: + self.issueCredentialStack.append(message) + case ProtocolTypes.didcommRequestPresentation.rawValue: + self.proofOfRequestStack.append(message) + default: + break + } + } + ) + .store(in: &cancellables) + + prismAgent.startFetchingMessages() + } + + func tearDown() async throws { + prismAgent.stopFetchingMessages() + try await prismAgent.stop() + } + + static private func getPrismMediatorDid() async throws -> DID { + let url = URL(string: Config.mediatorOobUrl)! + let jsonData: [String: Any] = try await Api.get(from: url) + let did = (jsonData["from"] as? String)! + return try DID(string: did) + } + + static private func getRootsMediatorDid() async throws -> DID { + let url = URL(string: Config.mediatorOobUrl)! + let invitationUrl: String = try await Api.get(from: url) + let base64data: String = String(invitationUrl.split(separator: "?_oob=").last!) + let decodedData = Data(base64Encoded: base64data)! + let json = try (JSONSerialization.jsonObject(with: decodedData, options: []) as? [String: Any])! + let from = (json["from"] as? String)! + return try DID(string: from) + } + + private static func fromBase64(_ encoded: String) -> Data { + var encoded = encoded; + let remainder = encoded.count % 4 + if remainder > 0 { + encoded = encoded.padding( + toLength: encoded.count + 4 - remainder, + withPad: "=", startingAt: 0); + } + return Data(base64Encoded: encoded)! + // return String(data: data, encoding: .utf8)! + } + + static private func createSecretsStream( + keyRestoration: KeyRestoration, + pluto: Pluto, + castor: Castor + ) -> AnyPublisher<[Secret], Error> { + pluto.getAllPeerDIDs() + .first() + .flatMap { array in + Future { + try await array.asyncMap { did, privateKeys, _ in + let privateKeys = try await privateKeys.asyncMap { + try await keyRestoration.restorePrivateKey($0) + } + let secrets = try parsePrivateKeys( + did: did, + privateKeys: privateKeys, + castor: castor + ) + + return secrets + } + } + } + .map { + $0.compactMap { + $0 + }.flatMap { + $0 + } } + .eraseToAnyPublisher() + } + + static private func parsePrivateKeys( + did: DID, + privateKeys: [PrivateKey], + castor: Castor + ) throws -> [Domain.Secret] { + return try privateKeys + .map { $0 as? (PrivateKey & ExportableKey) } + .compactMap { $0 } + .map { privateKey in + let ecnumbasis = try castor.getEcnumbasis(did: did, publicKey: privateKey.publicKey()) + return (did, privateKey, ecnumbasis) + } + .map { did, privateKey, ecnumbasis in + try parseToSecret( + did: did, + privateKey: privateKey, + ecnumbasis: ecnumbasis + ) + } + } + + static private func parseToSecret(did: DID, privateKey: PrivateKey & ExportableKey, ecnumbasis: String) throws -> Domain.Secret { + let id = did.string + "#" + ecnumbasis + let jwk = privateKey.jwk + guard + let dataJson = try? JSONEncoder().encode(jwk), + let stringJson = String(data: dataJson, encoding: .utf8) + else { + throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk") + } + return .init( + id: id, + type: .jsonWebKey2020, + secretMaterial: .jwk(value: stringJson) + ) + } + } + +} diff --git a/E2E/e2eTests/Source/Api.swift b/E2E/e2eTests/Source/Api.swift new file mode 100644 index 00000000..770fc9fa --- /dev/null +++ b/E2E/e2eTests/Source/Api.swift @@ -0,0 +1,21 @@ +import Foundation + +class Api { + static func get(from url: URL) async throws -> [String : Any] { + let session = URLSession.shared + let (data, _) = try await session.data(from: url) + let response = try (JSONSerialization.jsonObject(with: data, options: []) as? [String: Any])! + return response + } + + static func get(from url: URL) async throws -> String { + let session = URLSession.shared + let (data, _) = try await session.data(from: url) + let response = String(bytes: data, encoding: String.Encoding.utf8)! + return response + } +} + +enum ApiError: Error { + case failure(message: String) +} diff --git a/E2E/e2eTests/Source/Config.swift b/E2E/e2eTests/Source/Config.swift new file mode 100644 index 00000000..3a99131c --- /dev/null +++ b/E2E/e2eTests/Source/Config.swift @@ -0,0 +1,120 @@ +import Foundation + +/// Initializes CucumberLite configuration +class Config: TestConfiguration { + static var mediatorOobUrl: String = "" + static var agentUrl: String = "" + static var publishedDid: String = "" + static var jwtSchemaGuid: String = "" + static var anoncredDefinitionGuid: String = "" + static var apiKey: String = "" + + override class func createInstance() -> ITestConfiguration { + return Config() + } + + override func targetDirectory() -> URL { + return URL(fileURLWithPath: #file) + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("Target") + } + + override func createReporters() async throws -> [Reporter] { + return [ConsoleReporter(), JunitReporter(), HtmlReporter()] + } + + override func createActors() async throws -> [Actor] { + let cloudAgent = Actor("Cloud Agent").whoCanUse(OpenEnterpriseAPI.self) + let edgeAgent = Actor("Edge Agent").whoCanUse(Sdk.self ) + return [cloudAgent, edgeAgent] + } + + override func setUp() async throws { + Config.mediatorOobUrl = environment["MEDIATOR_OOB_URL"]! + Config.agentUrl = environment["PRISM_AGENT_URL"]! + Config.publishedDid = environment["PUBLISHED_DID"] ?? "" + Config.jwtSchemaGuid = environment["JWT_SCHEMA_GUID"] ?? "" + Config.anoncredDefinitionGuid = environment["ANONCRED_DEFINITION_GUID"] ?? "" + Config.apiKey = environment["APIKEY"] ?? "" + + try await checkPublishedDid() + try await checkJwtSchema() + try await checkAnoncredDefinition() + + print("Mediator", Config.mediatorOobUrl) + print("Agent", Config.agentUrl) + print("DID", Config.publishedDid) + print("JWT Schema", Config.jwtSchemaGuid) + print("Anoncred Definition", Config.anoncredDefinitionGuid) + // TODO: Get SDK version + } + + override func tearDown() async throws { + } + + private func checkPublishedDid() async throws { + let api = OpenEnterpriseAPI.API() + let isPresent = try await api.isDidPresent(Config.publishedDid) + if (isPresent) { + return + } + print("DID [\(Config.publishedDid)] not found. Creating a new one.") + + let unpublishedDid = try await api.createUnpublishedDid() + let publishedDid = try await api.publishDid(unpublishedDid.longFormDid) + let shortFormDid = publishedDid.scheduledOperation.didRef + + try await Wait.until(timeout: 60) { + let did = try await api.getDid(shortFormDid) + return did.status == "PUBLISHED" + } + + Config.publishedDid = shortFormDid + } + + private func checkJwtSchema() async throws { + let api = OpenEnterpriseAPI.API() + let isPresent = try await api.isJwtSchemaGuidPresent(Config.jwtSchemaGuid) + if (isPresent) { + return + } + print("JWT schema [\(Config.jwtSchemaGuid)] not found. Creating a new one.") + + let jwtSchema = try await api.createJwtSchema(author: Config.publishedDid) + Config.jwtSchemaGuid = jwtSchema.guid + } + + private func checkAnoncredDefinition() async throws { + let api = OpenEnterpriseAPI.API() + let isPresent = try await api.isAnoncredDefinitionPresent(Config.anoncredDefinitionGuid) + if (isPresent) { + return + } + print("Anoncred Definition not found for [\(Config.anoncredDefinitionGuid)]. Creating a new one.") + + let anoncredSchema = try await api.createAnoncredSchema(Config.publishedDid) + let anoncredDefinition = try await api.createAnoncredDefinition(Config.publishedDid, anoncredSchema.guid) + Config.anoncredDefinitionGuid = anoncredDefinition.guid + } + + private func writeToGithubSummary(_ command: String) { + // if (ProcessInfo.processInfo.environment.keys.contains("CI")) { + let process = Process() + process.launchPath = "/bin/bash" + process.arguments = ["-c", command] + + let pipe = Pipe() + process.standardOutput = pipe + process.launch() + + let data = pipe.fileHandleForReading.readDataToEndOfFile() + if let output = String(data: data, encoding: .utf8) { + print("Command output:\n\(output)") + } + } +} + +enum ConfigError: Error { + case PublishedDIDNotFound +} diff --git a/E2E/e2eTests/Source/Features/AnoncredFeature.swift b/E2E/e2eTests/Source/Features/AnoncredFeature.swift new file mode 100644 index 00000000..33aedb58 --- /dev/null +++ b/E2E/e2eTests/Source/Features/AnoncredFeature.swift @@ -0,0 +1,23 @@ +import Foundation +import XCTest + +class AnoncredFeature: Feature { + override func title() -> String { + "Receive anonymous credential" + } + + override func description() -> String { + "The Edge Agent should be able to receive an anonymous credential from Cloud Agent" + } + + func testReceiveOneAnoncred() async throws { + currentScenario = Scenario("Receive one anonymous credential") + .given("Cloud Agent is connected to Edge Agent") + .when("Cloud Agent offers an anonymous credential") + .then("Edge Agent should receive the credential") + .when("Edge Agent accepts the credential") + .when("Cloud Agent should see the credential was accepted") + .then("Edge Agent wait to receive 1 issued credentials") + .then("Edge Agent process 1 issued credentials") + } +} diff --git a/E2E/e2eTests/Source/Features/ConnectionFeature.swift b/E2E/e2eTests/Source/Features/ConnectionFeature.swift new file mode 100644 index 00000000..cba37ac2 --- /dev/null +++ b/E2E/e2eTests/Source/Features/ConnectionFeature.swift @@ -0,0 +1,18 @@ + +final class ConnectionFeature: Feature { + override func title() -> String { + "Create connection" + } + + override func description() -> String { + "The Edge Agent should be able to create a connection to Open Enterprise Agent" + } + + func testConnection() async throws { + currentScenario = Scenario("Create connection between Cloud and Edge agents") + .given("Cloud Agent has a connection invitation") + .given("Cloud Agent shares invitation to Edge Agent") + .when("Edge Agent connects through the invite") + .then("Cloud Agent should have the connection status updated to 'ConnectionResponseSent'") + } +} diff --git a/E2E/e2eTests/Source/Features/CredentialFeature.swift b/E2E/e2eTests/Source/Features/CredentialFeature.swift new file mode 100644 index 00000000..55556cf9 --- /dev/null +++ b/E2E/e2eTests/Source/Features/CredentialFeature.swift @@ -0,0 +1,40 @@ +import XCTest + +final class CredentialTests: Feature { + override func title() -> String { + "Receive verifiable credential" + } + + override func description() -> String { + "The Edge Agent should be able to receive a verifiable credential from Cloud Agent" + } + + func testReceiveOneCredential() async throws { + currentScenario = Scenario("Receive one verifiable credential") + .given("Cloud Agent is connected to Edge Agent") + .when("Cloud Agent offers a credential") + .then("Edge Agent should receive the credential") + .when("Edge Agent accepts the credential") + .when("Cloud Agent should see the credential was accepted") + .then("Edge Agent wait to receive 1 issued credentials") + .then("Edge Agent process 1 issued credentials") + } + + func testReceiveMultipleCredentialsSequentially() async throws { + currentScenario = Scenario("Receive multiple verifiable credentials sequentially") + .given("Cloud Agent is connected to Edge Agent") + .when("Edge Agent accepts 3 credential offer sequentially from Cloud Agent") + .then("Cloud Agent should see all credentials were accepted") + .and("Edge Agent wait to receive 3 issued credentials") + .and("Edge Agent process 3 issued credentials") + } + + func testReceiveMultipleCredentialsAtOnce() async throws { + currentScenario = Scenario("Receive multiple verifiable credentials at once") + .given("Cloud Agent is connected to Edge Agent") + .when("Edge Agent accepts 3 credentials offer at once from Cloud Agent") + .then("Cloud Agent should see all credentials were accepted") + .and("Edge Agent wait to receive 3 issued credentials") + .and("Edge Agent process 3 issued credentials") + } +} diff --git a/E2E/e2eTests/Source/Features/ProofOfRequestFeature.swift b/E2E/e2eTests/Source/Features/ProofOfRequestFeature.swift new file mode 100644 index 00000000..5b512f3d --- /dev/null +++ b/E2E/e2eTests/Source/Features/ProofOfRequestFeature.swift @@ -0,0 +1,20 @@ +import XCTest + +final class ProofOfRequestFeature: Feature { + override func title() -> String { + "Provide proof of request" + } + + override func description() -> String { + "The Edge Agent should provide proof to Cloud Agent" + } + + func testRespondToProofOfRequest() async throws { + currentScenario = Scenario("Respond to request proof") + .given("Cloud Agent is connected to Edge Agent") + .and("Edge Agent has 1 credentials issued by Cloud Agent") + .when("Cloud Agent asks for present-proof") + .and("Edge Agent sends the present-proof") + .then("Cloud Agent should see the present-proof is verified") + } +} diff --git a/E2E/e2eTests/Source/KeychainMock.swift b/E2E/e2eTests/Source/KeychainMock.swift new file mode 100644 index 00000000..4640ccf2 --- /dev/null +++ b/E2E/e2eTests/Source/KeychainMock.swift @@ -0,0 +1,28 @@ +import Domain +import Foundation +import Pluto + +class KeychainMock: KeychainStore, KeychainProvider { + var keys: [String: KeychainStorableKey] = [:] + + func getKey( + service: String, + account: String, + tag: String?, + algorithm: KeychainStorableKeyProperties.KeyAlgorithm, + type: KeychainStorableKeyProperties.KeyType + ) throws -> Data { + guard let key = keys[service+account] else { + throw PlutoError.errorRetrivingKeyFromKeychainKeyNotFound(service: service, account: account, applicationLabel: tag) + } + return key.storableData + } + + func addKey( + _ key: KeychainStorableKey, + service: String, + account: String + ) throws { + keys[service+account] = key + } +} diff --git a/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift b/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift new file mode 100644 index 00000000..692d7c15 --- /dev/null +++ b/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift @@ -0,0 +1,57 @@ +import Foundation + +class CloudAgentSteps: Steps { + @Step("{actor} offers an anonymous credential") + var cloudAgentOffersAnAnonymousCredential = { (cloudAgent: Actor) in + try await CloudAgentWorkflow.offersAnonymousCredential(cloudAgent: cloudAgent) + } + + @Step("{actor} asks for present-proof") + var cloudAgentAsksForPresentProof = { (cloudAgent: Actor) in + try await CloudAgentWorkflow.asksForPresentProof(cloudAgent: cloudAgent) + } + + @Step("{actor} should see the present-proof is verified") + var cloudAgentShouldSeeThePresentProofIsVerified = { (cloudAgent: Actor) in + try await CloudAgentWorkflow.verifyPresentProof(cloudAgent: cloudAgent, expectedState: .PresentationVerified) + } + + @Step("{actor} offers a credential") + var cloudAgentOffersACredential = { (cloudAgent: Actor) in + try await CloudAgentWorkflow.offersACredential(cloudAgent: cloudAgent) + } + + @Step("{actor} should see the credential was accepted") + var cloudAgentShouldSeeTheCredentialWasAccepted = { (cloudAgent: Actor) in + let recordId: String = try cloudAgent.recall(key: "recordId") + try await CloudAgentWorkflow.verifyCredentialState(cloudAgent: cloudAgent, recordId: recordId, expectedState: .CredentialSent) + } + + @Step("{actor} should see all credentials were accepted") + var cloudAgentSeeAllCredentialsWereAccepted = { (cloudAgent: Actor) in + let recordIdList: [String] = try cloudAgent.recall(key: "recordIdList") + for recordId in recordIdList { + try await CloudAgentWorkflow.verifyCredentialState(cloudAgent: cloudAgent, recordId: recordId, expectedState: .CredentialSent) + } + } + + @Step("{actor} is connected to {actor}") + var cloudAgentIsConnectedToEdgeAgent = { (cloudAgent: Actor, edgeAgent: Actor) in + try await CloudAgentWorkflow.isConnectedToEdgeAgent(cloudAgent: cloudAgent, edgeAgent: edgeAgent) + } + + @Step("{actor} has a connection invitation") + var cloudAgentHasAConnectionInvitation = { (cloudAgent: Actor) in + try await CloudAgentWorkflow.hasAConnectionInvitation(cloudAgent: cloudAgent) + } + + @Step("{actor} shares invitation to {actor}") + var cloudAgentSharesInvitationToEdgeAgent = { (cloudAgent: Actor, edgeAgent: Actor) in + try await CloudAgentWorkflow.sharesInvitationToEdgeAgent(cloudAgent: cloudAgent, edgeAgent: edgeAgent) + } + + @Step("{actor} should have the connection status updated to '{}'") + var cloudAgentShouldHaveTheConnectionStatusUpdatedToConnectionResponseSent = { (cloudAgent: Actor, state: String) in + try await CloudAgentWorkflow.shouldHaveTheConnectionStatusUpdated(cloudAgent: cloudAgent, expectedState: .ConnectionResponseSent) + } +} diff --git a/E2E/e2eTests/Source/Steps/EdgeAgentSteps.swift b/E2E/e2eTests/Source/Steps/EdgeAgentSteps.swift new file mode 100644 index 00000000..18476efb --- /dev/null +++ b/E2E/e2eTests/Source/Steps/EdgeAgentSteps.swift @@ -0,0 +1,73 @@ +import Foundation + +enum Riri: Error { + case Riri(message: String) +} +class EdgeAgentSteps: Steps { + @Step("{actor} sends the present-proof") + var edgeAgentSendsThePresentProof = { (edgeAgent: Actor) in + try await EdgeAgentWorkflow.waitForProofRequest(edgeAgent: edgeAgent) + try await EdgeAgentWorkflow.presentProof(edgeAgent: edgeAgent) + } + + @Step("{actor} has {int} credentials issued by {actor}") + var edgeAgentHasCredentialsIssuedByCloudAgent = { (edgeAgent: Actor, numberOfCredentials: Int, cloudAgent: Actor) in + try await EdgeAgentWorkflow.hasIssuedCredentials(edgeAgent: edgeAgent, numberOfCredentialsIssued: numberOfCredentials, cloudAgent: cloudAgent) + } + + @Step("{actor} accepts {int} credential offer sequentially from {actor}") + var edgeAgentAcceptsCredentialsOfferSequentiallyFromCloudAgent = { (edgeAgent: Actor, numberOfCredentials: Int, cloudAgent: Actor) in +// throw TestFramework.Failure.parameterTypeNotFound + var recordIdList: [String] = [] + for _ in 0.. String { + fatalError("Set feature title") + } + + func description() -> String { + return "" + } + + /// our lifecycle starts after xctest is ending + override func tearDown() async throws { + try await run() + try await super.tearDown() + } + + override class func tearDown() { + // signal end of feature + TestConfiguration.shared().endCurrentFeature() + } + + func run() async throws { + // check if we have the scenario + if (currentScenario == nil) { + fatalError(""" + To run the feature you have to setup the scenario for each test case. + Usage: + func testMyScenario() async throws { + scenario = Scenario("description") + .given // ... + } + """) + } + + try await TestConfiguration.setUpInstance() + try await TestConfiguration.shared().run(self, currentScenario!) + } +} diff --git a/E2E/e2eTests/TestFramework/BDD/Scenario.swift b/E2E/e2eTests/TestFramework/BDD/Scenario.swift new file mode 100644 index 00000000..de7925d6 --- /dev/null +++ b/E2E/e2eTests/TestFramework/BDD/Scenario.swift @@ -0,0 +1,63 @@ +import Foundation +import XCTest + +class Scenario { + let id = UUID().uuidString + var title: String + var steps: [ConcreteStep] = [] + var pass: Bool = false + var error: Error? = nil + + private var lastContext: String = "" + + init(_ title: String) { + self.title = title + } + + func fail(file: StaticString?, line: UInt?, message: String) { + if (file != nil) { + XCTFail(message, file: file!, line: line!) + } else { + XCTFail(message) + } + } + + private func addStep(_ step: String) { + let stepInstance = ConcreteStep() + stepInstance.context = lastContext + stepInstance.action = step + steps.append(stepInstance) + } + + func given(_ step: String) -> Scenario { + lastContext = "Given" + addStep(step) + return self + } + + func when(_ step: String) -> Scenario { + lastContext = "When" + addStep(step) + return self + } + + func then(_ step: String) -> Scenario { + lastContext = "Then" + addStep(step) + return self + } + + func but(_ step: String) -> Scenario { + lastContext = "But" + addStep(step) + return self + } + + func and(_ step: String) -> Scenario { + if (lastContext.isEmpty) { + fatalError("Trying to add an [and] step without previous context.") + } + addStep(step) + return self + } +} diff --git a/E2E/e2eTests/TestFramework/BDD/Step.swift b/E2E/e2eTests/TestFramework/BDD/Step.swift new file mode 100644 index 00000000..0640ff59 --- /dev/null +++ b/E2E/e2eTests/TestFramework/BDD/Step.swift @@ -0,0 +1,23 @@ + +import Foundation + +@propertyWrapper +class Step { + let file: StaticString + let line: UInt + var definition: String + var callback: (T) async throws -> () + var wrappedValue: () async throws -> () { + get { + return {} + } + } + + init(wrappedValue: @escaping (T) async throws -> (), _ definition: String, file: StaticString = #file, line: UInt = #line) { + self.file = file + self.line = line + self.callback = wrappedValue + self.definition = definition + StepRegistry.addStep(self) + } +} diff --git a/E2E/e2eTests/TestFramework/Configuration/ClassLocator.swift b/E2E/e2eTests/TestFramework/Configuration/ClassLocator.swift new file mode 100644 index 00000000..d7de7941 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Configuration/ClassLocator.swift @@ -0,0 +1,44 @@ +import Foundation + +class ClassLocator { + public static func allClasses() -> [AnyClass] { + let numberOfClasses = Int(objc_getClassList(nil, 0)) + if numberOfClasses > 0 { + let classesPtr = UnsafeMutablePointer.allocate(capacity: numberOfClasses) + let autoreleasingClasses = AutoreleasingUnsafeMutablePointer(classesPtr) + let count = objc_getClassList(autoreleasingClasses, Int32(numberOfClasses)) + assert(numberOfClasses == count) + defer { classesPtr.deallocate() } + let classes = (0 ..< numberOfClasses).map { classesPtr[$0] } + return classes + } + return [] + } + + public static func subclasses(of `class`: AnyClass) -> [AnyClass] { + return self.allClasses().filter { + var ancestor: AnyClass? = $0 + while let type = ancestor { + if ObjectIdentifier(type) == ObjectIdentifier(`class`) { return true } + ancestor = class_getSuperclass(type) + } + return false + } + } + + public static func classes(conformToProtocol `protocol`: Protocol) -> [AnyClass] { + let classes = self.allClasses().filter { aClass in + var subject: AnyClass? = aClass + while let aClass = subject { + if class_conformsToProtocol(aClass, `protocol`) { return true } + subject = class_getSuperclass(aClass) + } + return false + } + return classes + } + + public static func classes(conformTo: T.Type) -> [AnyClass] { + return self.allClasses().filter { $0 is T } + } +} diff --git a/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift b/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift new file mode 100644 index 00000000..e6a24554 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift @@ -0,0 +1,33 @@ +import Foundation + +protocol ITestConfiguration { + static var shared: () -> ITestConfiguration {get} + static func createInstance() -> ITestConfiguration + + func run(_ feature: Feature, _ currentScenario: Scenario) async throws + + /// setup + func setUp() async throws + + /// teardown + func tearDown() async throws + + /// phases + func beforeFeature(_ feature: Feature) async throws + func beforeScenario(_ scenario: Scenario) async throws + func beforeStep(_ step: ConcreteStep) + func afterStep(_ stepOutcome: StepOutcome) + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws + func afterFeature(_ featureOutcome: FeatureOutcome) async throws + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws + + func endCurrentFeature() + func end() + + /// methods + func createActors() async throws -> [Actor] + func createReporters() async throws -> [Reporter] + func report(_ phase: Phase, _ object: Any) + func targetDirectory() -> URL + +} diff --git a/E2E/e2eTests/TestFramework/Configuration/ParameterParser.swift b/E2E/e2eTests/TestFramework/Configuration/ParameterParser.swift new file mode 100644 index 00000000..2554bfa3 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Configuration/ParameterParser.swift @@ -0,0 +1,25 @@ + +import Foundation + +@propertyWrapper +struct ParameterParser { + var wrappedValue: (String) async throws -> T + + init(wrappedValue: @escaping (String) async throws -> T) { + self.wrappedValue = wrappedValue + ParserRegistry.addParser(wrappedValue) + } +} + +struct ParserRegistry { + static var annotatedMethods: [String: (String) async throws -> Any] = [:] + + static func getParser(_ name: String) async throws -> (String) async throws -> T { + return ParserRegistry.annotatedMethods[name] as! (String) async throws -> T + } + + static func addParser(_ callback: @escaping (String) async throws -> T) { + let type = String(describing: T.self).uppercased() + ParserRegistry.annotatedMethods[type] = callback + } +} diff --git a/E2E/e2eTests/TestFramework/Configuration/Phase.swift b/E2E/e2eTests/TestFramework/Configuration/Phase.swift new file mode 100644 index 00000000..d99d6abd --- /dev/null +++ b/E2E/e2eTests/TestFramework/Configuration/Phase.swift @@ -0,0 +1,12 @@ +import Foundation + +enum Phase { + case AFTER_FEATURES + case BEFORE_FEATURE + case AFTER_FEATURE + case BEFORE_SCENARIO + case AFTER_SCENARIO + case BEFORE_STEP + case AFTER_STEP + case ACTION +} diff --git a/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift b/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift new file mode 100644 index 00000000..66af4708 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift @@ -0,0 +1,320 @@ +import Foundation +import XCTest +import SwiftHamcrest + +class TestConfiguration: ITestConfiguration { + static var shared = { instance! } + + let environment: [String: String] = { readEnvironmentVariables() }() + + private static var instance: ITestConfiguration? = nil + private static var actors: [String: Actor] = [:] + + private var assertionFailure: (String, StaticString, UInt)? = nil + private var reporters: [Reporter] = [] + + private var result: ResultOutcome = ResultOutcome() + private var features: [Feature.Type] = [] + private var steps: [Steps] = [] + + private var currentFeatureOutcome: FeatureOutcome? = nil + private var currentScenario: Scenario? = nil + + class func createInstance() -> ITestConfiguration { + fatalError("Configuration must implement createInstance method") + } + + func targetDirectory() -> URL { + fatalError("Configuration must implement targetDirectory method") + } + + func createActors() async throws -> [Actor] { + fatalError("Configuration must implement createActors method") + } + + func setUp() async throws { + fatalError("Configuration must implement setUp method") + } + + func tearDown() async throws { + fatalError("Configuration must implement tearDown method") + } + + @MainActor + static func setUpInstance() async throws { + if (instance == nil) { + XCTestObservationCenter.shared.addTestObserver(TestObserver()) + } + try await setUpConfigurationInstance() + + } + + /// Refresh for each feature + func tearDownInstance() async throws { + try await tearDownSteps() + try await tearDownActors() + try await tearDownConfigurationInstance() + } + + func createReporters() async throws -> [Reporter] { + return [JunitReporter(), HtmlReporter(), DotReporter()] + } + + /// Main function that runs feature, scenario and steps + func run(_ feature: Feature, _ scenario: Scenario) async throws { + currentScenario = scenario + try await beforeFeature(feature) + try await beforeScenario(scenario) + let scenarioOutcome = try await runSteps(scenario) + try await afterScenario(scenarioOutcome) + } + + func beforeFeature(_ feature: Feature) async throws { + let type: Feature.Type = type(of: feature) + + if (features.contains(where: { $0 == type })) { + return + } + + features.append(type) + currentFeatureOutcome = FeatureOutcome(feature) + result.featuresOutcome.append(currentFeatureOutcome!) + + report(.BEFORE_FEATURE, feature) + } + + func beforeScenario(_ scenario: Scenario) async throws { + report(.BEFORE_SCENARIO, scenario) + } + + func beforeStep(_ step: ConcreteStep) { + report(.BEFORE_STEP, step.action) + } + + func runSteps(_ scenario: Scenario) async throws -> ScenarioOutcome { + let scenarioOutcome = ScenarioOutcome(scenario) + + for step in scenario.steps { + let stepOutcome: StepOutcome + report(.BEFORE_STEP, step) + + do { + try await StepRegistry.run(step) + if (assertionFailure != nil) { + let message = assertionFailure!.0 + let file = assertionFailure!.1 + let line = assertionFailure!.2 + XCTFail(message, file: file, line: line) + throw Assertion.AssertionError( + message: message, + file: file, + line: line + ) + } + stepOutcome = StepOutcome(step) + } catch { + stepOutcome = StepOutcome(step, error) + currentScenario!.fail(file: step.file, line: step.line, message: String(describing: error)) + } + + scenarioOutcome.steps.append(stepOutcome) + report(.AFTER_STEP, stepOutcome) + assertionFailure = nil + + if (stepOutcome.error != nil) { + scenarioOutcome.failedStep = stepOutcome + break + } + } + return scenarioOutcome + } + + func afterStep(_ stepOutcome: StepOutcome) { + report(.AFTER_STEP, stepOutcome.step.action) + } + + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws { + currentFeatureOutcome!.scenarios.append(scenarioOutcome) + if (scenarioOutcome.failedStep != nil) { + currentFeatureOutcome!.failedScenarios.append(scenarioOutcome) + } + report(.AFTER_SCENARIO, scenarioOutcome) + } + + func afterFeature(_ featureOutcome: FeatureOutcome) async throws { + report(.AFTER_FEATURE, featureOutcome) + } + + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws { + report(.AFTER_FEATURES, featuresOutcome) + } + + private func unsafeSync(_ f: @escaping () async -> ()) { + let semaphore = DispatchSemaphore(value: 0) + Task.init { + await f() + semaphore.signal() + } + semaphore.wait() + } + + func endCurrentFeature() { + unsafeSync { + try! await self.afterFeature(self.currentFeatureOutcome!) + } + } + + func end() { + unsafeSync { + try! await self.afterFeatures(self.result.featuresOutcome) + try! await self.tearDownInstance() + } + // TODO: throw exception if it fails + } + + func report(_ phase: Phase, _ object: Any) { + self.reporters.forEach { reporter in + unsafeSync { + switch(phase) { + case .BEFORE_FEATURE: + try! await reporter.beforeFeature(object as! Feature) + case .AFTER_FEATURE: + try! await reporter.afterFeature(object as! FeatureOutcome) + case .BEFORE_SCENARIO: + try! await reporter.beforeScenario(object as! Scenario) + case .AFTER_SCENARIO: + try! await reporter.afterScenario(object as! ScenarioOutcome) + case .BEFORE_STEP: + try! await reporter.beforeStep(object as! ConcreteStep) + case .AFTER_STEP: + try! await reporter.afterStep(object as! StepOutcome) + case .ACTION: + try! await reporter.action(object as! String) + case .AFTER_FEATURES: + try! await reporter.afterFeatures(object as! [FeatureOutcome]) + } + } + } + } + + private func setUpActors() async throws { + let actors = try await createActors() + for actor in actors { + TestConfiguration.actors[actor.name] = actor + try await actor.initialize() + } + } + + private func tearDownActors() async throws { + for actor in TestConfiguration.actors.values { + try await actor.tearDown() + } + } + + private func setUpSteps() async throws { + let subclasses = ClassLocator.subclasses(of: Steps.self) + for subclass in subclasses { + if (subclass != Steps.self) { + steps.append(try await (subclass as! Steps.Type).init()) + } + } + } + + private func tearDownSteps() async throws { + for step in steps { + try await step.tearDown() + } + } + + private static func setUpConfigurationInstance() async throws { + if (self.instance != nil) { + return + } + + let subclasses = ClassLocator.subclasses(of: TestConfiguration.self).filter { $0 != TestConfiguration.self } + if (subclasses.count == 0) { + fatalError("No configuration class found. Create a class that extends CucumberConfig class.") + } + if (subclasses.count > 1) { + fatalError("More than 1 configuration class found.") + } + + let instanceType = (subclasses[0] as! ITestConfiguration.Type) + + // force as own instance + let instance = instanceType.createInstance() as! TestConfiguration + + try await instance.setUp() + try await instance.setUpReporters() + try await instance.setUpSteps() + try await instance.setUpActors() + + /// setup hamcrest to update variable if failed + HamcrestReportFunction = { message, file, line in + instance.assertionFailure = (message, file, line) + } + + self.instance = instance + + let fileManager = FileManager.default + /// delete target folder + do { + try fileManager.removeItem(at: instance.targetDirectory()) + } catch { + } + /// recreate it + do { + try fileManager.createDirectory(at: instance.targetDirectory(), withIntermediateDirectories: true, attributes: nil) + } catch { + } + } + + private func setUpReporters() async throws { + reporters = try await createReporters() + } + + private func tearDownConfigurationInstance() async throws { + try await tearDown() + } + + private static func readEnvironmentVariables() -> [String: String] { + var environment: [String: String] = [:] + // load property file + if let path = Bundle.module.path(forResource: "properties", ofType: "plist", inDirectory: "Resources") { + if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) { + if let dictionary = try? (PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [String: String]) { + dictionary.forEach { + environment[$0.key] = $0.value + } + } + } + } + // overrides if any environment variable is available + ProcessInfo.processInfo.environment.forEach { + environment[$0.key] = $0.value + } + return environment + } + + /// Default parsers + @ParameterParser + var actorParser = { (actor: String) in + return actors[actor]! + } + + @ParameterParser + var stringParser = { (string: String) in + return string + } + + @ParameterParser + var intParser = { (int: String) in + return Int(int)! + } + + enum Failure: Error { + case StepParameterDoesNotMatch(step: String, expected: String, actual: String) + case StepNotFound(step: String) + case ParameterTypeNotFound + } +} diff --git a/E2E/e2eTests/TestFramework/Outcome/FeatureOutcome.swift b/E2E/e2eTests/TestFramework/Outcome/FeatureOutcome.swift new file mode 100644 index 00000000..6060d5a5 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Outcome/FeatureOutcome.swift @@ -0,0 +1,11 @@ +import Foundation + +class FeatureOutcome { + let feature: Feature + var scenarios: [ScenarioOutcome] = [] + var failedScenarios: [ScenarioOutcome] = [] + + init(_ feature: Feature) { + self.feature = feature + } +} diff --git a/E2E/e2eTests/TestFramework/Outcome/ResultOutcome.swift b/E2E/e2eTests/TestFramework/Outcome/ResultOutcome.swift new file mode 100644 index 00000000..c7dbf6f9 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Outcome/ResultOutcome.swift @@ -0,0 +1,5 @@ +import Foundation + +class ResultOutcome { + var featuresOutcome: [FeatureOutcome] = [] +} diff --git a/E2E/e2eTests/TestFramework/Outcome/ScenarioOutcome.swift b/E2E/e2eTests/TestFramework/Outcome/ScenarioOutcome.swift new file mode 100644 index 00000000..3e1aad55 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Outcome/ScenarioOutcome.swift @@ -0,0 +1,11 @@ +import Foundation + +class ScenarioOutcome { + let scenario: Scenario + var steps: [StepOutcome] = [] + var failedStep: StepOutcome? = nil + + init(_ scenario: Scenario) { + self.scenario = scenario + } +} diff --git a/E2E/e2eTests/TestFramework/Outcome/StepOutcome.swift b/E2E/e2eTests/TestFramework/Outcome/StepOutcome.swift new file mode 100644 index 00000000..cee4f7fb --- /dev/null +++ b/E2E/e2eTests/TestFramework/Outcome/StepOutcome.swift @@ -0,0 +1,11 @@ +import Foundation + +class StepOutcome { + let step: ConcreteStep + var error: Error? + + init(_ step: ConcreteStep, _ error: Error? = nil) { + self.step = step + self.error = error + } +} diff --git a/E2E/e2eTests/TestFramework/Report/ConsoleReporter.swift b/E2E/e2eTests/TestFramework/Report/ConsoleReporter.swift new file mode 100644 index 00000000..1ac18fed --- /dev/null +++ b/E2E/e2eTests/TestFramework/Report/ConsoleReporter.swift @@ -0,0 +1,45 @@ +import Foundation + +class ConsoleReporter: Reporter { + private let pass = "(✔)" + private let fail = "(✘)" + + private var actions: [String] = [] + + func beforeFeature(_ feature: Feature) async throws { + print() + print("Feature:", feature.title()) + } + + func beforeScenario(_ scenario: Scenario) async throws { + print(" ", scenario.title) + } + + func beforeStep(_ step: ConcreteStep) async throws { + } + + func action(_ action: String) async throws { + actions.append(action) + } + + func afterStep(_ stepOutcome: StepOutcome) async throws { + let result = stepOutcome.error != nil ? fail : pass + print(" ", result, stepOutcome.step.action) + actions.forEach { action in + print(" ", action) + } + actions = [] + } + + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws { + let result = scenarioOutcome.failedStep != nil ? "FAIL" : "PASS" + print(" ", "Result:", result) + } + + func afterFeature(_ featureOutcome: FeatureOutcome) async throws { + print() + } + + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws { + } +} diff --git a/E2E/e2eTests/TestFramework/Report/DotReporter.swift b/E2E/e2eTests/TestFramework/Report/DotReporter.swift new file mode 100644 index 00000000..9377c73b --- /dev/null +++ b/E2E/e2eTests/TestFramework/Report/DotReporter.swift @@ -0,0 +1,52 @@ +import Foundation + +class DotReporter: Reporter { + private func printDot() { + print(".", terminator: "") + } + func beforeFeature(_ feature: Feature) async throws { + printDot() + } + + func beforeScenario(_ scenario: Scenario) async throws { + printDot() + } + + func beforeStep(_ step: ConcreteStep) async throws { + printDot() + } + + func action(_ action: String) async throws { + printDot() + } + + func afterStep(_ stepOutcome: StepOutcome) async throws { + printDot() + } + + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws { + print() + } + + func afterFeature(_ featureOutcome: FeatureOutcome) async throws { + } + + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws { + print("Executed", featuresOutcome.count, "features") + for featureOutcome in featuresOutcome { + print(" ", "Feature:", featureOutcome.feature.title()) + for scenarioOutcome in featureOutcome.scenarios { + print( + " ", + scenarioOutcome.failedStep != nil ? "(fail)" : "(pass)", + scenarioOutcome.scenario.title + ) + if (scenarioOutcome.failedStep != nil) { + let failedStep = scenarioOutcome.failedStep! + print(" ", failedStep.error!) + print(" at step: \"\(failedStep.step.action)\"") + } + } + } + } +} diff --git a/E2E/e2eTests/TestFramework/Report/HtmlReporter.swift b/E2E/e2eTests/TestFramework/Report/HtmlReporter.swift new file mode 100644 index 00000000..59858847 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Report/HtmlReporter.swift @@ -0,0 +1,88 @@ +import Foundation + +class HtmlReporter: Reporter { + private let pass = "(✔)" + private let fail = "(✘)" + + private var currentFeature: Feature? = nil + private var currentScenario: Scenario? = nil + private var currentStep: ConcreteStep? = nil + private var currentId: String? = nil + + private var actions: [String: [String]] = [:] + + func beforeFeature(_ feature: Feature) async throws { + currentFeature = feature + } + + func beforeScenario(_ scenario: Scenario) async throws { + currentScenario = scenario + } + + func beforeStep(_ step: ConcreteStep) async throws { + currentStep = step + currentId = currentFeature!.id + currentScenario!.id + step.id + } + + func action(_ action: String) async throws { + if (actions[currentId!] == nil) { + actions[currentId!] = [] + } + actions[currentId!]!.append(action) + } + + func afterStep(_ stepOutcome: StepOutcome) async throws { + currentStep = nil + } + + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws { + currentScenario = nil + } + + func afterFeature(_ featureOutcome: FeatureOutcome) async throws { + currentFeature = nil + } + + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws { + + var summary = "" + summary.append("Executed \(featuresOutcome.count) features\n") + + for featureOutcome in featuresOutcome { + summary.append(" Feature: \(featureOutcome.feature.title())\n") + + for scenarioOutcome in featureOutcome.scenarios { + summary.append(" Scenario: \(scenarioOutcome.scenario.title)\n") + + for stepOutcome in scenarioOutcome.steps { + if (stepOutcome.error != nil) { + summary.append(" \(fail) \(stepOutcome.step.action)\n") + } else { + summary.append(" \(pass) \(stepOutcome.step.action)\n") + } + let stepId = featureOutcome.feature.id + scenarioOutcome.scenario.id + stepOutcome.step.id + if let stepActions = actions[stepId] { + for action in stepActions { + summary.append(" \(action)\n") + } + } + if (stepOutcome.error != nil) { + summary.append(" caused by: \(String(describing: scenarioOutcome.failedStep!.error!))\n") + } + } + + if (scenarioOutcome.failedStep != nil) { + summary.append(" Status: FAILED\n") + } else { + summary.append(" Status: SUCCESS\n") + } + + summary.append("\n") + + } + } + + let outputPath = TestConfiguration.shared().targetDirectory().appendingPathComponent("result.txt") + try summary.write(to: outputPath, atomically: true, encoding: .utf8) + } +} diff --git a/E2E/e2eTests/TestFramework/Report/JunitReporter.swift b/E2E/e2eTests/TestFramework/Report/JunitReporter.swift new file mode 100644 index 00000000..492cfd18 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Report/JunitReporter.swift @@ -0,0 +1,121 @@ +import Foundation + +class JunitReporter: Reporter { + let root = XMLElement(name: "testsuites") + let xml: XMLDocument + + var currentFeature = XMLElement(name: "testsuite") + var currentScenario = XMLElement(name: "testcase") + + let testSuitesStart = Date() + var featureStart = Date() + var scenarioStart = Date() + + var totalTests: Int = 0 + var totalFailures: Int = 0 + var featureTests: Int = 0 + var featureFailures: Int = 0 + + init() { + xml = XMLDocument(rootElement: root) + xml.version = "1.0" + xml.characterEncoding = "UTF-8" + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyyMMdd_HHmmss" + + let currentDate = Date() + let formattedDate = dateFormatter.string(from: currentDate) + + let id = XMLNode.attribute(withName: "id", stringValue: formattedDate) as! XMLNode + let name = XMLNode.attribute(withName: "name", stringValue: "swift-e2e-results - \(Date().formatted())") as! XMLNode + + root.addAttribute(id) + root.addAttribute(name) + } + + func beforeFeature(_ feature: Feature) async throws { + featureStart = Date() + currentFeature = XMLElement(name: "testsuite") + featureTests = 0 + featureFailures = 0 + + let id = XMLNode.attribute(withName: "id", stringValue: feature.id) as! XMLNode + let name = XMLNode.attribute(withName: "name", stringValue: feature.title()) as! XMLNode + + currentFeature.addAttribute(id) + currentFeature.addAttribute(name) + + root.addChild(currentFeature) + } + + func beforeScenario(_ scenario: Scenario) async throws { + scenarioStart = Date() + currentScenario = XMLElement(name: "testcase") + + let id = XMLNode.attribute(withName: "id", stringValue: scenario.id) as! XMLNode + let name = XMLNode.attribute(withName: "name", stringValue: scenario.title) as! XMLNode + + currentScenario.addAttribute(id) + currentScenario.addAttribute(name) + + currentFeature.addChild(currentScenario) + } + + func beforeStep(_ step: ConcreteStep) async throws { + } + + func action(_ action: String) async throws { + } + + func afterStep(_ stepOutcome: StepOutcome) async throws { + } + + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws { + featureTests += 1 + let delta = String(format: "%.4f seconds", Date().timeIntervalSince(scenarioStart)) + let time = XMLNode.attribute(withName: "time", stringValue: delta) as! XMLNode + currentScenario.addAttribute(time) + + if (scenarioOutcome.failedStep != nil) { + let failure = XMLElement(name: "failure") + featureFailures += 1 + let message = XMLNode.attribute(withName: "message", stringValue: String(describing: scenarioOutcome.failedStep!.error!)) as! XMLNode + let type = XMLNode.attribute(withName: "type", stringValue: "ERROR") as! XMLNode + failure.addAttribute(message) + failure.addAttribute(type) + + currentScenario.addChild(failure) + } + } + + func afterFeature(_ featureOutcome: FeatureOutcome) async throws { + let delta = String(format: "%.4f seconds", Date().timeIntervalSince(featureStart)) + let time = XMLNode.attribute(withName: "time", stringValue: delta) as! XMLNode + let tests = XMLNode.attribute(withName: "tests", stringValue: String(featureTests)) as! XMLNode + let failures = XMLNode.attribute(withName: "failures", stringValue: String(featureFailures)) as! XMLNode + currentFeature.addAttribute(time) + currentFeature.addAttribute(tests) + currentFeature.addAttribute(failures) + + totalTests += featureTests + totalFailures += featureFailures + } + + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws { + let delta = String(format: "%.4f seconds", Date().timeIntervalSince(testSuitesStart)) + let time = XMLNode.attribute(withName: "time", stringValue: delta) as! XMLNode + let tests = XMLNode.attribute(withName: "tests", stringValue: String(totalTests)) as! XMLNode + let failures = XMLNode.attribute(withName: "failures", stringValue: String(totalFailures)) as! XMLNode + + root.addAttribute(time) + root.addAttribute(tests) + root.addAttribute(failures) + + let outputPath = TestConfiguration.shared().targetDirectory().appendingPathComponent("junit.xml") + let prettyPrintOptions: XMLNode.Options = [.nodePrettyPrint] + let prettyPrintedData = xml.xmlData(options: prettyPrintOptions) + let summary = String(data: prettyPrintedData, encoding: .utf8) + try summary!.write(to: outputPath, atomically: true, encoding: .utf8) + } +} diff --git a/E2E/e2eTests/TestFramework/Report/Reporter.swift b/E2E/e2eTests/TestFramework/Report/Reporter.swift new file mode 100644 index 00000000..6e0b5930 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Report/Reporter.swift @@ -0,0 +1,12 @@ +import Foundation + +protocol Reporter { + func beforeFeature(_ feature: Feature) async throws + func beforeScenario(_ scenario: Scenario) async throws + func beforeStep(_ step: ConcreteStep) async throws + func action(_ action: String) async throws + func afterStep(_ stepOutcome: StepOutcome) async throws + func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws + func afterFeature(_ featureOutcome: FeatureOutcome) async throws + func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws +} diff --git a/E2E/e2eTests/TestFramework/Report/TestObserver.swift b/E2E/e2eTests/TestFramework/Report/TestObserver.swift new file mode 100644 index 00000000..3edf2f51 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Report/TestObserver.swift @@ -0,0 +1,8 @@ +import Foundation +import XCTest + +class TestObserver: NSObject, XCTestObservation { + func testBundleDidFinish(_ testBundle: Bundle) { + TestConfiguration.shared().end() + } +} diff --git a/E2E/e2eTests/TestFramework/Runner/ConcreteStep.swift b/E2E/e2eTests/TestFramework/Runner/ConcreteStep.swift new file mode 100644 index 00000000..3a0a57a3 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Runner/ConcreteStep.swift @@ -0,0 +1,20 @@ +import Foundation + +/** + Concrete step + */ +class ConcreteStep { + let id: String + var context: String = "" + var action: String = "" + var line: UInt? = nil + var file: StaticString? = nil + + init() { + id = UUID().uuidString + } + + static func == (lhs: ConcreteStep, rhs: ConcreteStep) -> Bool { + return lhs.context == rhs.context && lhs.action == rhs.action + } +} diff --git a/E2E/e2eTests/TestFramework/Runner/StepRegistry.swift b/E2E/e2eTests/TestFramework/Runner/StepRegistry.swift new file mode 100644 index 00000000..57eef102 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Runner/StepRegistry.swift @@ -0,0 +1,82 @@ +import Foundation + +struct StepRegistry { + + static var runnableSteps: [String : StepRunner] = [:] + + static func run(_ concreteStep: ConcreteStep) async throws { + let action = concreteStep.action + var parameters: [String] = [] + var matchedStep: String? + + for stepMatcher in runnableSteps.keys { + if let regex = try? NSRegularExpression(pattern: stepMatcher, options: []) { + let range = NSRange(action.startIndex..., in: action) + regex.enumerateMatches(in: action, options: [], range: range) { (match, _, _) in + if let match = match { + matchedStep = stepMatcher + for i in 1..(_ step: Step) { + let runnableStep = StepRunner(step) + runnableSteps[runnableStep.stepMatcher] = runnableStep + } +} diff --git a/E2E/e2eTests/TestFramework/Runner/StepRunner.swift b/E2E/e2eTests/TestFramework/Runner/StepRunner.swift new file mode 100644 index 00000000..eb0dc2b4 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Runner/StepRunner.swift @@ -0,0 +1,87 @@ +import Foundation + +class StepRunner { + private let callback: (Any) async throws -> () + let stepDefinition: String + var stepMatcher: String + var parsers: [(String) async throws -> Any] + var stepLine: UInt + var stepFile: StaticString + private static var parameterPattern = "\\{([^}]*)\\}" + private static var specialCharacters = [".", "*", "+", "?", "^", "$", "[", "]", "|", "(", ")", "\\"] + + init(_ step: Step) { + self.stepLine = step.line + self.stepFile = step.file + self.stepDefinition = step.definition + + self.callback = { input in + guard let typedInput = input as? T else { + let mirror = Mirror(reflecting: input) + let actualType: String = + if (String(describing: mirror.displayStyle!) == "tuple") { + "(" + mirror.children.map { String(describing: type(of: $0.value)) }.joined(separator: ", ") + ")" + } else { + String(describing: mirror.subjectType) + } + + throw TestConfiguration.Failure.StepParameterDoesNotMatch( + step: step.definition, + expected: String(describing: T.self), + actual: String(describing: actualType) + ) + } + return try await step.callback(typedInput) + } + self.stepMatcher = StepRunner.createMatcher(stepDefinition) + self.parsers = StepRunner.createParsers(stepDefinition) + } + + private static func createMatcher(_ stepDefinition: String) -> String { + let sanitizedString = sanitizeString(stepDefinition) + return sanitizedString.replacingOccurrences(of: StepRunner.parameterPattern, with: "(.*)", options: .regularExpression) + } + + private static func sanitizeString(_ stepDefinition: String) -> String { + return stepDefinition.map { char -> String in + if specialCharacters.contains(String(char)) { + return "\\" + String(char) + } else { + return String(char) + } + }.joined() + } + + private static func createParsers(_ stepDefinition: String) -> [(String) async throws -> Any] { + var parsers: [(String) async throws -> Any] = [] + for parser in getStepParameterTypes(from: stepDefinition) { + let method = ParserRegistry.annotatedMethods[parser.uppercased()]! + parsers.append(method) + } + return parsers + } + + private static func getStepParameterTypes(from text: String) -> [String] { + do { + let regex = try NSRegularExpression(pattern: StepRunner.parameterPattern, options: []) + let nsString = NSString(string: text) + let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length)) + + return matches.map { match in + let range = match.range(at: 1) + if range.location != NSNotFound { + let parser = nsString.substring(with: range) + return parser.isEmpty ? "string" : parser + } else { + return "string" + } + } + } catch { + fatalError("Unable to get parameters type") + } + } + + func invoke(parameters: T) async throws { + try await callback(parameters) + } +} diff --git a/E2E/e2eTests/TestFramework/Runner/Steps.swift b/E2E/e2eTests/TestFramework/Runner/Steps.swift new file mode 100644 index 00000000..ac22e247 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Runner/Steps.swift @@ -0,0 +1,12 @@ +import Foundation + +class Steps { + required init() async throws { + } + + func setUp() async throws { + } + + func tearDown() async throws { + } +} diff --git a/E2E/e2eTests/TestFramework/Screenplay/Ability.swift b/E2E/e2eTests/TestFramework/Screenplay/Ability.swift new file mode 100644 index 00000000..a187dd3f --- /dev/null +++ b/E2E/e2eTests/TestFramework/Screenplay/Ability.swift @@ -0,0 +1,20 @@ +import Foundation + +protocol Ability { + /// return interface for the ability + associatedtype AbilityInstanceType + + var abilityName: String {get} + var actor: Actor {get} + + init(_ actor: Actor) + + /// object instance returned by the ability + func instance() -> AbilityInstanceType + + /// initialization hook, used to create the object instance for ability + func setUp(_ actor: Actor) async throws + + /// teardown hook + func tearDown() async throws +} diff --git a/E2E/e2eTests/TestFramework/Screenplay/Actor.swift b/E2E/e2eTests/TestFramework/Screenplay/Actor.swift new file mode 100644 index 00000000..daabe3b3 --- /dev/null +++ b/E2E/e2eTests/TestFramework/Screenplay/Actor.swift @@ -0,0 +1,77 @@ +import Foundation +import XCTest + +class Actor { + var name: String + private var context: [String: Any] = [:] + private var abilities: [String : any Ability] = [:] + + init(_ name: String) { + self.name = name + } + + func initialize() async throws { + for ability in abilities.values { + try await ability.setUp(self) + } + } + + func tearDown() async throws { + for ability in abilities.values { + try await ability.tearDown() + } + } + + func whoCanUse(_ abilityType: T.Type) -> Actor { + let ability = abilityType.init(self) + abilities[String(describing: T.self)] = ability + return self + } + + private func getAbility(_ ability: T.Type) -> T { + return self.abilities[String(describing: ability.self)]! as! T + } + + func using(ability: T.Type, + action: String // = "executes an action" + ) throws -> T.AbilityInstanceType { + if !abilities.contains(where: { $0.key == String(describing: ability.self) }) { + throw ActorError.CantUseAbility("Actor [\(name)] don't have the ability to use [\(ability.self)]") + } + let ability = getAbility(ability) + TestConfiguration.shared().report(.ACTION, "\(name) \(action) using \(ability.abilityName)") + return ability.instance() + } + + func waitUsingAbility(ability: T.Type, + action: String, // = "an expectation is met", + callback: (_ ability: T.AbilityInstanceType) async throws -> Bool + ) async throws { + let ability = getAbility(ability) + TestConfiguration.shared().report(.ACTION, "\(name) waits until \(action) using \(ability.abilityName)") + return try await Wait.until { + try await callback(ability.instance()) + } + } + + func remember(key: String, value: Any) throws { + TestConfiguration.shared().report(.ACTION, "\(name) remembers [\(key)]") + context[key] = value + } + + func recall(key: String) throws -> T { + TestConfiguration.shared().report(.ACTION, "\(name) recalls [\(key)]") + XCTAssert(context[key] != nil, "Unable to recall [\(key)] all I know is \(context.keys)") + if (context[key] == nil) { + throw ActorError.CantFindNote("\(name) don't have any note named [\(key)]") + } + return context[key] as! T + } + + /// Here we could add attempsTo where actor can run actions, wait, etc +} + +enum ActorError : Error { + case CantUseAbility(_ message: String) + case CantFindNote(_ message: String) +} diff --git a/E2E/e2eTests/TestFramework/Screenplay/Wait.swift b/E2E/e2eTests/TestFramework/Screenplay/Wait.swift new file mode 100644 index 00000000..72c5fcba --- /dev/null +++ b/E2E/e2eTests/TestFramework/Screenplay/Wait.swift @@ -0,0 +1,21 @@ +import Foundation + +class Wait { + static func until(timeout: Int = 30, callback: () async throws -> Bool, file: StaticString = #file, line: UInt = #line) async throws { + let startTime = Date() + while try await !callback() { + if Date().timeIntervalSince(startTime) >= Double(timeout) { + throw Assertion.TimeoutError( + timeout: timeout, + file: file, + line: line + ) + } + try await Task.sleep(nanoseconds: UInt64(500000000)) + } + } +} + +enum TimeoutError: Error { + case timeoutReached +} diff --git a/E2E/e2eTests/openapi-generator-config.yaml b/E2E/e2eTests/openapi-generator-config.yaml new file mode 100644 index 00000000..ecefb471 --- /dev/null +++ b/E2E/e2eTests/openapi-generator-config.yaml @@ -0,0 +1,3 @@ +generate: + - types + - client diff --git a/E2E/e2eTests/openapi.yaml b/E2E/e2eTests/openapi.yaml new file mode 100644 index 00000000..9b991ad8 --- /dev/null +++ b/E2E/e2eTests/openapi.yaml @@ -0,0 +1,4579 @@ +openapi: 3.1.0 +info: + title: Prism Agent + version: 1.20.1 +paths: + /credential-definition-registry/definitions: + get: + tags: + - Credential Definition Registry + 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 ' + operationId: lookupCredentialDefinitionsByQuery + parameters: + - 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: tag + in: query + required: false + schema: + type: string + example: licence + - 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 + schema: + type: string + responses: + '200': + description: Collection of CredentialDefinitions records. + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialDefinitionResponsePage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Credential Definition Registry + summary: Publish new definition to the definition registry + 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. + operationId: createCredentialDefinition + requestBody: + description: JSON object required for the credential definition creation + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialDefinitionInput' + required: true + responses: + '201': + description: The new credential definition record is successfully created + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialDefinitionResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /credential-definition-registry/definitions/{guid}: + get: + tags: + - Credential Definition Registry + summary: Fetch the credential definition from the registry by `guid` + description: Fetch the credential definition by the unique identifier + operationId: getCredentialDefinitionById + parameters: + - name: guid + in: path + description: Globally unique identifier of the credential definition record + required: true + schema: + type: string + format: uuid + responses: + '200': + description: CredentialDefinition found by `guid` + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialDefinitionResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /credential-definition-registry/definitions/{guid}/definition: + get: + tags: + - Credential Definition Registry + 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 + operationId: getCredentialDefinitionInnerDefinitionById + parameters: + - name: guid + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: CredentialDefinition found by `guid` + content: + application/json: + schema: {} + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /schema-registry/schemas: + get: + tags: + - Schema Registry + summary: Lookup schemas by indexed fields + description: 'Lookup schemas by `author`, `name`, `tags` parameters and control + the pagination by `offset` and `limit` parameters ' + operationId: lookupSchemasByQuery + parameters: + - 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 + schema: + type: string + responses: + '200': + description: Collection of CredentialSchema records. + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialSchemaResponsePage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Schema Registry + summary: Publish new schema to the schema registry + 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. + operationId: createSchema + requestBody: + description: JSON object required for the credential schema creation + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialSchemaInput' + required: true + responses: + '201': + description: The new credential schema record is successfully created + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialSchemaResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /schema-registry/{author}/{id}: + put: + tags: + - Schema Registry + 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. + operationId: updateSchema + parameters: + - name: author + in: path + description: DID of the identity which authored the credential schema. A piece + of Metadata. + required: true + schema: + type: string + - name: id + in: path + description: A locally unique identifier to address the schema. UUID is generated + by the backend. + required: true + schema: + type: string + format: uuid + requestBody: + description: JSON object required for the credential schema update + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialSchemaInput' + required: true + responses: + '200': + description: The credential schema record is successfully updated + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialSchemaResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /schema-registry/schemas/{guid}: + get: + tags: + - Schema Registry + summary: Fetch the schema from the registry by `guid` + description: Fetch the credential schema by the unique identifier + operationId: getSchemaById + parameters: + - name: guid + in: path + description: Globally unique identifier of the credential schema record + required: true + schema: + type: string + format: uuid + responses: + '200': + description: CredentialSchema found by `guid` + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialSchemaResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /schema-registry/test: + get: + tags: + - Schema Registry + summary: Trace the request input from the point of view of the server + description: Trace the request input from the point of view of the server + operationId: test + responses: + '200': + description: '' + content: + application/json: + schema: + type: string + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /verification/policies: + get: + tags: + - Verification + summary: Lookup verification policies by query + description: Lookup verification policies by `name`, and control the pagination + by `offset` and `limit` parameters + operationId: lookupVerificationPoliciesByQuery + parameters: + - name: name + in: query + required: false + schema: + type: string + - 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 + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationPolicyPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Verification + summary: Create the new verification policy + description: Create the new verification policy + operationId: createVerificationPolicy + requestBody: + description: Create verification policy object + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationPolicyInput' + required: true + responses: + '201': + description: Created verification policy entity + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationPolicy' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /verification/policies/{id}: + get: + tags: + - Verification + summary: Fetch the verification policy by id + description: Get the verification policy by id + operationId: getVerificationPolicyById + parameters: + - name: id + in: path + description: Get the verification policy by id + required: true + schema: + type: string + format: uuid + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationPolicy' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + put: + tags: + - Verification + summary: Update the verification policy object by id + description: Update the verification policy entry + operationId: updateVerificationPolicy + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: nonce + in: query + description: Nonce of the previous VerificationPolicy + required: true + schema: + type: integer + format: int32 + requestBody: + description: Update verification policy object + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationPolicyInput' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/VerificationPolicy' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + delete: + tags: + - Verification + summary: Deleted the verification policy by id + description: Delete the verification policy by id + operationId: deleteVerificationPolicyById + parameters: + - name: id + in: path + description: Delete the verification policy by id + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Verification policy deleted successfully + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /connections: + get: + tags: + - Connections Management + summary: Gets the list of connection records. + description: Get the list of connection records paginated + operationId: getConnections + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + - name: thid + in: query + description: The thid of a DIDComm communication. + required: false + schema: + type: string + responses: + '200': + description: The list of connection records. + content: + application/json: + schema: + $ref: '#/components/schemas/ConnectionsPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Connections Management + summary: Creates a new connection record and returns an Out of Band invitation. + description: |2 + + Generates a new Peer DID and creates an [Out of Band 2.0](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) invitation. + It returns a new connection record in `InvitationGenerated` state. + The request body may contain a `label` that can be used as a human readable alias for the connection, for example `{'label': "Bob"}` + operationId: createConnection + requestBody: + description: JSON object required for the connection creation + content: + application/json: + schema: + $ref: '#/components/schemas/CreateConnectionRequest' + required: true + responses: + '201': + description: The connection record was created successfully, and is returned + in the response body. + content: + application/json: + schema: + $ref: '#/components/schemas/Connection' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /connections/{connectionId}: + get: + tags: + - Connections Management + summary: Gets an existing connection record by its unique identifier. + description: Gets an existing connection record by its unique identifier + operationId: getConnection + parameters: + - name: connectionId + in: path + description: The unique identifier of the connection record. + required: true + schema: + type: string + format: uuid + responses: + '200': + description: The connection record. + content: + application/json: + schema: + $ref: '#/components/schemas/Connection' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /connection-invitations: + post: + tags: + - Connections Management + summary: Accepts an Out of Band invitation. + description: |2 + + Accepts an [Out of Band 2.0](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) invitation, generates a new Peer DID, + and submits a Connection Request to the inviter. + It returns a connection object in `ConnectionRequestPending` state, until the Connection Request is eventually sent to the inviter by the prism-agent's background process. The connection object state will then automatically move to `ConnectionRequestSent`. + operationId: acceptConnectionInvitation + requestBody: + description: The request used by an invitee to accept a connection invitation + received from an inviter, using out-of-band mechanism. + content: + application/json: + schema: + $ref: '#/components/schemas/AcceptConnectionInvitationRequest' + required: true + responses: + '200': + description: The invitation was successfully accepted. + content: + application/json: + schema: + $ref: '#/components/schemas/Connection' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /dids/{didRef}: + get: + tags: + - DID + summary: Resolve Prism DID to a W3C representation + description: | + Resolve Prism DID to a W3C DID document representation. + The response can be the [DID resolution result](https://w3c-ccg.github.io/did-resolution/#did-resolution-result) + or [DID document representation](https://www.w3.org/TR/did-core/#representations) depending on the `Accept` request header. + The response is implemented according to [resolver HTTP binding](https://w3c-ccg.github.io/did-resolution/#bindings-https) in the DID resolution spec. + operationId: getDID + parameters: + - name: didRef + in: path + description: Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax) + required: true + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + responses: + '200': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + $ref: '#/components/schemas/DIDDocument' + '400': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + type: string + description: Empty representation + format: binary + example: '' + '404': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + type: string + description: Empty representation + format: binary + example: '' + '406': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + type: string + description: Empty representation + format: binary + example: '' + '410': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + type: string + description: Empty representation + format: binary + example: '' + '500': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + type: string + description: Empty representation + format: binary + example: '' + '501': + description: '' + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: '#/components/schemas/DIDResolutionResult' + application/did+ld+json: + schema: + type: string + description: Empty representation + format: binary + example: '' + /did-registrar/dids: + get: + tags: + - DID Registrar + summary: List all DIDs stored in Prism Agent's wallet + description: |- + List all DIDs stored in Prism Agent's wallet. + Return a paginated items ordered by created timestamp. + If the `limit` parameter is not set, it defaults to 100 items per page. + operationId: getDid-registrarDids + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: List Prism Agent managed DIDs + content: + application/json: + schema: + $ref: '#/components/schemas/ManagedDIDPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - DID Registrar + summary: Create unpublished DID and store it in Prism Agent's wallet + description: |- + Create unpublished DID and store it inside Prism Agent's wallet. The private keys of the DID is + managed by Prism Agent. The DID can later be published to the VDR using publications endpoint. + operationId: postDid-registrarDids + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateManagedDidRequest' + required: true + responses: + '201': + description: Created unpublished DID. + content: + application/json: + schema: + $ref: '#/components/schemas/CreateManagedDIDResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /did-registrar/dids/{didRef}: + get: + tags: + - DID Registrar + summary: Get DID stored in Prism Agent's wallet + description: Get DID stored in Prism Agent's wallet + operationId: getDid-registrarDidsDidref + parameters: + - name: didRef + in: path + description: Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax) + required: true + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + responses: + '200': + description: Get Prism Agent managed DID + content: + application/json: + schema: + $ref: '#/components/schemas/ManagedDID' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /did-registrar/dids/{didRef}/publications: + post: + tags: + - DID Registrar + summary: Publish the DID stored in Prism Agent's wallet to the VDR + description: Publish the DID stored in Prism Agent's wallet to the VDR. + operationId: postDid-registrarDidsDidrefPublications + parameters: + - name: didRef + in: path + description: Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax) + required: true + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + responses: + '202': + description: Publishing DID to the VDR. + content: + application/json: + schema: + $ref: '#/components/schemas/DIDOperationResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /did-registrar/dids/{didRef}/updates: + post: + tags: + - DID Registrar + summary: Update DID in Prism Agent's wallet and post update operation to the + VDR + description: |- + Update DID in Prism Agent's wallet and post update operation to the VDR. + This endpoint updates the DID document from the last confirmed operation. + Submitting multiple update operations without waiting for confirmation will result in + some operations being rejected as only one operation is allowed to be appended to the last confirmed operation. + operationId: postDid-registrarDidsDidrefUpdates + parameters: + - name: didRef + in: path + description: Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax) + required: true + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateManagedDIDRequest' + required: true + responses: + '202': + description: DID update operation accepted + content: + application/json: + schema: + $ref: '#/components/schemas/DIDOperationResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Cannot process due to conflict with current state of the resource + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /did-registrar/dids/{didRef}/deactivations: + post: + tags: + - DID Registrar + summary: Deactivate DID in Prism Agent's wallet and post deactivate operation + to the VDR + description: Deactivate DID in Prism Agent's wallet and post deactivate operation + to the VDR. + operationId: postDid-registrarDidsDidrefDeactivations + parameters: + - name: didRef + in: path + description: Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax) + required: true + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + responses: + '202': + description: DID deactivation operation accepted + content: + application/json: + schema: + $ref: '#/components/schemas/DIDOperationResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /issue-credentials/credential-offers: + post: + tags: + - Issue Credentials Protocol + summary: As a credential issuer, create a new credential offer to be sent to + a holder. + description: Creates a new credential offer in the database + operationId: createCredentialOffer + requestBody: + description: The credential offer object. + content: + application/json: + schema: + $ref: '#/components/schemas/CreateIssueCredentialRecordRequest' + required: true + responses: + '201': + description: The issue credential record. + content: + application/json: + schema: + $ref: '#/components/schemas/IssueCredentialRecord' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /issue-credentials/records: + get: + tags: + - Issue Credentials Protocol + summary: Gets the list of issue credential records. + description: Get the list of issue credential records paginated + operationId: getCredentialRecords + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + - name: thid + in: query + description: The thid of a DIDComm communication. + required: false + schema: + type: string + responses: + '200': + description: The list of issue credential records. + content: + application/json: + schema: + $ref: '#/components/schemas/IssueCredentialRecordPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /issue-credentials/records/{recordId}: + get: + tags: + - Issue Credentials Protocol + summary: Gets an existing issue credential record by its unique identifier. + description: Gets issue credential records by record id + operationId: getCredentialRecord + parameters: + - name: recordId + in: path + description: The unique identifier of the issue credential record. + required: true + schema: + type: string + responses: + '200': + description: The issue credential record. + content: + application/json: + schema: + $ref: '#/components/schemas/IssueCredentialRecord' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /issue-credentials/records/{recordId}/accept-offer: + post: + tags: + - Issue Credentials Protocol + summary: As a holder, accepts a credential offer received from an issuer. + description: Accepts a credential offer received from a VC issuer and sends + back a credential request. + operationId: acceptCredentialOffer + parameters: + - name: recordId + in: path + description: The unique identifier of the issue credential record. + required: true + schema: + type: string + requestBody: + description: The accept credential offer request object. + content: + application/json: + schema: + $ref: '#/components/schemas/AcceptCredentialOfferRequest' + required: true + responses: + '200': + description: The issue credential offer was successfully accepted. + content: + application/json: + schema: + $ref: '#/components/schemas/IssueCredentialRecord' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /issue-credentials/records/{recordId}/issue-credential: + post: + tags: + - Issue Credentials Protocol + summary: As an issuer, issues the verifiable credential related to the specified + record. + description: Sends credential to a holder (holder DID is specified in credential + as subjectDid). Credential is constructed from the credential records found + by credential id. + operationId: issueCredential + parameters: + - name: recordId + in: path + description: The unique identifier of the issue credential record. + required: true + schema: + type: string + responses: + '200': + description: The request was processed successfully and the credential will + be issued asynchronously. + content: + application/json: + schema: + $ref: '#/components/schemas/IssueCredentialRecord' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /present-proof/presentations: + get: + tags: + - Present Proof + summary: Gets the list of proof presentation records. + description: list of presentation statuses + operationId: getAllPresentation + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + - name: thid + in: query + required: false + schema: + type: string + responses: + '200': + description: The list of proof presentation records. + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationStatusPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Present Proof + summary: As a Verifier, create a new proof presentation request and send it + to the Prover. + description: Holder presents proof derived from the verifiable credential to + verifier. + operationId: requestPresentation + requestBody: + description: The present proof creation request. + content: + application/json: + schema: + $ref: '#/components/schemas/RequestPresentationInput' + required: true + responses: + '201': + description: The proof presentation request was created successfully and + will be sent asynchronously to the Prover. + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationStatus' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /present-proof/presentations/{presentationId}: + get: + tags: + - Present Proof + summary: Gets an existing proof presentation record by its unique identifier. + More information on the error can be found in the response body. + description: Returns an existing presentation record by id. + operationId: getPresentation + parameters: + - name: presentationId + in: path + description: The unique identifier of the presentation record. + required: true + schema: + type: string + format: uuid + responses: + '200': + description: The proof presentation record. + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationStatus' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + patch: + tags: + - Present Proof + summary: Updates the proof presentation record matching the unique identifier, + with the specific action to perform. + description: Accept or reject presentation of proof request. + operationId: updatePresentation + parameters: + - name: presentationId + in: path + description: The unique identifier of the presentation record. + required: true + schema: + type: string + format: uuid + requestBody: + description: The action to perform on the proof presentation record. + content: + application/json: + schema: + $ref: '#/components/schemas/RequestPresentationAction' + required: true + responses: + '200': + description: The proof presentation record was successfully updated. + content: + application/json: + schema: + $ref: '#/components/schemas/PresentationStatus' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /_system/health: + get: + tags: + - System + summary: As a system user, check the health status of the running service + description: Returns the health info object of the running service + operationId: systemHealth + responses: + '200': + description: The health info object. + content: + application/json: + schema: + $ref: '#/components/schemas/HealthInfo' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /_system/metrics: + get: + tags: + - System + summary: As a system user, check the health status of the running service + description: Returns the health info object of the running service + operationId: systemMetrics + responses: + '200': + description: The metrics as pain strings. + content: + text/plain: + schema: + type: string + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /iam/entities: + get: + tags: + - Identity and Access Management + summary: Get all entities + description: 'Get all entities with the pagination by `offset` and `limit` parameters ' + operationId: getAllEntities + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: Collection of Entity records + content: + application/json: + schema: + $ref: '#/components/schemas/EntityResponsePage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + post: + tags: + - Identity and Access Management + summary: Create a new entity record + description: Create the new entity record. The entity record is a representation + of the account in the system. + operationId: createEntity + requestBody: + description: JSON object required for the entity creation + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEntityRequest' + required: true + responses: + '201': + description: The new entity is successfully created + content: + application/json: + schema: + $ref: '#/components/schemas/EntityResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + /iam/entities/{id}/name: + put: + tags: + - Identity and Access Management + summary: Update the entity record name by `id` + description: Update the entity record name by `id` + operationId: updateEntityName + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + requestBody: + description: JSON object required for the entity name update + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateEntityNameRequest' + required: true + responses: + '200': + description: The entity record is successfully updated + content: + application/json: + schema: + $ref: '#/components/schemas/EntityResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + /iam/entities/{id}/walletId: + put: + tags: + - Identity and Access Management + summary: Update the entity record `walletId` by `id` + description: Update the entity record `walletId` field by `id` + operationId: updateEntityWalletId + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + requestBody: + description: JSON object required for the entity walletId update + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateEntityWalletIdRequest' + required: true + responses: + '200': + description: The entity record is successfully updated + content: + application/json: + schema: + $ref: '#/components/schemas/EntityResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + /iam/entities/{id}: + get: + tags: + - Identity and Access Management + summary: Get the entity by the `id` + description: Get the entity by the unique identifier + operationId: getEntityById + parameters: + - name: id + in: path + description: Identifier of the entity + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Entity found by `id` + content: + application/json: + schema: + $ref: '#/components/schemas/EntityResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + delete: + tags: + - Identity and Access Management + summary: Delete the entity by `id` + description: Delete the entity by the unique identifier + operationId: deleteEntityById + parameters: + - name: id + in: path + description: Identifier of the entity + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Entity deleted successfully + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + /iam/apikey-authentication: + post: + tags: + - Identity and Access Management + summary: Register the `apikey` for the entity + description: Register the `apikey` for the entity. + operationId: addEntityApiKeyAuthentication + requestBody: + description: JSON object required for the registering the entity and `apikey` + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKeyAuthenticationRequest' + required: true + responses: + '201': + description: The new `apikey` is successfully registered for the entity + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + delete: + tags: + - Identity and Access Management + summary: Unregister the `apikey` for the entity + description: Unregister the `apikey` for the entity. + operationId: deleteEntityApiKeyAuthentication + requestBody: + description: JSON object required for the unregistering the entity and `apikey` + content: + application/json: + schema: + $ref: '#/components/schemas/ApiKeyAuthenticationRequest' + required: true + responses: + '200': + description: The new `apikey` is successfully unregistered for the entity + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - adminApiKeyAuth: [] + /wallets: + get: + tags: + - Wallet Management + summary: List all wallets + operationId: getWallets + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: Successfully list all the wallets + content: + application/json: + schema: + $ref: '#/components/schemas/WalletDetailPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Wallet Management + summary: Create a new wallet + description: |- + Create a new wallet with optional to use provided seed. + The seed will be used for DID key derivation inside the wallet. + operationId: createWallet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWalletRequest' + required: true + responses: + '201': + description: A new wallet has been created + content: + application/json: + schema: + $ref: '#/components/schemas/WalletDetail' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + /wallets/{walletId}: + get: + tags: + - Wallet Management + summary: Get the wallet by ID + operationId: getWalletsWalletid + parameters: + - name: walletId + in: path + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Successfully get the wallet + content: + application/json: + schema: + $ref: '#/components/schemas/WalletDetail' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + /wallets/{walletId}/uma-permissions: + post: + tags: + - Wallet Management + summary: Create a UMA resource permission on an authorization server for the + wallet. + operationId: createWalletUmaPermission + parameters: + - name: walletId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWalletUmaPermissionRequest' + required: true + responses: + '200': + description: UMA resource permission is created on an authorization server. + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + delete: + tags: + - Wallet Management + summary: Delete a UMA resource permission on an authorization server for the + wallet. + operationId: deleteWalletUmaPermission + parameters: + - name: walletId + in: path + required: true + schema: + type: string + format: uuid + - name: subject + in: query + required: true + schema: + type: string + format: uuid + responses: + '200': + description: UMA resource permission is removed from an authorization server. + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + /events/webhooks: + get: + tags: + - Events + summary: List wallet webhook notifications + operationId: getEventsWebhooks + responses: + '200': + description: List wallet webhook notifications + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookNotificationPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Events + summary: Create wallet webhook notifications + operationId: postEventsWebhooks + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWebhookNotification' + required: true + responses: + '200': + description: Webhook notification has been created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookNotification' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Cannot process due to conflict with current state of the resource + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /events/webhooks/{id}: + delete: + tags: + - Events + summary: Delete the wallet webhook notification by `id` + operationId: deleteEventsWebhooksId + parameters: + - name: id + in: path + description: ID of the webhook notification to delete. + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Webhook notification has been deleted. + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] +components: + schemas: + AcceptConnectionInvitationRequest: + required: + - invitation + type: object + properties: + invitation: + type: string + description: The base64-encoded raw invitation. + example: eyJAaWQiOiIzZmE4NWY2NC01NzE3LTQ1NjItYjNmYy0yYzk2M2Y2NmFmYTYiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvbXktZmFtaWx5LzEuMC9teS1tZXNzYWdlLXR5cGUiLCJkaWQiOiJXZ1d4cXp0ck5vb0c5MlJYdnhTVFd2IiwiaW1hZ2VVcmwiOiJodHRwOi8vMTkyLjE2OC41Ni4xMDEvaW1nL2xvZ28uanBnIiwibGFiZWwiOiJCb2IiLCJyZWNpcGllbnRLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInJvdXRpbmdLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8xOTIuMTY4LjU2LjEwMTo4MDIwIn0= + AcceptCredentialOfferRequest: + type: object + properties: + subjectId: + type: string + description: The short-form subject Prism DID to which the JWT verifiable + credential will be issued.This parameter is used for JWT credentials only. + example: did:prism:3bb0505d13fcb04d28a48234edb27b0d4e6d7e18a81e2c1abab58f3bbc21ce6f + ActionType: + type: string + enum: + - ADD_KEY + - ADD_SERVICE + - PATCH_CONTEXT + - REMOVE_KEY + - REMOVE_SERVICE + - UPDATE_SERVICE + ApiKeyAuthenticationRequest: + required: + - entityId + - apiKey + type: object + properties: + entityId: + type: string + description: The `entityId` of the entity to be updated + format: uuid + example: 01234567-0000-0000-0000-000000000000 + apiKey: + type: string + description: The `apikey` of the entity to be updated + example: dkflks3DflkFmkllnDfde + minLength: 16 + maxLength: 128 + Arr: + type: object + properties: + elements: + type: array + items: {} + Bool: + required: + - value + type: object + properties: + value: + type: boolean + Connection: + required: + - connectionId + - thid + - role + - state + - invitation + - createdAt + - metaRetries + - self + - kind + type: object + properties: + connectionId: + type: string + description: The unique identifier of the connection. + format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + thid: + type: string + description: The unique identifier of the thread this connection record + belongs to. The value will identical on both sides of the connection (inviter + and invitee) + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + label: + type: string + description: A human readable alias for the connection. + example: Peter + goalCode: + type: string + description: A self-attested code the receiver may want to display to the + user or use in automatically deciding what to do with the out-of-band + message. + example: issue-vc + goal: + type: string + description: A self-attested string that the receiver may want to display + to the user about the context-specific goal of the out-of-band message. + example: To issue a Peter College Graduate credential + myDid: + type: string + description: The DID representing me as the inviter or invitee in this specific + connection. + example: did:peer:12345 + theirDid: + type: string + description: The DID representing the other peer as the an inviter or invitee + in this specific connection. + example: did:peer:67890 + role: + type: string + description: The role played by the Prism agent in the connection flow. + example: Inviter + enum: + - Inviter + - Invitee + state: + type: string + description: The current state of the connection protocol execution. + example: InvitationGenerated + enum: + - InvitationGenerated + - InvitationReceived + - ConnectionRequestPending + - ConnectionRequestSent + - ConnectionRequestReceived + - ConnectionResponsePending + - ConnectionResponseSent + - ConnectionResponseReceived + - ProblemReportPending + - ProblemReportSent + - ProblemReportReceived + invitation: + $ref: '#/components/schemas/ConnectionInvitation' + createdAt: + type: string + description: The date and time the connection record was created. + format: date-time + example: 2022-03-10T12:00Z + updatedAt: + type: string + description: The date and time the connection record was last updated. + format: date-time + example: 2022-03-10T12:00Z + metaRetries: + type: integer + description: The maximum background processing attempts remaining for this + record + format: int32 + example: 5 + self: + type: string + description: The reference to the connection resource. + example: https://atala-prism-products.io/connections/ABCD-1234 + kind: + type: string + description: The type of object returned. In this case a `Connection`. + example: Connection + ConnectionInvitation: + required: + - id + - type + - from + - invitationUrl + type: object + properties: + id: + type: string + description: The unique identifier of the invitation. It should be used + as parent thread ID (pthid) for the Connection Request message that follows. + format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + type: + type: string + description: The DIDComm Message Type URI (MTURI) the invitation message + complies with. + example: https://didcomm.org/out-of-band/2.0/invitation + from: + type: string + description: The DID representing the sender to be used by recipients for + future interactions. + example: did:peer:1234457 + invitationUrl: + type: string + description: The invitation message encoded as a URL. This URL follows the + Out of [Band 2.0 protocol](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) + and can be used to generate a QR code for example. + example: https://my.domain.com/path?_oob=eyJAaWQiOiIzZmE4NWY2NC01NzE3LTQ1NjItYjNmYy0yYzk2M2Y2NmFmYTYiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvbXktZmFtaWx5LzEuMC9teS1tZXNzYWdlLXR5cGUiLCJkaWQiOiJXZ1d4cXp0ck5vb0c5MlJYdnhTVFd2IiwiaW1hZ2VVcmwiOiJodHRwOi8vMTkyLjE2OC41Ni4xMDEvaW1nL2xvZ28uanBnIiwibGFiZWwiOiJCb2IiLCJyZWNpcGllbnRLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInJvdXRpbmdLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8xOTIuMTY4LjU2LjEwMTo4MDIwIn0= + description: The invitation for this connection + ConnectionsPage: + required: + - kind + - self + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: '#/components/schemas/Connection' + description: '' + example: [] + kind: + type: string + description: '' + example: ConnectionsPage + self: + type: string + description: '' + example: /prism-agent/connections?offset=10&limit=10 + pageOf: + type: string + description: '' + example: '' + next: + type: string + description: '' + example: /prism-agent/connections?offset=20&limit=10 + previous: + type: string + description: '' + example: /prism-agent/connections?offset=0&limit=10 + CreateConnectionRequest: + type: object + properties: + label: + type: string + description: A human readable alias for the connection. + example: Peter + goalCode: + type: string + description: A self-attested code the receiver may want to display to the + user or use in automatically deciding what to do with the out-of-band + message. + example: issue-vc + goal: + type: string + description: A self-attested string that the receiver may want to display + to the user about the context-specific goal of the out-of-band message. + example: To issue a Peter College Graduate credential + CreateEntityRequest: + required: + - name + type: object + properties: + id: + type: string + description: The new `id` of the entity to be created. If this field is + not provided, the server will generate a new UUID for the entity + format: uuid + example: 00000000-0000-0000-0000-000000000000 + name: + type: string + description: The new `name` of the entity to be created. If this field is + not provided, the server will generate a random name for the entity + example: John Doe + minLength: 1 + maxLength: 128 + walletId: + type: string + description: The new `walletId` of the entity to be created. If this field + is not provided, the server will set the default `walletId` + format: uuid + example: 00000000-0000-0000-0000-000000000000 + CreateIssueCredentialRecordRequest: + required: + - claims + - connectionId + type: object + properties: + validityPeriod: + type: number + description: The validity period in seconds of the verifiable credential + that will be issued. + format: double + example: 3600.0 + schemaId: + type: string + description: The unique identifier of the schema used for this credential + offer. + example: https://agent-host.com/prism-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 + credentialDefinitionId: + type: string + description: The unique identifier of the credential definition used for + this credential offer (AnonCreds only) + format: uuid + example: d9569cec-c81e-4779-aa86-0d5994d82676 + credentialFormat: + type: string + description: The format used for this credential offer (default to 'JWT') + example: JWT + claims: + description: The claims that will be associated with the issued verifiable + credential. + example: (firstname,Alice) + automaticIssuance: + type: boolean + description: Specifies whether or not the credential should be automatically + generated and issued when receiving the `CredentialRequest` from the holder. + If set to `false`, a manual approval by the issuer via API call will be + required for the VC to be issued. + example: true + issuingDID: + type: string + description: The issuer DID of the verifiable credential (JWT credentials + only) + example: did:prism:issuerofverifiablecredentials + connectionId: + type: string + description: The unique identifier of a DIDComm connection that already + exists between the issuer and the holder, and that will be used to execute + the issue credential protocol. + format: uuid + example: d9569cec-c81e-4779-aa86-0d5994d82676 + CreateManagedDIDResponse: + required: + - longFormDid + type: object + properties: + longFormDid: + type: string + description: A long-form DID for the created DID + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff:Cr4BCrsBElsKBmF1dGgtMRAEQk8KCXNlY3AyNTZrMRIg0opTuxu-zt6aRbT1tPniG4eu4CYsQPM3rrLzvzNiNgwaIIFTnyT2N4U7qCQ78qtWC3-p0el6Hvv8qxG5uuEw-WgMElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIKhBU0eCOO6Vinz_8vhtFSAhYYqrkEXC8PHGxkuIUev8GiAydFHLXb7c22A1Uj_PR21NZp6BCDQqNq2xd244txRgsQ + CreateManagedDidRequest: + required: + - documentTemplate + type: object + properties: + documentTemplate: + $ref: '#/components/schemas/CreateManagedDidRequestDocumentTemplate' + CreateManagedDidRequestDocumentTemplate: + type: object + properties: + publicKeys: + type: array + items: + $ref: '#/components/schemas/ManagedDIDKeyTemplate' + services: + type: array + items: + $ref: '#/components/schemas/Service' + contexts: + type: array + items: + type: string + CreateWalletRequest: + required: + - name + type: object + properties: + seed: + type: string + description: A BIP32 seed encoded in hexadecimal string. It is expected + to represent 64-bytes binary seed (128 hex characters). + example: c9994785ce6d548134020f610b76102ca1075d3bb672a75ec8c9a27a7b8607e3b9b384e43b77bb08f8d5159651ae38b98573f7ecc79f2d7e1f1cc371ce60cf8a + name: + type: string + description: A name of the wallet + example: my-wallet-1 + minLength: 1 + maxLength: 128 + id: + type: string + description: The unique `id` of the wallet. Randomly generated if not specified. + format: uuid + example: 00000000-0000-0000-0000-000000000000 + CreateWalletUmaPermissionRequest: + required: + - subject + type: object + properties: + subject: + type: string + description: The subject ID that should be granted the permission to the + wallet. This can be found in the `sub` claim of a JWT token. + format: uuid + example: 00000000-0000-0000-0000-000000000000 + CreateWebhookNotification: + required: + - url + type: object + properties: + url: + type: string + customHeaders: + $ref: '#/components/schemas/Map_String' + CredentialDefinitionInput: + required: + - name + - version + - tag + - author + - schemaId + - signatureType + - supportRevocation + type: object + properties: + name: + type: string + description: A human-readable name for the credential definition. A piece + of Metadata. + example: DrivingLicense + minLength: 1 + description: + type: string + description: A human-readable description of the credential definition + example: Simple credential definition for the driving licence verifiable + credential. + minLength: 1 + version: + type: string + description: Denotes the revision of a given Credential Definition. It should + follow semantic version convention to describe the impact of the credential + definition 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-]+)*))?$ + tag: + type: string + description: Token that allow to lookup and filter the credential definition + records. + example: licence + author: + type: string + description: DID of the identity which authored the credential definition. + A piece of Metadata. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + pattern: ^did:(?[a-z0-9]+(:[a-z0-9]+)*)\:(?[^#?]*)$ + schemaId: + type: string + description: The unique identifier of the schema used for this credential + definition. + example: https://agent-host.com/prism-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 + signatureType: + type: string + description: Signature type used in the CredentialDefinition. + example: CL + supportRevocation: + type: boolean + description: Boolean flag indicating whether revocation is supported for + this CredentialDefinition. + example: false + CredentialDefinitionResponse: + required: + - guid + - id + - name + - version + - tag + - description + - author + - authored + - schemaId + - definition + - keyCorrectnessProof + - signatureType + - supportRevocation + - kind + - self + type: object + properties: + guid: + type: string + description: 'Globally unique id of the credential definition.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`' + format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + id: + type: string + description: A locally unique identifier to address the credential definition. + UUID is generated by the backend. + example: 0527aea1-d131-3948-a34d-03af39aba8b5 + longId: + type: string + description: Resource id of the credential definition. 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 definition. A piece + of Metadata. + example: DrivingLicense + version: + type: string + description: Denotes the revision of a given Credential Definition. It should + follow semantic version convention to describe the impact of the credential + definition evolution. + example: 1.0.0 + tag: + type: string + description: Token that allow to lookup and filter the credential definition + records. + example: licence + description: + type: string + description: A human-readable description of the credential definition + example: Simple credential definition for the driving licence verifiable + credential. + author: + type: string + description: DID of the identity which authored the credential definition. + 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 definition was created. A piece of Metadata.' + format: date-time + example: 2022-03-10T12:00Z + schemaId: + type: string + description: The unique identifier of the schema used for this credential + definition. + example: https://agent-host.com/prism-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 + definition: + description: Definition object that represents the actual definition of + the credential. + example: + credentialSubject: + emailAddress: alice@wonderland.com + givenName: Alice + familyName: Wonderland + dateOfIssuance: '2000-01-01T10:00:00Z' + drivingLicenseID: '12345' + drivingClass: 5 + keyCorrectnessProof: + description: A proof that validates the correctness of the key within the + context of the credential definition. + signatureType: + type: string + description: Signature type used in the CredentialDefinition. + example: CL + supportRevocation: + type: boolean + description: Boolean flag indicating whether revocation is supported for + this CredentialDefinition. + example: false + proof: + $ref: '#/components/schemas/Proof' + kind: + type: string + description: A string that identifies the type of resource being returned + in the response. + example: CredentialDefinition + self: + type: string + description: The URL that uniquely identifies the resource being returned + in the response. + example: /prism-agent/credential-definition-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 + CredentialDefinitionResponsePage: + required: + - kind + - self + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: '#/components/schemas/CredentialDefinitionResponse' + description: A sequence of CredentialSchemaResponse objects representing + the list of credential schemas that the API response contains + example: [] + 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 + CredentialSchemaInput: + required: + - name + - version + - type + - schema + - author + type: object + properties: + name: + type: string + description: A human-readable name for the credential schema. A piece of + Metadata. + example: DrivingLicense + minLength: 1 + version: + 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 + description: A human-readable description of the credential schema + example: Simple credential schema for the driving licence verifiable credential. + minLength: 1 + 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: + description: Valid JSON Schema where the Credential Schema data fields are + defined. A piece of Metadata + example: + $id: https://example.com/driving-license-1.0 + $schema: https://json-schema.org/draft/2020-12/schema + description: Driving License + type: object + properties: + emailAddress: + type: string + format: email + givenName: + type: string + familyName: + type: string + dateOfIssuance: + type: string + format: date-time + drivingLicenseID: + type: string + drivingClass: + type: integer + required: + - emailAddress + - familyName + - dateOfIssuance + - drivingLicenseID + - drivingClass + additionalProperties: false + tags: + type: array + items: + type: string + description: Tokens that allow to lookup and filter the credential schema + records. + example: + - driving + - licence + - id + author: + type: string + description: DID of the identity which authored the credential schema. A + piece of Metadata. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + pattern: ^did:(?[a-z0-9]+(:[a-z0-9]+)*)\:(?[^#?]*)$ + CredentialSchemaResponse: + required: + - guid + - id + - name + - version + - description + - type + - schema + - author + - authored + - kind + - self + type: object + properties: + guid: + type: string + 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`' + 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: 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. + 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. + 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: + description: Valid JSON Schema where the Credential Schema data fields are + defined. A piece of Metadata + example: + $id: https://example.com/driving-license-1.0 + $schema: https://json-schema.org/draft/2020-12/schema + description: Driving License + type: object + properties: + emailAddress: + type: string + format: email + givenName: + type: string + familyName: + type: string + dateOfIssuance: + type: string + format: date-time + drivingLicenseID: + type: string + drivingClass: + type: integer + required: + - emailAddress + - familyName + - dateOfIssuance + - drivingLicenseID + - drivingClass + additionalProperties: false + 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 + example: 2022-03-10T12:00Z + proof: + $ref: '#/components/schemas/Proof1' + 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 + CredentialSchemaResponsePage: + required: + - kind + - self + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: '#/components/schemas/CredentialSchemaResponse' + description: A sequence of CredentialSchemaResponse objects representing + the list of credential schemas that the API response contains + example: [] + 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 + DIDDocument: + required: + - id + type: object + properties: + '@context': + type: array + items: + type: string + description: The JSON-LD context for the DID resolution result. + id: + type: string + description: |- + [DID subject](https://www.w3.org/TR/did-core/#did-subject). + The value must match the DID that was given to the resolver. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + controller: + type: string + description: '[DID controller](https://www.w3.org/TR/did-core/#did-controller)' + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + verificationMethod: + type: array + items: + $ref: '#/components/schemas/VerificationMethod' + authentication: + type: array + items: + type: string + assertionMethod: + type: array + items: + type: string + keyAgreement: + type: array + items: + type: string + capabilityInvocation: + type: array + items: + type: string + capabilityDelegation: + type: array + items: + type: string + service: + type: array + items: + $ref: '#/components/schemas/Service' + description: A W3C compliant Prism DID document representation. + DIDDocumentMetadata: + type: object + properties: + deactivated: + type: boolean + description: If a DID has been deactivated, DID document metadata MUST include + this property with the boolean value true. If a DID has not been deactivated, + this property is OPTIONAL, but if included, MUST have the boolean value + false. + example: false + canonicalId: + type: string + description: |2 + + A DID in canonical form. + If a DID is in long form and has been published, DID document metadata MUST contain a `canonicalId`` property with the short form DID as its value. + If a DID in short form or has not been published, DID document metadata MUST NOT contain a `canonicalId` property. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + versionId: + type: string + description: |2 + + DID document metadata MUST contain a versionId property with the hash of the AtalaOperation contained in the latest valid SignedAtalaOperation that created the DID or changed the DID's internal state. + example: 4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + created: + type: string + description: The timestamp of the Cardano block that contained the first + valid SignedAtalaOperation with a CreateDIDOperation that created the + DID. + example: '2023-02-04T13:52:10Z' + updated: + type: string + description: The timestamp of the Cardano block that contained the latest + valid SignedAtalaOperation that changed the DID's internal state. + example: '2023-02-04T13:52:10Z' + description: '[DID document metadata](https://www.w3.org/TR/did-core/#did-document-metadata)' + DIDOperationResponse: + required: + - scheduledOperation + type: object + properties: + scheduledOperation: + $ref: '#/components/schemas/DidOperationSubmission' + DIDResolutionMetadata: + type: object + properties: + error: + type: string + description: Resolution error constant according to [DID spec registries](https://www.w3.org/TR/did-spec-registries/#error) + example: invalidDid + errorMessage: + type: string + description: Resolution error message + example: The initialState does not match the suffix + contentType: + type: string + description: The media type of the returned DID document + example: application/did+ld+json + description: '[DID resolution metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata)' + DIDResolutionResult: + required: + - '@context' + - didDocumentMetadata + - didResolutionMetadata + type: object + properties: + '@context': + type: string + description: The JSON-LD context for the DID resolution result. + example: https://w3id.org/did-resolution/v1 + didDocument: + $ref: '#/components/schemas/DIDDocument' + didDocumentMetadata: + $ref: '#/components/schemas/DIDDocumentMetadata' + didResolutionMetadata: + $ref: '#/components/schemas/DIDResolutionMetadata' + DidOperationSubmission: + required: + - id + - didRef + type: object + properties: + id: + type: string + description: A scheduled operation ID + example: 98e6a4db10e58fcc011dd8def5ce99fd8b52af39e61e5fb436dc28259139818b + didRef: + type: string + description: A DID affected by the scheduled operation + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + EntityResponse: + required: + - kind + - self + - id + - name + - walletId + - createdAt + - updatedAt + type: object + properties: + kind: + type: string + description: The `kind` of the entity. + example: Entity + self: + type: string + description: The `self` link of the entity. + example: http://localhost:8080/prism-agent/iam/entities/00000000-0000-0000-0000-000000000000 + id: + type: string + description: The unique `id` of the entity + format: uuid + example: 00000000-0000-0000-0000-000000000000 + name: + type: string + description: The `name` of the entity. + example: John Doe + walletId: + type: string + description: The `walletId` owned by the entity. + format: uuid + example: 00000000-0000-0000-0000-000000000000 + createdAt: + type: string + description: The `createdAt` timestamp of the entity. + format: date-time + example: '2023-01-01T00:00:00Z' + updatedAt: + type: string + description: The `updatedAt` timestamp of the entity. + format: date-time + example: '2023-01-01T00:00:00Z' + EntityResponsePage: + required: + - kind + - self + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: '#/components/schemas/EntityResponse' + description: A sequence of CredentialSchemaResponse objects representing + the list of credential schemas that the API response contains + example: + - EntityResponse(Entity,/prism-agent/iam/entities/00000000-0000-0000-0000-000000000000,00000000-0000-0000-0000-000000000000,John + Doe,00000000-0000-0000-0000-000000000000,2023-01-01T00:00:00Z,2023-01-01T00:00:00Z) + 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 + ErrorResponse: + required: + - status + - type + - title + - instance + type: object + properties: + status: + type: integer + description: The HTTP status code for this occurrence of the problem. + format: int32 + example: 200 + type: + type: string + description: A URI reference that identifies the problem type. + example: https://example.org/doc/#model-MalformedEmail/ + title: + type: string + description: A short, human-readable summary of the problem type. It does + not change from occurrence to occurrence of the problem. + example: Malformed email + detail: + type: string + description: A human-readable explanation specific to this occurrence of + the problem. + example: The received '{}à!è@!.b}' email does not conform to the email format + instance: + type: string + description: A URI reference that identifies the specific occurrence of + the problem. It may or may not yield further information if dereferenced. + example: The received '{}à!è@!.b}' email does not conform to the email format + HealthInfo: + required: + - version + type: object + properties: + version: + type: string + description: The semantic version number of the running service + example: 1.1.0 + IssueCredentialRecord: + required: + - recordId + - thid + - credentialFormat + - claims + - createdAt + - role + - protocolState + - metaRetries + type: object + properties: + recordId: + type: string + description: The unique identifier of the issue credential record. + example: 80d612dc-0ded-4ac9-90b4-1b8eabb04545 + thid: + type: string + description: The unique identifier of the thread this credential record + belongs to. The value will identical on both sides of the issue flow (issuer + and holder) + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + credentialFormat: + type: string + description: The format used for this credential offer (default to 'JWT') + example: JWT + enum: + - JWT + - AnonCreds + subjectId: + type: string + description: The identifier (e.g DID) of the subject to which the verifiable + credential will be issued. + example: did:prism:subjectofverifiablecredentials + validityPeriod: + type: number + description: The validity period in seconds of the verifiable credential + that will be issued. + format: double + example: 3600.0 + claims: + description: The claims that will be associated with the issued verifiable + credential. + example: (firstname,Alice) + automaticIssuance: + type: boolean + description: Specifies whether or not the credential should be automatically + generated and issued when receiving the `CredentialRequest` from the holder. + If set to `false`, a manual approval by the issuer via API call will be + required for the VC to be issued. + example: true + createdAt: + type: string + description: The date and time when the issue credential record was created. + format: date-time + example: '2023-12-06T10:40:43.781066302Z' + updatedAt: + type: string + description: The date and time when the issue credential record was last + updated. + format: date-time + role: + type: string + description: The role played by the Prism agent in the credential issuance + flow. + example: Issuer + enum: + - Issuer + - Holder + protocolState: + type: string + description: The current state of the issue credential protocol execution. + example: OfferPending + enum: + - OfferPending + - OfferSent + - OfferReceived + - RequestPending + - RequestGenerated + - RequestSent + - RequestReceived + - CredentialPending + - CredentialGenerated + - CredentialSent + - CredentialReceived + - ProblemReportPending + - ProblemReportSent + - ProblemReportReceived + credential: + type: string + description: The base64-encoded verifiable credential, in JWT or AnonCreds + format, that has been sent by the issuer. + issuingDID: + type: string + description: Issuer DID of the verifiable credential object. + example: did:prism:issuerofverifiablecredentials + metaRetries: + type: integer + description: The maximum background processing attempts remaining for this + record + format: int32 + example: 5 + IssueCredentialRecordPage: + required: + - self + - kind + - pageOf + type: object + properties: + 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 + kind: + 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 + contents: + type: array + items: + $ref: '#/components/schemas/IssueCredentialRecord' + description: A sequence of IssueCredentialRecord objects representing the + list of credential records that the API response contains + example: [] + Json: + description: The service endpoint. Can contain multiple possible values as described + in the [Create DID operation] + example: https://example.com + oneOf: + - $ref: '#/components/schemas/Arr' + - $ref: '#/components/schemas/Bool' + - $ref: '#/components/schemas/Null' + - $ref: '#/components/schemas/Num' + - $ref: '#/components/schemas/Obj' + - $ref: '#/components/schemas/Str' + ManagedDID: + required: + - did + - status + type: object + properties: + did: + type: string + description: A managed DID + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + longFormDid: + type: string + description: A long-form DID. Mandatory when status is not PUBLISHED and + optional when status is PUBLISHED + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff:Cr4BCrsBElsKBmF1dGgtMRAEQk8KCXNlY3AyNTZrMRIg0opTuxu-zt6aRbT1tPniG4eu4CYsQPM3rrLzvzNiNgwaIIFTnyT2N4U7qCQ78qtWC3-p0el6Hvv8qxG5uuEw-WgMElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIKhBU0eCOO6Vinz_8vhtFSAhYYqrkEXC8PHGxkuIUev8GiAydFHLXb7c22A1Uj_PR21NZp6BCDQqNq2xd244txRgsQ + status: + type: string + description: |- + A status indicating a publication state of a DID in the wallet (e.g. PUBLICATION_PENDING, PUBLISHED). + Does not represent DID a full lifecyle (e.g. deactivated, recovered, updated). + example: CREATED + ManagedDIDKeyTemplate: + required: + - id + - purpose + type: object + properties: + id: + type: string + description: Identifier of a verification material in the DID Document + example: key-1 + purpose: + $ref: '#/components/schemas/Purpose' + description: key-pair template to add to DID document. + ManagedDIDPage: + required: + - self + - kind + - pageOf + type: object + properties: + self: + type: string + kind: + type: string + pageOf: + type: string + next: + type: string + previous: + type: string + contents: + type: array + items: + $ref: '#/components/schemas/ManagedDID' + Map_String: + type: object + additionalProperties: + type: string + 'Null': + type: object + Num: + required: + - value + type: object + properties: + value: + type: number + Obj: + type: object + Options: + required: + - challenge + - domain + type: object + properties: + challenge: + type: string + description: The challenge should be a randomly generated string. + example: 11c91493-01b3-4c4d-ac36-b336bab5bddf + domain: + type: string + description: The domain value can be any string or URI. + example: https://example-verifier.com + description: The options to use when creating the proof presentation request + (e.g., domain, challenge). + PatchContextAction: + type: object + properties: + contexts: + type: array + items: + type: string + PresentationStatus: + required: + - presentationId + - thid + - role + - status + - metaRetries + type: object + properties: + presentationId: + type: string + description: The unique identifier of the presentation record. + example: 3c6d9fa5-d277-431e-a6cb-d3956e47e610 + thid: + type: string + description: The unique identifier of the thread this presentation record + belongs to. The value will identical on both sides of the presentation + flow (verifier and prover) + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + role: + type: string + description: The role played by the Prism agent in the proof presentation + flow. + example: Verifier + enum: + - Verifier + - Prover + status: + type: string + description: The current state of the proof presentation record. + example: RequestPending + enum: + - RequestPending + - RequestSent + - RequestReceived + - RequestRejected + - PresentationPending + - PresentationGenerated + - PresentationSent + - PresentationReceived + - PresentationVerified + - PresentationAccepted + - PresentationRejected + - ProblemReportPending + - ProblemReportSent + - ProblemReportReceived + proofs: + type: array + items: + $ref: '#/components/schemas/ProofRequestAux' + description: The type of proofs requested in the context of this proof presentation + request (e.g., VC schema, trusted issuers, etc.) + example: [] + data: + type: array + items: + type: string + description: The list of proofs presented by the prover to the verifier. + example: [] + connectionId: + type: string + description: The unique identifier of an established connection between + the verifier and the prover. + example: bc528dc8-69f1-4c5a-a508-5f8019047900 + metaRetries: + type: integer + description: The maximum background processing attempts remaining for this + record + format: int32 + example: 5 + PresentationStatusPage: + required: + - self + - kind + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: '#/components/schemas/PresentationStatus' + description: A sequence of Presentation objects. + example: [] + self: + type: string + description: The reference to the presentation collection itself. + example: /present-proof/presentations + kind: + type: string + description: The type of object returned. In this case a `Collection`. + example: Collection + pageOf: + type: string + description: Page number within the context of paginated response. + example: 1 + next: + type: string + description: URL of the next page (if available) + example: '' + previous: + type: string + description: URL of the previous page (if available) + example: '' + Proof: + required: + - type + - created + - 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 definition itself. + format: date-time + example: 2022-03-10T12: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 definition 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 + definition and proof are being used + example: prims.atala.com + description: A digital signature over the Credential Definition 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 + Proof1: + required: + - type + - created + - 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: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 + ProofRequestAux: + required: + - schemaId + type: object + properties: + schemaId: + type: string + description: The unique identifier of a schema the VC should comply with. + example: https://schema.org/Person + trustIssuers: + type: array + items: + type: string + description: One or more issuers that are trusted by the verifier emitting + the proof presentation request. + example: + - did:web:atalaprism.io/users/testUser + - did.prism:123 + - did:prism:... + PublicKeyJwk: + required: + - kty + type: object + properties: + crv: + type: string + x: + type: string + y: + type: string + kty: + type: string + Purpose: + type: string + description: Purpose of the verification material in the DID Document + example: authentication + enum: + - assertionMethod + - authentication + - capabilityDelegation + - capabilityInvocation + - keyAgreement + RemoveEntryById: + required: + - id + type: object + properties: + id: + type: string + RequestPresentationAction: + required: + - action + type: object + properties: + action: + type: string + description: The action to perform on the proof presentation record. + example: request-accept + enum: + - request-accept + - request-reject + - presentation-accept + - presentation-reject + proofId: + type: array + items: + type: string + description: The unique identifier of the issue credential record - and + hence VC - to use as the prover accepts the presentation request. Only + applicable on the prover side when the action is `request-accept`. + RequestPresentationInput: + required: + - connectionId + type: object + properties: + connectionId: + type: string + description: The unique identifier of an established connection between + the verifier and the prover. + format: uuid + example: bc528dc8-69f1-4c5a-a508-5f8019047900 + options: + $ref: '#/components/schemas/Options' + proofs: + type: array + items: + $ref: '#/components/schemas/ProofRequestAux' + description: The type of proofs requested in the context of this proof presentation + request (e.g., VC schema, trusted issuers, etc.) + example: [] + credentialFormat: + type: string + description: The credential format (default to 'JWT') + example: JWT + Service: + required: + - id + - type + - serviceEndpoint + type: object + properties: + id: + type: string + description: |- + The id of the service. + Requires a URI fragment when use in create / update DID. + Returns the full ID (with DID prefix) when resolving DID + example: service-1 + type: + description: Service type. Can contain multiple possible values as described + in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) + under the construction section. + example: Single(LinkedDomains) + oneOf: + - type: array + items: + type: string + - type: string + serviceEndpoint: + $ref: '#/components/schemas/Json' + description: A service expressed in the DID document. https://www.w3.org/TR/did-core/#services + Str: + required: + - value + type: object + properties: + value: + type: string + UpdateEntityNameRequest: + required: + - name + type: object + properties: + name: + type: string + description: New name of the entity + example: John Doe + minLength: 1 + maxLength: 128 + UpdateEntityWalletIdRequest: + required: + - walletId + type: object + properties: + walletId: + type: string + description: The walletId owned by the entity + format: uuid + example: 00000000-0000-0000-0000-000000000000 + UpdateManagedDIDRequest: + type: object + properties: + actions: + type: array + items: + $ref: '#/components/schemas/UpdateManagedDIDRequestAction' + UpdateManagedDIDRequestAction: + required: + - actionType + type: object + properties: + actionType: + $ref: '#/components/schemas/ActionType' + addKey: + $ref: '#/components/schemas/ManagedDIDKeyTemplate' + removeKey: + $ref: '#/components/schemas/RemoveEntryById' + addService: + $ref: '#/components/schemas/Service' + removeService: + $ref: '#/components/schemas/RemoveEntryById' + updateService: + $ref: '#/components/schemas/UpdateManagedDIDServiceAction' + patchContext: + $ref: '#/components/schemas/PatchContextAction' + description: |- + A list of actions to perform on DID document. + The field `addKey`, `removeKey`, `addService`, `removeService`, `updateService`, `patchContext` must corresponds to + the `actionType` specified. For example, `addKey` must be present when `actionType` is `ADD_KEY`. + UpdateManagedDIDServiceAction: + required: + - id + type: object + properties: + id: + type: string + description: The id of the service to update + example: service-1 + type: + description: The type of the service + example: LinkedDomains + oneOf: + - type: array + items: + type: string + - type: string + serviceEndpoint: + $ref: '#/components/schemas/Json' + description: A patch to existing Service. 'type' and 'serviceEndpoint' cannot + both be empty. + VerificationMethod: + required: + - id + - type + - controller + - publicKeyJwk + type: object + properties: + id: + type: string + description: The identifier for the verification method. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 + type: + type: string + description: The type of the verification method. + example: JsonWebKey2020 + controller: + type: string + description: The DID that controls the verification method. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + publicKeyJwk: + $ref: '#/components/schemas/PublicKeyJwk' + description: A cryptographic public key expressed in the DID document. https://www.w3.org/TR/did-core/#verification-methods + VerificationPolicy: + required: + - self + - kind + - id + - nonce + - name + - description + - createdAt + - updatedAt + type: object + properties: + self: + type: string + kind: + type: string + id: + type: string + format: uuid + nonce: + type: integer + format: int32 + name: + type: string + description: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + constraints: + type: array + items: + $ref: '#/components/schemas/VerificationPolicyConstraint' + VerificationPolicyConstraint: + required: + - schemaId + type: object + properties: + schemaId: + type: string + trustedIssuers: + type: array + items: + type: string + VerificationPolicyInput: + required: + - name + - description + type: object + properties: + id: + type: string + format: uuid + name: + type: string + description: + type: string + constraints: + type: array + items: + $ref: '#/components/schemas/VerificationPolicyConstraint' + VerificationPolicyPage: + required: + - self + - kind + - pageOf + type: object + properties: + self: + type: string + kind: + type: string + pageOf: + type: string + next: + type: string + previous: + type: string + contents: + type: array + items: + $ref: '#/components/schemas/VerificationPolicy' + WalletDetail: + required: + - id + - name + - createdAt + - updatedAt + type: object + properties: + id: + type: string + description: A wallet ID + format: uuid + example: 00000000-0000-0000-0000-000000000000 + name: + type: string + description: The name of the wallet + example: my-wallet-1 + createdAt: + type: string + description: The `createdAt` timestamp of the wallet. + format: date-time + example: '2023-01-01T00:00:00Z' + updatedAt: + type: string + description: The `updateddAt` timestamp of the wallet. + format: date-time + example: '2023-01-01T00:00:00Z' + WalletDetailPage: + required: + - self + - kind + - pageOf + type: object + properties: + self: + type: string + kind: + type: string + pageOf: + type: string + next: + type: string + previous: + type: string + contents: + type: array + items: + $ref: '#/components/schemas/WalletDetail' + WebhookNotification: + required: + - id + - url + - customHeaders + - createdAt + type: object + properties: + id: + type: string + description: ID of webhook notification resource + format: uuid + example: 00000000-0000-0000-0000-000000000000 + url: + type: string + description: A URL of webhook for event notification + example: http://example.com + customHeaders: + $ref: '#/components/schemas/Map_String' + createdAt: + type: string + description: A time which the webhook notification resource was created. + format: date-time + example: '1970-01-01T00:00:00Z' + WebhookNotificationPage: + required: + - self + - kind + - pageOf + type: object + properties: + self: + type: string + kind: + type: string + pageOf: + type: string + next: + type: string + previous: + type: string + contents: + type: array + items: + $ref: '#/components/schemas/WebhookNotification' + securitySchemes: + adminApiKeyAuth: + type: apiKey + name: x-admin-api-key + in: header + apiKeyAuth: + type: apiKey + name: apikey + in: header + jwtAuth: + type: http + scheme: bearer