import type { CredentialExchangeRecord} from '@aries-framework/core'; import type { EventAnonCredsCredentialOfferGetAll, EventAnonCredsCredentialOfferGetAllInput, EventAnonCredsCredentialOfferGetById, EventAnonCredsCredentialOfferGetByIdInput, EventAnonCredsCredentialRequestGetAll, EventAnonCredsCredentialRequestGetAllInput, EventAnonCredsCredentialRequestGetById, EventAnonCredsCredentialRequestGetByIdInput, EventAnonCredsCredentialsDeleteById, EventAnonCredsCredentialsDeleteByIdInput, EventAnonCredsCredentialsGetAllInput, EventAnonCredsCredentialsGetByIdInput, EventDidcommAnonCredsCredentialsOffer, EventDidcommAnonCredsCredentialsOfferInput, EventDidcommAnonCredsCredentialsOfferToSelf, EventDidcommAnonCredsCredentialsOfferToSelfInput } from '@ocm/shared'; import { AutoAcceptCredential, CredentialState } from '@aries-framework/core'; import { Injectable } from '@nestjs/common'; import { logger } from '@ocm/shared'; import { MetadataTokens } from '../../common/constants.js'; import { WithTenantService } from '../withTenantService.js'; @Injectable() export class AnonCredsCredentialsService { public constructor(private withTenantService: WithTenantService) {} public async getAll({ tenantId, }: EventAnonCredsCredentialsGetAllInput): Promise< Array<CredentialExchangeRecord> > { return this.withTenantService.invoke(tenantId, (t) => t.credentials.getAll(), ); } public async getAllOffers({ tenantId, }: EventAnonCredsCredentialOfferGetAllInput): Promise< EventAnonCredsCredentialOfferGetAll['data'] > { return this.withTenantService.invoke(tenantId, (t) => t.credentials.findAllByQuery({ $or: [ { state: CredentialState.OfferSent }, { state: CredentialState.OfferReceived }, ], }), ); } public async getAllRequests({ tenantId, }: EventAnonCredsCredentialRequestGetAllInput): Promise< EventAnonCredsCredentialRequestGetAll['data'] > { return this.withTenantService.invoke(tenantId, (t) => t.credentials.findAllByQuery({ $or: [ { state: CredentialState.RequestSent }, { state: CredentialState.RequestReceived }, ], }), ); } public async deleteById({ tenantId, credentialRecordId, }: EventAnonCredsCredentialsDeleteByIdInput): Promise< EventAnonCredsCredentialsDeleteById['data'] > { return this.withTenantService.invoke(tenantId, async (t) => { await t.credentials.deleteById(credentialRecordId); return {}; }); } public async getById({ tenantId, credentialRecordId, }: EventAnonCredsCredentialsGetByIdInput): Promise<CredentialExchangeRecord | null> { return this.withTenantService.invoke(tenantId, (t) => t.credentials.findById(credentialRecordId), ); } public async getOfferById({ tenantId, credentialOfferId, }: EventAnonCredsCredentialOfferGetByIdInput): Promise< EventAnonCredsCredentialOfferGetById['data'] > { return this.withTenantService.invoke(tenantId, async (t) => { const credential = await t.credentials.findById(credentialOfferId); if ( credential && credential.state !== CredentialState.OfferSent && credential.state !== CredentialState.OfferReceived ) { logger.warn( `Credential '${credentialOfferId}' does exist, but is not in offer state. Actual state: ${credential.state}`, ); } return credential; }); } public async getRequestById({ tenantId, credentialRequestId, }: EventAnonCredsCredentialRequestGetByIdInput): Promise< EventAnonCredsCredentialRequestGetById['data'] > { return this.withTenantService.invoke(tenantId, async (t) => { const credential = await t.credentials.findById(credentialRequestId); if ( credential && credential.state !== CredentialState.RequestSent && credential.state !== CredentialState.RequestReceived ) { logger.warn( `Credential '${credentialRequestId}' does exist, but is not in a request state. Actual state: ${credential.state}`, ); } return credential; }); } public async offer({ tenantId, connectionId, credentialDefinitionId, attributes, }: EventDidcommAnonCredsCredentialsOfferInput): Promise< EventDidcommAnonCredsCredentialsOffer['data'] > { return this.withTenantService.invoke(tenantId, (t) => t.credentials.offerCredential({ protocolVersion: 'v2', connectionId, credentialFormats: { anoncreds: { credentialDefinitionId, attributes }, }, }), ); } public async offerToSelf({ tenantId, credentialDefinitionId, attributes, }: EventDidcommAnonCredsCredentialsOfferToSelfInput): Promise< EventDidcommAnonCredsCredentialsOfferToSelf['data'] > { return this.withTenantService.invoke(tenantId, async (t) => { const connections = await t.connections.getAll(); const connection = connections.find((c) => { const metadata = c.metadata.get<{ withSelf: boolean }>( MetadataTokens.GAIA_X_CONNECTION_METADATA_KEY, ); return metadata && metadata.withSelf === true; }); if (!connection) { throw new Error( 'Cannot offer a credential to yourself as there is no connection', ); } if (!connection.isReady) { throw new Error('Connection with yourself is not ready, yet'); } return t.credentials.offerCredential({ protocolVersion: 'v2', autoAcceptCredential: AutoAcceptCredential.Always, connectionId: connection.id, credentialFormats: { anoncreds: { credentialDefinitionId, attributes }, }, }); }); } }