Skip to content

Commit

Permalink
Merge branch 'main' into update_graph_sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeCap08055 authored Oct 24, 2024
2 parents 3dde2da + db34494 commit a86e100
Show file tree
Hide file tree
Showing 48 changed files with 1,619 additions and 880 deletions.
6 changes: 5 additions & 1 deletion apps/account-api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ BigInt.prototype['toJSON'] = function () {
* can't connect to Redis at startup)
*/
function startShutdownTimer() {
setTimeout(() => process.exit(1), 10_000);
setTimeout(() => {
logger.log('Shutdown timer expired');
process.exit(0);
}, 10_000);
}

async function bootstrap() {
Expand Down Expand Up @@ -60,6 +63,7 @@ async function bootstrap() {
logger.warn('Received shutdown event');
startShutdownTimer();
await app.close();
logger.warn('app closed');
});

const config = app.get<IAccountApiConfig>(apiConfig.KEY);
Expand Down
3 changes: 2 additions & 1 deletion apps/account-api/src/services/accounts.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { TransactionType } from '#types/account-webhook';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import blockchainConfig, { IBlockchainConfig } from '#blockchain/blockchain.config';
import { ApiPromise } from '@polkadot/api';

@Injectable()
export class AccountsService {
Expand Down Expand Up @@ -70,7 +71,7 @@ export class AccountsService {

// eslint-disable-next-line class-methods-use-this
async signInWithFrequency(request: WalletLoginRequestDto): Promise<WalletLoginResponseDto> {
const api = await this.blockchainService.getApi();
const api = (await this.blockchainService.getApi()) as ApiPromise;
const { providerId } = this.blockchainConf;
if (request.signUp) {
try {
Expand Down
4 changes: 2 additions & 2 deletions apps/account-api/src/services/delegation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ export class DelegationService {
throw new NotFoundException(`Invalid MSA Id ${providerId}`);
}

const providerInfo = await this.blockchainService.api.query.msa.providerToRegistryEntry(providerId);
if (providerInfo.isNone) {
const providerInfo = await this.blockchainService.getProviderToRegistryEntry(providerId);
if (!providerInfo) {
throw new BadRequestException(`Supplied ID not a Provider ${providerId}`);
}

Expand Down
2 changes: 1 addition & 1 deletion apps/account-api/src/services/keys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { HexString } from '@polkadot/util/types';
import {
AddNewPublicKeyAgreementPayloadRequest,
AddNewPublicKeyAgreementRequestDto,
ItemActionType,
ItemizedSignaturePayloadDto,
} from '#types/dtos/account/graphs.request.dto';
import { ItemActionType } from '#types/enums/item-action-type.enum';
import { u8aToHex, u8aWrapBytes } from '@polkadot/util';
import * as BlockchainConstants from '#types/constants/blockchain-constants';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
Expand Down
94 changes: 56 additions & 38 deletions apps/account-api/src/services/siwfV2.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { expect, it, jest } from '@jest/globals';
import { Test, TestingModule } from '@nestjs/testing';
import { BadRequestException } from '@nestjs/common';
import { decodeSignedRequest } from '@projectlibertylabs/siwfv2';
Expand All @@ -14,12 +15,31 @@ import {
validSiwfNewUserResponse,
} from './siwfV2.mock.spec';
import { EnqueueService } from '#account-lib/services/enqueue-request.service';
import { ApiPromise } from '@polkadot/api';
import { mockApiPromise } from '#testlib/polkadot-api.mock.spec';

jest.mock<typeof import('#blockchain/blockchain-rpc-query.service')>('#blockchain/blockchain-rpc-query.service');
jest.mock<typeof import('#account-lib/services/enqueue-request.service')>(
'#account-lib/services/enqueue-request.service',
);
jest.mock('@polkadot/api', () => {
const originalModule = jest.requireActual<typeof import('@polkadot/api')>('@polkadot/api');
return {
__esModules: true,
WsProvider: jest.fn().mockImplementation(() => originalModule.WsProvider),
ApiPromise: jest.fn().mockImplementation(() => ({
...originalModule.ApiPromise,
...mockApiPromise,
})),
};
});

const mockBlockchainConfigProvider = GenerateMockConfigProvider<IBlockchainConfig>('blockchain', {
capacityLimit: { serviceLimit: { type: 'percentage', value: 80n } },
providerId: 1n,
providerSeedPhrase: '//Alice',
frequencyApiWsUrl: new URL('ws://localhost:9944'),
frequencyTimeoutSecs: 10,
isDeployedReadOnly: false,
});

Expand All @@ -44,40 +64,24 @@ const exampleCredentials = [

describe('SiwfV2Service', () => {
let siwfV2Service: SiwfV2Service;

const mockBlockchainService = {
getNetworkType: jest.fn(),
publicKeyToMsaId: jest.fn(),
getApi: jest.fn(),
};

const mockBlockchainServiceProvider = {
provide: BlockchainRpcQueryService,
useValue: mockBlockchainService,
};

const mockEnqueueService = {
enqueueRequest: jest.fn(),
};

const mockEnqueueServiceProvider = {
provide: EnqueueService,
useValue: mockEnqueueService,
};
let blockchainService: BlockchainRpcQueryService;
let enqueueService: EnqueueService;

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
SiwfV2Service,
mockBlockchainServiceProvider,
BlockchainRpcQueryService,
mockAccountApiConfigProvider,
mockBlockchainConfigProvider,
mockEnqueueServiceProvider,
EnqueueService,
],
}).compile();

siwfV2Service = module.get<SiwfV2Service>(SiwfV2Service);
blockchainService = module.get<BlockchainRpcQueryService>(BlockchainRpcQueryService);
enqueueService = module.get<EnqueueService>(EnqueueService);
});

it('should be defined', () => {
Expand Down Expand Up @@ -151,7 +155,7 @@ describe('SiwfV2Service', () => {
jest.spyOn(siwfV2Service as any, 'swifV2Endpoint').mockReturnValue('https://siwf.example.com');

// Mock the global fetch function
global.fetch = jest.fn().mockResolvedValue({
jest.spyOn(global, 'fetch').mockResolvedValue({
ok: true,
status: 200,
json: async () => validSiwfAddDelegationResponsePayload,
Expand Down Expand Up @@ -193,7 +197,7 @@ describe('SiwfV2Service', () => {
jest.spyOn(siwfV2Service as any, 'swifV2Endpoint').mockReturnValue('https://siwf.example.com');

// Mock the global fetch function
global.fetch = jest.fn().mockResolvedValue({
jest.spyOn(global, 'fetch').mockResolvedValue({
ok: false,
status: 400,
json: async () => ({ error: 'Invalid authorization code' }),
Expand Down Expand Up @@ -225,7 +229,7 @@ describe('SiwfV2Service', () => {
jest.spyOn(siwfV2Service as any, 'swifV2Endpoint').mockReturnValue('https://siwf.example.com');

// Mock the global fetch function
global.fetch = jest.fn().mockResolvedValue({
jest.spyOn(global, 'fetch').mockResolvedValue({
ok: true,
status: 200,
json: async () => validSiwfLoginResponsePayload,
Expand Down Expand Up @@ -280,24 +284,24 @@ describe('SiwfV2Service', () => {
});

it('Should parse MSA Id', async () => {
mockBlockchainService.publicKeyToMsaId.mockReturnValueOnce('123456');
jest.spyOn(blockchainService, 'publicKeyToMsaId').mockResolvedValueOnce('123456');

const result = await siwfV2Service.getSiwfV2LoginResponse(validSiwfAddDelegationResponsePayload);

expect(result).toBeDefined();
expect(result.msaId).toEqual('123456');
expect(mockBlockchainService.publicKeyToMsaId).toHaveBeenCalledWith(
expect(blockchainService.publicKeyToMsaId).toHaveBeenCalledWith(
'f6akufkq9Lex6rT8RCEDRuoZQRgo5pWiRzeo81nmKNGWGNJdJ',
);
});

it('Should NOT return an MSA Id if there is none', async () => {
mockBlockchainService.publicKeyToMsaId.mockReturnValueOnce(null);
jest.spyOn(blockchainService, 'publicKeyToMsaId').mockResolvedValueOnce(null);
const result = await siwfV2Service.getSiwfV2LoginResponse(validSiwfAddDelegationResponsePayload);

expect(result).toBeDefined();
expect(result.msaId).toBeUndefined();
expect(mockBlockchainService.publicKeyToMsaId).toHaveBeenCalledWith(
expect(blockchainService.publicKeyToMsaId).toHaveBeenCalledWith(
'f6akufkq9Lex6rT8RCEDRuoZQRgo5pWiRzeo81nmKNGWGNJdJ',
);
});
Expand Down Expand Up @@ -340,21 +344,27 @@ describe('SiwfV2Service', () => {
);

it('should do nothing if there are no chain submissions', async () => {
const enqueueSpy = jest.spyOn(enqueueService, 'enqueueRequest');
const result = await siwfV2Service.queueChainActions(validSiwfLoginResponsePayload);

expect(result).toBeNull();
expect(mockEnqueueService.enqueueRequest).not.toHaveBeenCalled();
expect(enqueueSpy).not.toHaveBeenCalled();
});

it('should return correctly for the add delegation setup', async () => {
const enqueueSpy = jest.spyOn(enqueueService, 'enqueueRequest');
const correctGrantDelegationHash =
'0xed01043c038eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4801bac399831b9e3ad464a16e62ad1252cc8344a2c52f80252b2aa450a06ae2362f6f4afcaca791a81f28eaa99080e2654bdbf1071a276213242fc153cca43cfa8e01000000000000001405000700080009000a0018000000';
mockBlockchainService.getApi.mockReturnValue(
mockApiTxHashes([{ pallet: 'msa', extrinsic: 'grantDelegation', hash: correctGrantDelegationHash }]),
);
jest
.spyOn(blockchainService, 'getApi')
.mockResolvedValue(
mockApiTxHashes([
{ pallet: 'msa', extrinsic: 'grantDelegation', hash: correctGrantDelegationHash },
]) as ApiPromise,
);

await expect(siwfV2Service.queueChainActions(validSiwfAddDelegationResponsePayload)).resolves.not.toThrow();
expect(mockEnqueueService.enqueueRequest).toHaveBeenCalledWith({
expect(enqueueSpy).toHaveBeenCalledWith({
calls: [
{
encodedExtrinsic: correctGrantDelegationHash,
Expand All @@ -367,22 +377,30 @@ describe('SiwfV2Service', () => {
});

it('should return correctly for the new user setup', async () => {
const enqueueSpy = jest.spyOn(enqueueService, 'enqueueRequest');
const correctGrantDelegationHash =
'0xed01043c018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48011a27cb6d79b508e1ffc8d6ae70af78d5b3561cdc426124a06f230d7ce70e757e1947dd1bac8f9e817c30676a5fa6b06510bae1201b698b044ff0660c60f18c8a01000000000000001405000700080009000a0018000000';
const correctStatefulStorageHash =
'0xf501043f068eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48019eb338773b386ded2e3731ba68ba734c80408b3ad24f92ed3c60342d374a32293851fa8e41d722c72a5a4e765a9e401c68570a8c666ab678e4e5d94aa6825d851c0014000000040040eea1e39d2f154584c4b1ca8f228bb49a';
const correctClaimHandleHash =
'0xd9010442008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4801b004140fd8ba3395cf5fcef49df8765d90023c293fde4eaf2e932cc24f74fc51b006c0bebcf31d85565648b4881fa22115e0051a3bdb95ab5bf7f37ac66f798f344578616d706c6548616e646c6518000000';
mockBlockchainService.getApi.mockReturnValue(
jest.spyOn(blockchainService, 'getApi').mockResolvedValue(
mockApiTxHashes([
{ pallet: 'msa', extrinsic: 'createSponsoredAccountWithDelegation', hash: correctGrantDelegationHash },
{ pallet: 'statefulStorage', extrinsic: 'applyItemActionsWithSignatureV2', hash: correctStatefulStorageHash },
{ pallet: 'handles', extrinsic: 'claimHandle', hash: correctClaimHandleHash },
]),
]) as ApiPromise,
);

await expect(siwfV2Service.queueChainActions(validSiwfNewUserResponse)).resolves.not.toThrow();
expect(mockEnqueueService.enqueueRequest).toHaveBeenCalledWith({
try {
await siwfV2Service.queueChainActions(validSiwfNewUserResponse);
} catch (err: any) {
console.error(err);
throw err;
}

// await expect(siwfV2Service.queueChainActions(validSiwfNewUserResponse)).resolves.not.toThrow();
expect(enqueueSpy).toHaveBeenCalledWith({
calls: [
{
encodedExtrinsic: correctGrantDelegationHash,
Expand Down
3 changes: 2 additions & 1 deletion apps/account-api/src/services/siwfV2.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { PublishSIWFSignupRequestDto, SIWFEncodedExtrinsic, TransactionResponse
import { TransactionType } from '#types/account-webhook';
import { isNotNull } from '#utils/common/common.utils';
import { chainSignature, statefulStoragePayload } from '#utils/common/signature.util';
import { ApiPromise } from '@polkadot/api';

@Injectable()
export class SiwfV2Service {
Expand Down Expand Up @@ -107,7 +108,7 @@ export class SiwfV2Service {
return {
pallet,
extrinsicName,
encodedExtrinsic: api.tx.statefulStorage
encodedExtrinsic: (api as ApiPromise).tx.statefulStorage
.applyItemActionsWithSignatureV2(
userPublicKey,
chainSignature(payload.signature),
Expand Down
29 changes: 22 additions & 7 deletions apps/account-api/test/handles.controller.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-undef */
import { HttpStatus, INestApplication, ValidationPipe } from '@nestjs/common';
import { HttpStatus, ValidationPipe, VersioningType } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { EventEmitter2 } from '@nestjs/event-emitter';
import request from 'supertest';
Expand All @@ -9,11 +9,15 @@ import { uniqueNamesGenerator, colors, names } from 'unique-names-generator';
import { ApiModule } from '../src/api.module';
import { setupProviderAndUsers } from './e2e-setup.mock.spec';
import { CacheMonitorService } from '#cache/cache-monitor.service';
import { TimeoutInterceptor } from '#utils/interceptors/timeout.interceptor';
import { NestExpressApplication } from '@nestjs/platform-express';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import { BlockchainRpcQueryService } from '#blockchain/blockchain-rpc-query.service';

let HTTP_SERVER: any;

describe('Handles Controller', () => {
let app: INestApplication;
let app: NestExpressApplication;
let module: TestingModule;
let users: ChainUser[];
let provider: ChainUser;
Expand Down Expand Up @@ -62,18 +66,29 @@ describe('Handles Controller', () => {
imports: [ApiModule],
}).compile();

app = module.createNestApplication();
app = module.createNestApplication({ logger: ['error', 'warn', 'log', 'verbose', 'debug'], rawBody: true });

// Uncomment below to see logs when debugging tests
// module.useLogger(new Logger());

const config = app.get<IAccountApiConfig>(apiConfig.KEY);
app.enableVersioning({ type: VersioningType.URI });
app.enableShutdownHooks();
app.useGlobalPipes();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, enableDebugMessages: true }));
app.useGlobalInterceptors(new TimeoutInterceptor(config.apiTimeoutMs));
app.useBodyParser('json', { limit: config.apiBodyJsonLimit });

const eventEmitter = app.get<EventEmitter2>(EventEmitter2);
eventEmitter.on('shutdown', async () => {
await app.close();
});

app.useGlobalPipes(new ValidationPipe());
app.enableShutdownHooks();

await app.init();

// Make sure we're connected to the chain before running tests
const blockchainService = app.get<BlockchainRpcQueryService>(BlockchainRpcQueryService);
await blockchainService.isReady();

HTTP_SERVER = app.getHttpServer();

// Redis timeout keeping test suite alive for too long; disable
Expand Down
Loading

0 comments on commit a86e100

Please sign in to comment.