Skip to content

Commit

Permalink
Schema-service:Interface Implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
ashwin275 committed Dec 13, 2024
1 parent aa5b102 commit 391e05e
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ENABLE_AUTH=false

# Anchor to cord block chain
# Flag to enable/disable anchoring to Cord blockchain
ANCHOR_TO_CORD=false
ANCHOR_TO_CORD=true

# Base URL for Issuer Agent
# This is the service responsible for issuing credentials
Expand Down
4 changes: 4 additions & 0 deletions services/credential-schema/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { PrismaHealthIndicator } from './utils/prisma.health';
import { PrismaClient } from '@prisma/client';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth/auth.guard';
import { AnchorCordService } from './schema/implementations/anchor-cord.service';
import { BlockchainAnchorFactory } from './schema/factories/blockchain-anchor.factory';

@Module({
imports: [
Expand All @@ -32,6 +34,8 @@ import { AuthGuard } from './auth/auth.guard';
UtilsService,
PrismaHealthIndicator,
PrismaClient,
AnchorCordService,
BlockchainAnchorFactory,
{
provide: APP_GUARD,
useClass: AuthGuard,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { SchemaService } from '../schema/schema.service';
import { HttpModule } from '@nestjs/axios';
import { UtilsService } from '../utils/utils.service';
import { PrismaClient } from '@prisma/client';
import { BlockchainAnchorFactory } from 'src/schema/factories/blockchain-anchor.factory';
import { AnchorCordService } from 'src/schema/implementations/anchor-cord.service';

@Module({
imports: [HttpModule],
Expand All @@ -15,6 +17,8 @@ import { PrismaClient } from '@prisma/client';
ValidateTemplateService,
SchemaService,
UtilsService,
BlockchainAnchorFactory,
AnchorCordService,
],
controllers: [RenderingTemplatesController],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Injectable, BadRequestException } from '@nestjs/common';
import { AnchorCordService } from '../implementations/anchor-cord.service';
import { BlockchainAnchor } from '../interfaces/blockchain_anchor.interface';

/**
* Factory class to dynamically resolve the appropriate BlockchainAnchor service.
* It uses the specified method to determine which implementation to return.
*/
@Injectable()
export class BlockchainAnchorFactory {
/**
* Constructor for the BlockchainAnchorFactory.
* @param cordService - An instance of AnchorCordService, which handles CORD-specific anchoring logic.
*/
constructor(private readonly cordService: AnchorCordService) {}

/**
* Resolves the appropriate BlockchainAnchor service based on the provided method.
* @param method - The blockchain method (e.g., 'cord').
* @returns The service instance corresponding to the specified method or null if no method is provided.
* @throws
*/
getAnchorService(method?: string): BlockchainAnchor | null {
// If no method is specified, return null to indicate no anchoring is required
if (!method) {
return null;
}

// Determine the appropriate service implementation based on the method
switch (method) {
case 'cord':
// Return the CORD-specific implementation
return this.cordService;
default:
throw new BadRequestException(`Unsupported blockchain method: ${method}`);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Injectable, Logger, InternalServerErrorException } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { BlockchainAnchor } from '../interfaces/blockchain_anchor.interface';
import {BadRequestException} from '@nestjs/common';
@Injectable()
export class AnchorCordService implements BlockchainAnchor {
private readonly logger = new Logger(AnchorCordService.name);

constructor(private readonly httpService: HttpService) {}

async anchorSchema(body: any): Promise<any> {
try {
const response = await this.httpService.axiosRef.post(
`${process.env.ISSUER_AGENT_BASE_URL}/schema`,
body,
);
return response.data.result;
} catch (err) {
const errorDetails = {
message: err.message,
status: err.response?.status,
statusText: err.response?.statusText,
data: err.response?.data,
headers: err.response?.headers,
request: err.config,
};

this.logger.error(
'Error anchoring schema to Cord blockchain',
errorDetails,
);
throw new InternalServerErrorException(
'Failed to anchor schema to Cord blockchain',
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface BlockchainAnchor {
/**
* Anchors a Scheam to the blockchain.
* @param body The request payload for anchoring.
* @returns The anchored Schema or related data.
*/
anchorSchema(body: any): Promise<any>;
}

4 changes: 3 additions & 1 deletion services/credential-schema/src/schema/schema.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { SchemaService } from './schema.service';
import { HttpModule } from '@nestjs/axios';
import { UtilsService } from '../utils/utils.service';
import { PrismaClient } from '@prisma/client';
import { AnchorCordService } from './implementations/anchor-cord.service';
import { BlockchainAnchorFactory } from './factories/blockchain-anchor.factory';

@Module({
imports: [HttpModule],
controllers: [SchemaController],
providers: [SchemaService, PrismaClient, UtilsService],
providers: [SchemaService, PrismaClient, UtilsService,AnchorCordService,BlockchainAnchorFactory],
})
export class SchemaModule {}
104 changes: 56 additions & 48 deletions services/credential-schema/src/schema/schema.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import { DefinedError } from 'ajv';
import { CreateCredentialDTO } from './dto/create-credentials.dto';
import { UtilsService } from '../utils/utils.service';
import { GetCredentialSchemaDTO } from './dto/getCredentialSchema.dto';
import { BlockchainAnchorFactory } from './factories/blockchain-anchor.factory';

@Injectable()
export class SchemaService {
constructor(
private readonly prisma: PrismaClient,
private readonly utilService: UtilsService,
) {}
private readonly blockchainFactory: BlockchainAnchorFactory
) { }
private logger = new Logger(SchemaService.name);
private semanticVersionRegex =
/^(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-]+)*))?$/;
Expand Down Expand Up @@ -123,31 +125,33 @@ export class SchemaService {
};
});
}
private shouldAnchorToCord(): boolean {
return (


/**
* Determines if anchoring to a blockchain is enabled based on environment variables.
* Checks for specific blockchain configurations and returns the appropriate method.
* @returns The blockchain method (e.g., 'cord', 'solana') if anchoring is enabled; otherwise, null.
*/
private shouldAnchorToBlockchain(): string | null {
// Check if the environment variable ANCHOR_TO_CORD is set to 'true' for the CORD blockchain
if (
process.env.ANCHOR_TO_CORD &&
process.env.ANCHOR_TO_CORD.toLowerCase().trim() === 'true'
);
}
) {
return 'cord'; // Return 'cord' as the service method if CORD anchoring is enabled
}

private async anchorSchemaToCord(schemaData: any): Promise<string | null> {
try {
const anchorResponse = await this.utilService.anchorSchema({
schema: schemaData,
});

this.logger.debug(
'Schema successfully anchored to Cord blockchain',
anchorResponse,
);

return anchorResponse.schemaId;
} catch (err) {
this.logger.error('Failed to anchor schema to Cord blockchain', err);
throw new InternalServerErrorException(
'Failed to anchor schema to Cord blockchain',
);
// Add additional checks here for other blockchains, e.g.,Solana, Ethereum, Polkadot
/*
if (
process.env.ANCHOR_TO_SOLANA &&
process.env.ANCHOR_TO_SOLANA.toLowerCase().trim() === 'true'
) {
return 'solana'; // Return 'solana' if solana anchoring is enabled
}
*/

return null; // Return null if no blockchain anchoring is required
}

async createCredentialSchema(
Expand All @@ -157,9 +161,9 @@ export class SchemaService {
) {
const data = createCredentialDto.schema;
const tags = createCredentialDto.tags;
let cordSchemaId: string | null = null;
let blockchainSchemaId: string | null = null;
let did: string | null = null;

// Validate the credential schema
if (!validate(data)) {
this.logger.log('Schema validation failed', validate.errors.join('\n'));
Expand All @@ -170,12 +174,16 @@ export class SchemaService {
`Schema validation failed with the following errors: ${validate.errors.join('\n')}`,
);
}

// Check if ANCHOR_TO_CORD is enabled, and anchor the schema to Cord if it is
if (this.shouldAnchorToCord()) {
// Anchor the schema to Cord blockchain and retrieve the cordSchemaId (which acts as the DID)
cordSchemaId = await this.anchorSchemaToCord(data);
did = cordSchemaId;
// Check if anchoring to blockchain is enabled and get the method
const method = this.shouldAnchorToBlockchain();

if (method) {
// Get the appropriate service from the factory
const anchorService = this.blockchainFactory.getAnchorService(method);
const response = await anchorService.anchorSchema(data);

blockchainSchemaId = response.schemaId;
did = blockchainSchemaId;
} else if (generateDID) {
// Generate a DID if anchoring is not enabled and generateDID is true
const didBody = {
Expand All @@ -192,19 +200,19 @@ export class SchemaService {
},
],
};

// Generate DID
const generatedDidResponse = await this.utilService.generateDID(didBody);
this.logger.debug('DID received from identity service', generatedDidResponse);
did = generatedDidResponse?.id || null;
did = generatedDidResponse?.id || null;

}


const credSchema = {
schema: {
type: data.type,
id: did ? did : data.id,
id: did ? did : data.id,
version: data.version ? data.version : '0.0.0',
name: data.name,
author: data.author,
Expand All @@ -215,16 +223,16 @@ export class SchemaService {
tags: tags,
status: createCredentialDto.status,
deprecatedId: createCredentialDto.deprecatedId,
blockchainStatus: cordSchemaId ? 'ANCHORED' : 'PENDING',
blockchainStatus: blockchainSchemaId ? 'ANCHORED' : 'PENDING',
};

// // sign the credential schema (only the schema part of the credSchema object above since it is the actual schema)
// // const proof = await this.utilService.sign(
// // credSchema.schema.author,
// // credSchema.schema,
// // );
// // credSchema.schema.proof = proof;
// // sign the credential schema (only the schema part of the credSchema object above since it is the actual schema)
// // const proof = await this.utilService.sign(
// // credSchema.schema.author,
// // credSchema.schema,
// // );
// // credSchema.schema.proof = proof;

// Save the credential schema to the database
try {
const resp = await this.prisma.verifiableCredentialSchema.create({
Expand All @@ -240,24 +248,24 @@ export class SchemaService {
proof: credSchema.schema.proof as Prisma.JsonValue || undefined,
tags: credSchema.tags as string[],
deprecatedId: deprecatedId,
blockchainStatus: cordSchemaId ? 'ANCHORED' : 'PENDING',
blockchainStatus: blockchainSchemaId ? 'ANCHORED' : 'PENDING',
},
});


credSchema['createdAt'] = resp.createdAt;
credSchema['updatedAt'] = resp.updatedAt;
credSchema['deletedAt'] = resp.deletedAt;
credSchema['createdBy'] = resp.createdBy;
credSchema['updatedBy'] = resp.updatedBy;

return credSchema;
} catch (err) {
this.logger.error('Error saving schema to db', err);
throw new InternalServerErrorException('Error saving schema to db');
}
}


private formatResponse(schema: VerifiableCredentialSchema) {
return JSON.parse(
Expand Down
26 changes: 0 additions & 26 deletions services/credential-schema/src/utils/utils.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,4 @@ export class UtilsService {
}
}

async anchorSchema(body: any): Promise<any> {
try {
const response = await this.httpService.axiosRef.post(
`${process.env.ISSUER_AGENT_BASE_URL}/schema`,
body,
);
return response.data;
} catch (err) {
const errorDetails = {
message: err.message,
status: err.response?.status,
statusText: err.response?.statusText,
data: err.response?.data,
headers: err.response?.headers,
request: err.config,
};

this.logger.error(
'Error anchoring schema to Cord blockchain',
errorDetails,
);
throw new InternalServerErrorException(
'Failed to anchor schema to Cord blockchain',
);
}
}
}
6 changes: 0 additions & 6 deletions services/identity-service/src/did/did.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,6 @@ export class DidService {
return this.resolveDID(webDidId);
}

private shouldAnchorToCord(): boolean {
return (
process.env.ANCHOR_TO_CORD &&
process.env.ANCHOR_TO_CORD.toLowerCase().trim() === 'true'
);
}

/**
* Determines if anchoring to a blockchain is enabled based on environment variables.
Expand Down

0 comments on commit 391e05e

Please sign in to comment.