diff --git a/agent-swagger.json b/agent-swagger.json index 386748ed774ada17758d9e65efb0fa075d8f137e..0f7708e8f07f507cb284bed4d82a0d82a28d357d 100644 --- a/agent-swagger.json +++ b/agent-swagger.json @@ -2,6 +2,58 @@ "openapi": "3.0.0", "paths": { "/api/v1/invitations": { + "get": { + "operationId": "RestController_fetchInvitations", + "parameters": [ + { + "name": "states", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "initial", + "await-response", + "prepare-response", + "done" + ] + } + } + }, + { + "name": "roles", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "sender", + "receiver" + ] + } + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CreateInvitationResponseDto" + } + } + } + } + } + } + }, "post": { "operationId": "RestController_createInvitation", "parameters": [], @@ -58,7 +110,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateInvitationResponseDto" + "$ref": "#/components/schemas/AcceptInvitationRequestDto" } } } @@ -893,6 +945,53 @@ "servers": [], "components": { "schemas": { + "CreateInvitationResponseDto": { + "type": "object", + "properties": { + "invitationUrl": { + "type": "string", + "description": "Example of long invitation url", + "example": "http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ" + }, + "shortInvitationUrl": { + "type": "string", + "example": "http://0.0.0.0:8001/invitations/85a7c179-122b-4d2d-9a86-d92ad31cef2b" + }, + "outOfBandId": { + "type": "string", + "example": "85a7c179-122b-4d2d-9a86-d92ad31cef2b" + }, + "role": { + "example": "sender", + "enum": [ + "sender", + "receiver" + ], + "type": "string" + }, + "state": { + "example": "Done", + "enum": [ + "initial", + "await-response", + "prepare-response", + "done" + ], + "type": "string" + }, + "id": { + "type": "string" + }, + "createdAt": { + "format": "date-time", + "type": "string" + }, + "updatedAt": { + "format": "date-time", + "type": "string" + } + } + }, "CreateInvitationRequestDto": { "type": "object", "properties": { @@ -913,7 +1012,7 @@ } } }, - "CreateInvitationResponseDto": { + "AcceptInvitationRequestDto": { "type": "object", "properties": { "invitationUrl": { @@ -924,10 +1023,6 @@ "shortInvitationUrl": { "type": "string", "example": "http://0.0.0.0:8001/invitations/85a7c179-122b-4d2d-9a86-d92ad31cef2b" - }, - "outOfBandId": { - "type": "string", - "example": "85a7c179-122b-4d2d-9a86-d92ad31cef2b" } }, "required": [ @@ -983,8 +1078,7 @@ } }, "required": [ - "state", - "id" + "state" ] }, "CreateSchemaRequestDto": { @@ -1051,8 +1145,7 @@ "required": [ "name", "attributes", - "version", - "id" + "version" ] }, "IdReqDto": { @@ -1093,8 +1186,7 @@ "required": [ "schemaId", "issuerId", - "tag", - "id" + "tag" ] }, "CreateCredentialDefinitionRequestDto": { @@ -1198,8 +1290,7 @@ "required": [ "state", "credentialRecordType", - "tags", - "id" + "tags" ] }, "CredentialOfferResponseDto": { @@ -1329,8 +1420,7 @@ "connectionId", "role", "sentTime", - "content", - "id" + "content" ] }, "ProofRecordDto": { @@ -1370,8 +1460,7 @@ }, "required": [ "state", - "tags", - "id" + "tags" ] }, "ProofFormatDataDto": { diff --git a/libs/askar/src/askar-nats/event.handler.service.ts b/libs/askar/src/askar-nats/event.handler.service.ts index 57afc535ed0c5b9ba5981387f1ddfb8015ef657d..e563f4479ccbb79f0583ce581641637c2231a986 100644 --- a/libs/askar/src/askar-nats/event.handler.service.ts +++ b/libs/askar/src/askar-nats/event.handler.service.ts @@ -40,6 +40,7 @@ import { MESSAGE_DELETE, IdReqDto, AcceptCredentialDto, + AcceptInvitationRequestDto, } from "@ocm-engine/dtos"; @Injectable() @@ -57,7 +58,7 @@ export class EventHandlerService { break; case CONNECTION_ACCEPT: - dto = event.data as CreateInvitationResponseDto; + dto = event.data as AcceptInvitationRequestDto; data = await this.agentService.acceptInvitation(dto.invitationUrl); break; diff --git a/libs/askar/src/askar-rest/rest.controller.ts b/libs/askar/src/askar-rest/rest.controller.ts index e12bdd642916e7aa29c0d084e097e71bc015a242..cf4f1dd1d2576f1c368ace73f3b71849f5d891e4 100644 --- a/libs/askar/src/askar-rest/rest.controller.ts +++ b/libs/askar/src/askar-rest/rest.controller.ts @@ -12,7 +12,6 @@ import { import { AgentService } from "../askar/agent.service"; import { CreateCredentialDefinitionRequestDto, - CreateInvitationResponseDto, OfferCredentialRequestDto, RequestProofDto, CreateSchemaRequestDto, @@ -24,6 +23,8 @@ import { ProofFilterDto, AcceptCredentialDto, CreateInvitationRequestDto, + InvitationFilterDto, + AcceptInvitationRequestDto, } from "@ocm-engine/dtos"; import { AllExceptionsHandler } from "./exception.handler"; import { DidResolutionResult } from "@aries-framework/core"; @@ -33,6 +34,11 @@ import { DidResolutionResult } from "@aries-framework/core"; export class RestController { constructor(private readonly agentService: AgentService) {} + @Get("/invitations") + async fetchInvitations(@Query() filter: InvitationFilterDto) { + return this.agentService.fetchInvitations(filter); + } + @Post("/invitations") createInvitation( @Body() createInvitationRequestDto: CreateInvitationRequestDto, @@ -47,7 +53,7 @@ export class RestController { @Post("/invitations/accept") async acceptInvitation( - @Body() createInvitationDto: CreateInvitationResponseDto, + @Body() createInvitationDto: AcceptInvitationRequestDto, ) { const url = createInvitationDto.invitationUrl || diff --git a/libs/askar/src/askar/agent.service.ts b/libs/askar/src/askar/agent.service.ts index f68a4925283c56b008230588321d554655717643..f648dccf3f4aed0e64a1319fbc5278d1bfe49d65 100644 --- a/libs/askar/src/askar/agent.service.ts +++ b/libs/askar/src/askar/agent.service.ts @@ -26,6 +26,7 @@ import { CredentialFormatDataDto, ProofFormatDataDto, CreateInvitationRequestDto, + InvitationFilterDto, } from "@ocm-engine/dtos"; import { AutoAcceptCredential, @@ -37,6 +38,7 @@ import { ProofState, Query, ProofExchangeRecord, + OutOfBandRecord, } from "@aries-framework/core"; import { AnonCredsRequestedAttribute } from "@aries-framework/anoncreds"; import { uuid } from "@aries-framework/core/build/utils/uuid"; @@ -63,6 +65,10 @@ export class AgentService { }); response.shortInvitationUrl = `${this.askar.agentConfig.agentPeerAddress}/invitations/${outOfBoundRecord.outOfBandInvitation.id}`; response.outOfBandId = outOfBoundRecord.id; + response.createdAt = outOfBoundRecord.createdAt; + response.updatedAt = outOfBoundRecord.updatedAt; + response.role = outOfBoundRecord.role; + response.state = outOfBoundRecord.state; return response; }; @@ -94,6 +100,41 @@ export class AgentService { return this.askar.agent.oob.deleteById(id); }; + fetchInvitations = async (filter: InvitationFilterDto) => { + const query: Query<OutOfBandRecord>[] = []; + + if (filter.states) { + const stateQuery: Query<OutOfBandRecord> = { + $or: filter.states.map((state) => ({ state })), + }; + query.push(stateQuery); + } + + if (filter.roles) { + const roleQuery: Query<OutOfBandRecord> = { + $or: filter.roles.map((role) => ({ role })), + }; + + query.push(roleQuery); + } + + const invitations = await this.askar.agent.oob.findAllByQuery({ + $and: query, + }); + + const invitationsResponse = invitations.map((invitation) => { + const response = new CreateInvitationResponseDto(); + response.outOfBandId = invitation.id; + response.createdAt = invitation.createdAt; + response.updatedAt = invitation.updatedAt; + response.role = invitation.role; + response.state = invitation.state; + return response; + }); + + return invitationsResponse; + }; + async fetchConnections(): Promise<ConnectionRecordDto[]> { const agentResponse = await this.askar.agent.connections.getAll(); diff --git a/libs/dtos/src/dtos/generics/base.record.dto.ts b/libs/dtos/src/dtos/generics/base.record.dto.ts index d594a71f3aa3011d3688120e537ab27ba171d76a..1654e22368f896de18b72e5ad8a0307d3676d811 100644 --- a/libs/dtos/src/dtos/generics/base.record.dto.ts +++ b/libs/dtos/src/dtos/generics/base.record.dto.ts @@ -1,9 +1,8 @@ -import { IsDateString, IsNotEmpty, IsString } from "class-validator"; +import { IsDateString, IsString } from "class-validator"; export class BaseRecordDto { @IsString() - @IsNotEmpty() - id: string; + id?: string; @IsDateString() createdAt?: Date; diff --git a/libs/dtos/src/dtos/generics/invitation.filter.dto.ts b/libs/dtos/src/dtos/generics/invitation.filter.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..08f0b3660ebbaaf3af6792a6f90b6019983f2f9e --- /dev/null +++ b/libs/dtos/src/dtos/generics/invitation.filter.dto.ts @@ -0,0 +1,13 @@ +import { OutOfBandRole, OutOfBandState } from "@aries-framework/core"; +import { Transform } from "class-transformer"; +import { IsOptional } from "class-validator"; + +export class InvitationFilterDto { + @IsOptional() + @Transform(({ value }) => (Array.isArray(value) ? value : Array(value))) + states?: Array<OutOfBandState>; + + @IsOptional() + @Transform(({ value }) => (Array.isArray(value) ? value : Array(value))) + roles?: Array<OutOfBandRole>; +} diff --git a/libs/dtos/src/dtos/requests/accept.invitation.request.dto.ts b/libs/dtos/src/dtos/requests/accept.invitation.request.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..62e8d99d10747b50455edf6e4e4b57de3f6977d2 --- /dev/null +++ b/libs/dtos/src/dtos/requests/accept.invitation.request.dto.ts @@ -0,0 +1,23 @@ +import { IsString, IsNotEmpty, ValidateIf, IsUrl } from "class-validator"; + +export class AcceptInvitationRequestDto { + /** + * Example of long invitation url + * @example "http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ" + */ + @IsString() + @IsNotEmpty() + @IsUrl() + @ValidateIf((o) => o.shortInvitationUrl === undefined) + invitationUrl: string; + + /** + * + * @example "http://0.0.0.0:8001/invitations/85a7c179-122b-4d2d-9a86-d92ad31cef2b" + */ + @IsString() + @IsNotEmpty() + @IsUrl() + @ValidateIf((o) => o.invitationUrl === undefined) + shortInvitationUrl: string; +} diff --git a/libs/dtos/src/dtos/requests/create.invitation.request.dto.ts b/libs/dtos/src/dtos/requests/create.invitation.request.dto.ts index ff81e588d297d9002f78c850054fe51f656fafb0..d1e82572a3756e3771a973ae5f432e437a1799b6 100644 --- a/libs/dtos/src/dtos/requests/create.invitation.request.dto.ts +++ b/libs/dtos/src/dtos/requests/create.invitation.request.dto.ts @@ -1,4 +1,10 @@ -import { IsBoolean, IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { + IsBoolean, + IsNotEmpty, + IsOptional, + IsString, + IsUrl, +} from "class-validator"; export class CreateInvitationRequestDto { @IsOptional() @@ -19,6 +25,7 @@ export class CreateInvitationRequestDto { @IsOptional() @IsString() @IsNotEmpty() + @IsUrl() imageUrl?: string; @IsOptional() diff --git a/libs/dtos/src/dtos/responses/create.invitation.response.dto.ts b/libs/dtos/src/dtos/responses/create.invitation.response.dto.ts index 238bb3607f4cfd6f5383452fb567d4f5f1c80dc6..e00443383aa9d4692b50da7d3e73c3b0b729e64e 100644 --- a/libs/dtos/src/dtos/responses/create.invitation.response.dto.ts +++ b/libs/dtos/src/dtos/responses/create.invitation.response.dto.ts @@ -1,6 +1,8 @@ import { IsNotEmpty, IsOptional, IsString, ValidateIf } from "class-validator"; +import { BaseRecordDto } from "../generics/base.record.dto"; +import { OutOfBandRole, OutOfBandState } from "@aries-framework/core"; -export class CreateInvitationResponseDto { +export class CreateInvitationResponseDto extends BaseRecordDto { /** * Example of long invitation url * @example "http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ" @@ -8,7 +10,7 @@ export class CreateInvitationResponseDto { @IsString() @IsNotEmpty() @ValidateIf((o) => o.shortInvitationUrl === undefined) - public invitationUrl: string; + invitationUrl?: string; /** * @@ -17,13 +19,27 @@ export class CreateInvitationResponseDto { @IsString() @IsNotEmpty() @ValidateIf((o) => o.invitationUrl === undefined) - public shortInvitationUrl: string; + shortInvitationUrl?: string; /** * @example "85a7c179-122b-4d2d-9a86-d92ad31cef2b" */ + @IsOptional() @IsString() @IsNotEmpty() - @IsOptional() outOfBandId?: string; + + /** + * @example "sender" + */ + @IsOptional() + @IsString() + role?: OutOfBandRole; + + /** + * @example "Done" + */ + @IsOptional() + @IsString() + state?: OutOfBandState; } diff --git a/libs/dtos/src/index.ts b/libs/dtos/src/index.ts index 18cb1e41ef1690281c887caa7c98910b4093d3eb..8589661be71e8dc7b9363e6a29083e6eaa5b2df3 100644 --- a/libs/dtos/src/index.ts +++ b/libs/dtos/src/index.ts @@ -11,9 +11,11 @@ export * from "./dtos/generics/proof.filter.dto"; export * from "./dtos/generics/schema.record.dto"; export * from "./dtos/generics/message.record.dto"; export * from "./dtos/generics/message.filter.dto"; +export * from "./dtos/generics/invitation.filter.dto"; export * from "./dtos/requests/accept.proof.dto"; export * from "./dtos/requests/accept.credential.dto"; +export * from "./dtos/requests/accept.invitation.request.dto"; export * from "./dtos/requests/id.req.dto"; export * from "./dtos/requests/create.schema.request.dto";