diff --git a/apps/account-api/k6-test/README.md b/apps/account-api/k6-test/README.md index c3792a97..33427a3e 100644 --- a/apps/account-api/k6-test/README.md +++ b/apps/account-api/k6-test/README.md @@ -17,7 +17,7 @@ Note that the default iteration count and VU count is 1. So each request in each ## Running the script -To run the script, you need to have k6 installed. You can download it from [here](https://k6.io/docs/getting-started/installation). +To run the script, you need to have k6 installed. You can download it from [here](https://grafana.com/docs/k6/latest/set-up/install-k6/). To run the script, execute the following command: diff --git a/apps/account-api/src/controllers/v1/accounts-v1.controller.ts b/apps/account-api/src/controllers/v1/accounts-v1.controller.ts index d89ca641..9dceaea0 100644 --- a/apps/account-api/src/controllers/v1/accounts-v1.controller.ts +++ b/apps/account-api/src/controllers/v1/accounts-v1.controller.ts @@ -6,8 +6,8 @@ import { WalletLoginResponseDto } from '#types/dtos/account/wallet.login.respons import { Body, Controller, Get, Post, HttpCode, HttpStatus, Logger, Param, HttpException } from '@nestjs/common'; import { ApiBody, ApiOkResponse, ApiCreatedResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; import { ConfigService } from '#account-lib/config'; -import { SignerPayloadRaw } from '@polkadot/types/types'; import { RetireMsaRequestDto, TransactionResponse } from '#types/dtos/account'; +import { AccountIdDto, MsaIdDto } from '#types/dtos/common'; @Controller('v1/accounts') @ApiTags('v1/accounts') @@ -46,7 +46,7 @@ export class AccountsControllerV1 { * @returns A promise that resolves to an Account object => {msaId, handle}. * @throws An error if the account cannot be found. */ - async getAccountForMsa(@Param('msaId') msaId: string): Promise { + async getAccountForMsa(@Param() { msaId }: MsaIdDto): Promise { try { this.logger.debug(`Received request to get account with msaId: ${msaId}`); const account = await this.accountsService.getAccount(msaId); @@ -69,7 +69,7 @@ export class AccountsControllerV1 { * @returns A promise that resolves to an Account object => {msaId, handle}. * @throws An error if the msaId or account cannot be found. */ - async getAccountForAccountId(@Param('accountId') accountId: string): Promise { + async getAccountForAccountId(@Param() { accountId }: AccountIdDto): Promise { try { this.logger.debug(`Received request to get account with accountId: ${accountId}`); const response = await this.accountsService.getMsaIdForAccountId(accountId); @@ -114,7 +114,7 @@ export class AccountsControllerV1 { * @returns A promise that resolves to an object consisting of a retire msa encodedExtrinsic hex string and payloadToSign hex string. * @throws An error if the payload fails to be created. */ - async getRetireMsaPayload(@Param('accountId') accountId: string): Promise { + async getRetireMsaPayload(@Param() { accountId }: AccountIdDto): Promise { try { const result = await this.accountsService.getRetireMsaPayload(accountId); if (result) return result; diff --git a/apps/account-api/src/controllers/v1/delegation-v1.controller.ts b/apps/account-api/src/controllers/v1/delegation-v1.controller.ts index 91aa63d6..836cf2a7 100644 --- a/apps/account-api/src/controllers/v1/delegation-v1.controller.ts +++ b/apps/account-api/src/controllers/v1/delegation-v1.controller.ts @@ -19,6 +19,7 @@ import { UseGuards, } from '@nestjs/common'; import { ApiBody, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { AccountIdDto, MsaIdDto, ProviderMsaIdDto } from '#types/dtos/common'; @Controller({ version: '1', path: 'delegation' }) @ApiTags('delegation') @@ -41,7 +42,7 @@ export class DelegationControllerV1 { * @returns A Promise that resolves to a DelegationResponse object representing the delegation. * @throws HttpException if the delegation cannot be found. */ - async getDelegation(@Param('msaId') msaId: string): Promise { + async getDelegation(@Param() { msaId }: MsaIdDto): Promise { try { const delegation = await this.delegationService.getDelegation(msaId); return delegation; @@ -70,8 +71,8 @@ export class DelegationControllerV1 { * @throws {HttpException} If there is an error generating the RevokeDelegationPayload. */ async getRevokeDelegationPayload( - @Param('accountId') accountId: string, - @Param('providerId') providerId: string, + @Param() { accountId }: AccountIdDto, + @Param() { providerId }: ProviderMsaIdDto, ): Promise { try { this.logger.verbose(`Getting RevokeDelegationPayload for account ${accountId} and provider ${providerId}`); diff --git a/apps/account-api/src/controllers/v1/handles-v1.controller.ts b/apps/account-api/src/controllers/v1/handles-v1.controller.ts index 55f65297..ac69ece1 100644 --- a/apps/account-api/src/controllers/v1/handles-v1.controller.ts +++ b/apps/account-api/src/controllers/v1/handles-v1.controller.ts @@ -24,6 +24,7 @@ import { HandleResponseDto } from '#types/dtos/account/accounts.response.dto'; import { ReadOnlyGuard } from '#account-api/guards/read-only.guard'; import { u8aToHex, u8aWrapBytes } from '@polkadot/util'; import { TransactionType } from '#types/account-webhook'; +import { HandleDto, MsaIdDto } from '#types/dtos/common'; @Controller('v1/handles') @ApiTags('v1/handles') @@ -98,7 +99,7 @@ export class HandlesControllerV1 { * @returns Payload is included for convenience. Encoded payload to be used when signing the transaction. * @throws An error if the change handle payload creation fails. */ - async getChangeHandlePayload(@Param('newHandle') newHandle: string): Promise { + async getChangeHandlePayload(@Param() { newHandle }: HandleDto): Promise { try { const expiration = await this.handlesService.getExpiration(); const payload = { baseHandle: newHandle, expiration }; @@ -124,7 +125,7 @@ export class HandlesControllerV1 { * @returns A promise that resolves to a Handle object, representing the found handle. * @throws An error if the handle cannot be found. */ - async getHandle(@Param('msaId') msaId: string): Promise { + async getHandle(@Param() { msaId }: MsaIdDto): Promise { try { const handle = await this.handlesService.getHandle(msaId); if (!handle) { diff --git a/apps/account-api/src/controllers/v1/keys-v1.controller.ts b/apps/account-api/src/controllers/v1/keys-v1.controller.ts index 854083e9..64a3d3da 100644 --- a/apps/account-api/src/controllers/v1/keys-v1.controller.ts +++ b/apps/account-api/src/controllers/v1/keys-v1.controller.ts @@ -25,6 +25,7 @@ import { PublicKeyAgreementsKeyPayload, } from '#types/dtos/account/graphs.request.dto'; import { TransactionType } from '#types/account-webhook'; +import { MsaIdDto } from '#types/dtos/common'; @Controller('v1/keys') @ApiTags('v1/keys') @@ -74,7 +75,7 @@ export class KeysControllerV1 { * @returns A promise that resolves to an array of public keys associated with the given msaId. * @throws An error if no public keys can be found. */ - async getKeys(@Param('msaId') msaId: string): Promise { + async getKeys(@Param() { msaId }: MsaIdDto): Promise { try { const keys = await this.keysService.getKeysByMsa(msaId); return keys; diff --git a/apps/account-api/src/main.ts b/apps/account-api/src/main.ts index 3aedc7d3..270d38ca 100644 --- a/apps/account-api/src/main.ts +++ b/apps/account-api/src/main.ts @@ -54,7 +54,12 @@ async function bootstrap() { const configService = app.get(ConfigService); app.enableShutdownHooks(); - app.useGlobalPipes(new ValidationPipe()); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + transform: true, + }), + ); app.useGlobalInterceptors(new TimeoutInterceptor(configService.apiTimeoutMs)); app.useBodyParser('json', { limit: configService.apiBodyJsonLimit }); diff --git a/apps/account-api/src/metadata.ts b/apps/account-api/src/metadata.ts index c4661af4..a2d089e0 100644 --- a/apps/account-api/src/metadata.ts +++ b/apps/account-api/src/metadata.ts @@ -13,5 +13,5 @@ export default async () => { ["../../../libs/types/src/dtos/account/handles.request.dto"]: await import("../../../libs/types/src/dtos/account/handles.request.dto"), ["../../../libs/types/src/dtos/account/keys.response.dto"]: await import("../../../libs/types/src/dtos/account/keys.response.dto") }; - return { "@nestjs/swagger": { "models": [[import("../../../libs/types/src/dtos/account/accounts.response.dto"), { "HandleResponseDto": { base_handle: { required: true, type: () => String }, canonical_base: { required: true, type: () => String }, suffix: { required: true, type: () => Number } }, "AccountResponseDto": { msaId: { required: true, type: () => String }, handle: { required: false, type: () => t["../../../libs/types/src/dtos/account/accounts.response.dto"].HandleResponseDto } }, "MsaIdResponseDto": { msaId: { required: true, type: () => String } }, "RetireMsaPayloadResponseDto": { encodedExtrinsic: { required: true, type: () => String }, payloadToSign: { required: true, type: () => String }, accountId: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/accounts.request.dto"), { "RetireMsaRequestDto": { signature: { required: true, type: () => String }, accountId: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/delegation.request.dto"), { "ProviderDelegationRequestDto": { msaId: { required: true, type: () => String }, providerId: { required: false, type: () => String } }, "DelegationRequestDto": {} }], [import("../../../libs/types/src/dtos/account/delegation.response.dto"), { "DelegationResponse": { providerId: { required: true, type: () => String }, schemaPermissions: { required: true }, revokedAt: { required: true, type: () => t["@polkadot/types-codec/primitive/U32"].u32 } }, "SchemaDelegation": { schemaId: { required: true, type: () => Number }, revokedAtBlock: { required: false, type: () => Number } }, "Delegation": { providerId: { required: true, type: () => String }, schemaDelegations: { required: true, type: () => [t["../../../libs/types/src/dtos/account/delegation.response.dto"].SchemaDelegation] }, revokedAtBlock: { required: false, type: () => Number } }, "DelegationResponseV2": { msaId: { required: true, type: () => String }, delegations: { required: true, type: () => [t["../../../libs/types/src/dtos/account/delegation.response.dto"].Delegation] } } }], [import("../../../libs/types/src/dtos/account/graphs.request.dto"), { "AddItemActionDto": { type: { required: true, type: () => String, enum: t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemActionType.ADD_ITEM }, encodedPayload: { required: true, type: () => String } }, "DeleteItemActionDto": { type: { required: true, type: () => String, enum: t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemActionType.DELETE_ITEM }, index: { required: true, type: () => Number } }, "ItemizedSignaturePayloadDto": { schemaId: { required: true, type: () => Number }, targetHash: { required: true, type: () => Number }, expiration: { required: true, type: () => Number }, actions: { required: true, type: () => [Object] } }, "AddNewPublicKeyAgreementRequestDto": { accountId: { required: true, type: () => String }, payload: { required: true, type: () => t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemizedSignaturePayloadDto }, proof: { required: true, type: () => String } }, "AddNewPublicKeyAgreementPayloadRequest": { payload: { required: true, type: () => t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemizedSignaturePayloadDto }, encodedPayload: { required: true, type: () => String } }, "PublicKeyAgreementsKeyPayload": { msaId: { required: true, type: () => String }, newKey: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/handles.request.dto"), { "HandleRequestDto": { accountId: { required: true, type: () => String }, proof: { required: true, type: () => String } }, "ChangeHandlePayloadRequest": { encodedPayload: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/keys.request.dto"), { "KeysRequestDto": { msaOwnerAddress: { required: true, type: () => String }, msaOwnerSignature: { required: true, type: () => String }, newKeyOwnerSignature: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/keys.response.dto"), { "KeysResponse": { msaKeys: { required: true } } }], [import("../../../libs/types/src/dtos/account/revokeDelegation.request.dto"), { "RevokeDelegationPayloadResponseDto": { accountId: { required: true, type: () => String }, providerId: { required: true, type: () => String }, encodedExtrinsic: { required: true, type: () => String }, payloadToSign: { required: true, type: () => String } }, "RevokeDelegationPayloadRequestDto": { signature: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/wallet.login.request.dto"), { "ErrorResponseDto": { message: { required: true, type: () => String } }, "SiwsPayloadDto": { message: { required: true, type: () => String }, signature: { required: true, type: () => String } }, "SignInResponseDto": { siwsPayload: { required: false, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].SiwsPayloadDto }, error: { required: false, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].ErrorResponseDto } }, "EncodedExtrinsicDto": { pallet: { required: true, type: () => String }, extrinsicName: { required: true, type: () => String }, encodedExtrinsic: { required: true, type: () => String } }, "SignUpResponseDto": { extrinsics: { required: false, type: () => [t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].EncodedExtrinsicDto] }, error: { required: false, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].ErrorResponseDto } }, "WalletLoginRequestDto": { signIn: { required: true, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].SignInResponseDto }, signUp: { required: true, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].SignUpResponseDto } } }], [import("../../../libs/types/src/dtos/account/transaction.response.dto"), { "TransactionResponse": { referenceId: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/wallet.login.config.response.dto"), { "WalletLoginConfigResponseDto": { providerId: { required: true, type: () => String }, siwfUrl: { required: true, type: () => String }, frequencyRpcUrl: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/wallet.login.response.dto"), { "WalletLoginResponseDto": { referenceId: { required: true, type: () => String }, msaId: { required: false, type: () => String }, publicKey: { required: false, type: () => String } } }]], "controllers": [[import("./controllers/health.controller"), { "HealthController": { "healthz": {}, "livez": {}, "readyz": {} } }], [import("./controllers/v1/accounts-v1.controller"), { "AccountsControllerV1": { "getSIWFConfig": { type: t["../../../libs/types/src/dtos/account/wallet.login.config.response.dto"].WalletLoginConfigResponseDto }, "getAccountForMsa": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].AccountResponseDto }, "getAccountForAccountId": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].AccountResponseDto }, "postSignInWithFrequency": { type: t["../../../libs/types/src/dtos/account/wallet.login.response.dto"].WalletLoginResponseDto }, "getRetireMsaPayload": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].RetireMsaPayloadResponseDto }, "postRetireMsa": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse } } }], [import("./controllers/v1/delegation-v1.controller"), { "DelegationControllerV1": { "getDelegation": { type: t["../../../libs/types/src/dtos/account/delegation.response.dto"].DelegationResponse }, "getRevokeDelegationPayload": { type: t["../../../libs/types/src/dtos/account/revokeDelegation.request.dto"].RevokeDelegationPayloadResponseDto }, "postRevokeDelegation": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse } } }], [import("./controllers/v1/handles-v1.controller"), { "HandlesControllerV1": { "createHandle": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse }, "changeHandle": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse }, "getChangeHandlePayload": { type: t["../../../libs/types/src/dtos/account/handles.request.dto"].ChangeHandlePayloadRequest }, "getHandle": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].HandleResponseDto } } }], [import("./controllers/v1/keys-v1.controller"), { "KeysControllerV1": { "addKey": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse }, "getKeys": { type: t["../../../libs/types/src/dtos/account/keys.response.dto"].KeysResponse }, "getPublicKeyAgreementsKeyPayload": { type: t["../../../libs/types/src/dtos/account/graphs.request.dto"].AddNewPublicKeyAgreementPayloadRequest }, "AddNewPublicKeyAgreements": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse } } }], [import("./controllers/v2/delegation-v2.controller"), { "DelegationsControllerV2": { "getDelegation": { type: Object }, "getProviderDelegation": { type: t["../../../libs/types/src/dtos/account/delegation.response.dto"].DelegationResponseV2 } } }]] } }; + return { "@nestjs/swagger": { "models": [[import("../../../libs/types/src/dtos/account/accounts.response.dto"), { "HandleResponseDto": { base_handle: { required: true, type: () => String }, canonical_base: { required: true, type: () => String }, suffix: { required: true, type: () => Number } }, "AccountResponseDto": { msaId: { required: true, type: () => String }, handle: { required: false, type: () => t["../../../libs/types/src/dtos/account/accounts.response.dto"].HandleResponseDto } }, "MsaIdResponseDto": { msaId: { required: true, type: () => String } }, "RetireMsaPayloadResponseDto": { encodedExtrinsic: { required: true, type: () => String }, payloadToSign: { required: true, type: () => String }, accountId: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/accounts.request.dto"), { "RetireMsaRequestDto": { signature: { required: true, type: () => String }, accountId: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/delegation.request.dto"), { "ProviderDelegationRequestDto": { msaId: { required: true, type: () => String }, providerId: { required: false, type: () => String } }, "DelegationRequestDto": {} }], [import("../../../libs/types/src/dtos/account/delegation.response.dto"), { "DelegationResponse": { providerId: { required: true, type: () => String }, schemaPermissions: { required: true }, revokedAt: { required: true, type: () => t["@polkadot/types-codec/primitive/U32"].u32 } }, "SchemaDelegation": { schemaId: { required: true, type: () => Number }, revokedAtBlock: { required: false, type: () => Number } }, "Delegation": { providerId: { required: true, type: () => String }, schemaDelegations: { required: true, type: () => [t["../../../libs/types/src/dtos/account/delegation.response.dto"].SchemaDelegation] }, revokedAtBlock: { required: false, type: () => Number } }, "DelegationResponseV2": { msaId: { required: true, type: () => String }, delegations: { required: true, type: () => [t["../../../libs/types/src/dtos/account/delegation.response.dto"].Delegation] } } }], [import("../../../libs/types/src/dtos/account/graphs.request.dto"), { "ItemActionDto": { type: { required: true, enum: t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemActionType }, encodedPayload: { required: false, type: () => String }, index: { required: false, type: () => Number } }, "ItemizedSignaturePayloadDto": { schemaId: { required: true, type: () => Number }, targetHash: { required: true, type: () => Number }, expiration: { required: true, type: () => Number }, actions: { required: true, type: () => [t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemActionDto] } }, "AddNewPublicKeyAgreementRequestDto": { accountId: { required: true, type: () => String }, payload: { required: true, type: () => t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemizedSignaturePayloadDto }, proof: { required: true, type: () => String } }, "AddNewPublicKeyAgreementPayloadRequest": { payload: { required: true, type: () => t["../../../libs/types/src/dtos/account/graphs.request.dto"].ItemizedSignaturePayloadDto }, encodedPayload: { required: true, type: () => String } }, "PublicKeyAgreementsKeyPayload": { msaId: { required: true, type: () => String }, newKey: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/handles.request.dto"), { "HandleRequestDto": { accountId: { required: true, type: () => String }, proof: { required: true, type: () => String } }, "ChangeHandlePayloadRequest": { encodedPayload: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/keys.request.dto"), { "KeysRequestDto": { msaOwnerAddress: { required: true, type: () => String }, msaOwnerSignature: { required: true, type: () => String }, newKeyOwnerSignature: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/keys.response.dto"), { "KeysResponse": { msaKeys: { required: true } } }], [import("../../../libs/types/src/dtos/account/revokeDelegation.request.dto"), { "RevokeDelegationPayloadResponseDto": { accountId: { required: true, type: () => String }, providerId: { required: true, type: () => String }, encodedExtrinsic: { required: true, type: () => String }, payloadToSign: { required: true, type: () => String } }, "RevokeDelegationPayloadRequestDto": { signature: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/wallet.login.request.dto"), { "ErrorResponseDto": { message: { required: true, type: () => String } }, "SiwsPayloadDto": { message: { required: true, type: () => String }, signature: { required: true, type: () => String } }, "SignInResponseDto": { siwsPayload: { required: false, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].SiwsPayloadDto }, error: { required: false, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].ErrorResponseDto } }, "EncodedExtrinsicDto": { pallet: { required: true, type: () => String, minLength: 1 }, extrinsicName: { required: true, type: () => String, minLength: 1 }, encodedExtrinsic: { required: true, type: () => String } }, "SignUpResponseDto": { extrinsics: { required: false, type: () => [t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].EncodedExtrinsicDto] }, error: { required: false, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].ErrorResponseDto } }, "WalletLoginRequestDto": { signIn: { required: true, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].SignInResponseDto }, signUp: { required: true, type: () => t["../../../libs/types/src/dtos/account/wallet.login.request.dto"].SignUpResponseDto } } }], [import("../../../libs/types/src/dtos/account/transaction.response.dto"), { "TransactionResponse": { referenceId: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/wallet.login.config.response.dto"), { "WalletLoginConfigResponseDto": { providerId: { required: true, type: () => String }, siwfUrl: { required: true, type: () => String }, frequencyRpcUrl: { required: true, type: () => String } } }], [import("../../../libs/types/src/dtos/account/wallet.login.response.dto"), { "WalletLoginResponseDto": { referenceId: { required: true, type: () => String }, msaId: { required: false, type: () => String }, publicKey: { required: false, type: () => String } } }], [import("../../../libs/types/src/dtos/common/params.dto"), { "MsaIdDto": { msaId: { required: true, type: () => String } }, "ProviderMsaIdDto": { providerId: { required: true, type: () => String } }, "UrlDto": { url: { required: true, type: () => String } }, "HandleDto": { newHandle: { required: true, type: () => String, minLength: 3 } }, "AccountIdDto": { accountId: { required: true, type: () => String } } }]], "controllers": [[import("./controllers/health.controller"), { "HealthController": { "healthz": {}, "livez": {}, "readyz": {} } }], [import("./controllers/v1/accounts-v1.controller"), { "AccountsControllerV1": { "getSIWFConfig": { type: t["../../../libs/types/src/dtos/account/wallet.login.config.response.dto"].WalletLoginConfigResponseDto }, "getAccountForMsa": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].AccountResponseDto }, "getAccountForAccountId": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].AccountResponseDto }, "postSignInWithFrequency": { type: t["../../../libs/types/src/dtos/account/wallet.login.response.dto"].WalletLoginResponseDto }, "getRetireMsaPayload": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].RetireMsaPayloadResponseDto }, "postRetireMsa": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse } } }], [import("./controllers/v1/delegation-v1.controller"), { "DelegationControllerV1": { "getDelegation": { type: t["../../../libs/types/src/dtos/account/delegation.response.dto"].DelegationResponse }, "getRevokeDelegationPayload": { type: t["../../../libs/types/src/dtos/account/revokeDelegation.request.dto"].RevokeDelegationPayloadResponseDto }, "postRevokeDelegation": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse } } }], [import("./controllers/v1/handles-v1.controller"), { "HandlesControllerV1": { "createHandle": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse }, "changeHandle": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse }, "getChangeHandlePayload": { type: t["../../../libs/types/src/dtos/account/handles.request.dto"].ChangeHandlePayloadRequest }, "getHandle": { type: t["../../../libs/types/src/dtos/account/accounts.response.dto"].HandleResponseDto } } }], [import("./controllers/v1/keys-v1.controller"), { "KeysControllerV1": { "addKey": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse }, "getKeys": { type: t["../../../libs/types/src/dtos/account/keys.response.dto"].KeysResponse }, "getPublicKeyAgreementsKeyPayload": { type: t["../../../libs/types/src/dtos/account/graphs.request.dto"].AddNewPublicKeyAgreementPayloadRequest }, "AddNewPublicKeyAgreements": { type: t["../../../libs/types/src/dtos/account/transaction.response.dto"].TransactionResponse } } }], [import("./controllers/v2/delegation-v2.controller"), { "DelegationsControllerV2": { "getDelegation": { type: Object }, "getProviderDelegation": { type: t["../../../libs/types/src/dtos/account/delegation.response.dto"].DelegationResponseV2 } } }]] } }; }; \ No newline at end of file diff --git a/apps/account-api/test/e2e-setup.mock.spec.ts b/apps/account-api/test/e2e-setup.mock.spec.ts index a0899a58..884a950f 100644 --- a/apps/account-api/test/e2e-setup.mock.spec.ts +++ b/apps/account-api/test/e2e-setup.mock.spec.ts @@ -16,6 +16,7 @@ import { KeyringPair } from '@polkadot/keyring/types'; import { AccountId } from '@polkadot/types/interfaces'; import { cryptoWaitReady, decodeAddress } from '@polkadot/util-crypto'; import log from 'loglevel'; +import { u8aToHex } from '@polkadot/util'; export const FREQUENCY_URL = process.env.FREQUENCY_URL || 'ws://0.0.0.0:9944'; export const BASE_SEED_PHRASE = process.env.SEED_PHRASE || '//Alice'; @@ -74,6 +75,7 @@ export async function generateSignedAddKeyPayload(user: ChainUser, newKeys: Keyr const addKeyData = ExtrinsicHelper.api.registry.createType('PalletMsaAddKeyData', payload); const ownerProof = signPayloadSr25519(user.keypair, addKeyData); const newKeyProof = signPayloadSr25519(newKeys, addKeyData); + payload.newPublicKey = u8aToHex(payload.newPublicKey); return { payload, ownerProof, newKeyProof }; } diff --git a/apps/account-api/test/handles.controller.e2e-spec.ts b/apps/account-api/test/handles.controller.e2e-spec.ts index acbffb51..ac10ae62 100644 --- a/apps/account-api/test/handles.controller.e2e-spec.ts +++ b/apps/account-api/test/handles.controller.e2e-spec.ts @@ -109,7 +109,7 @@ describe('Handles Controller', () => { await request(HTTP_SERVER) .post('/v1/handles') - .send({ accountId, payload, proof }) + .send({ accountId, payload, proof: proof.Sr25519 }) .expect(200) .expect((req) => req.text === 'Handle created successfully'); }); @@ -121,7 +121,7 @@ describe('Handles Controller', () => { const accountId = user.keypair.address; await request(HTTP_SERVER) .post('/v1/handles/change') - .send({ accountId, payload, proof }) + .send({ accountId, payload, proof: proof.Sr25519 }) .expect(200) .expect((res) => res.text === 'Handle created successfully'); }); diff --git a/apps/account-api/test/keys.controller.e2e-spec.ts b/apps/account-api/test/keys.controller.e2e-spec.ts index 8f460e4b..376a837b 100644 --- a/apps/account-api/test/keys.controller.e2e-spec.ts +++ b/apps/account-api/test/keys.controller.e2e-spec.ts @@ -105,11 +105,7 @@ describe('Keys Controller', () => { }, }; - await request(HTTP_SERVER) - .post('/v1/keys/add') - .send(keysRequest) - .expect(200) - .expect((req) => req.text === 'Successfully added key.'); + await request(HTTP_SERVER).post('/v1/keys/add').send(keysRequest).expect(200); }); }); diff --git a/docs/account/index.html b/docs/account/index.html index ee99b6ed..ff92fc1a 100644 --- a/docs/account/index.html +++ b/docs/account/index.html @@ -1,2073 +1,351 @@ - + - - - Account Service - - - - - - - - -
-
- -
-
- - - + Account Service + + + + + + + + + +
- - - - - -
-
-
-
-
-
-

- Account Service - (1.0) -

-

- Download OpenAPI specification:Download -

-
-
-
- - -
-
-
-
-
-

Account Service API

-
-
-
-
-
-
-
-

- v1/accounts -

-
-
-
-
-
-
-

- Get the Sign In With Frequency configuration -

-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "providerId": - "string", -
    -
  • -
  • -
    - "siwfUrl": - "string", -
    -
  • -
  • -
    - "frequencyRpcUrl": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Request to Sign In With Frequency -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - -
- - -
-
- object -
-
-
-

The wallet login request information

-
-
-
-
- - -
-
- object (SignUpResponseDto) -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "signIn": - { -
      -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "signUp": - { -
      -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "referenceId": - "string", -
    -
  • -
  • -
    - "msaId": - "string", -
    -
  • -
  • -
    - "publicKey": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Fetch an account given an MSA Id -

-
-
- path - Parameters -
- - - - - - - -
- msaId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "msaId": - "string", -
    -
  • -
  • -
    - "handle": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Fetch an account given an Account Id -

-
-
- path - Parameters -
- - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "msaId": - "string", -
    -
  • -
  • -
    - "handle": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get a retireMsa unsigned, encoded extrinsic payload. -

-
-
- path - Parameters -
- - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "encodedExtrinsic": - "string", -
    -
  • -
  • -
    - "payloadToSign": - "string", -
    -
  • -
  • -
    - "accountId": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Request to retire an MSA ID. -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - - - - - - - - - -
- encodedExtrinsic -
required
-
-
-
- string -
-
-
-
- payloadToSign -
required
-
-
-
- string -
-
-
-
- accountId -
required
-
-
-
- string -
-
-
-
- signature -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "encodedExtrinsic": - "string", -
    -
  • -
  • -
    - "payloadToSign": - "string", -
    -
  • -
  • -
    - "accountId": - "string", -
    -
  • -
  • -
    - "signature": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "referenceId": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- delegations -

-
-
-
-
-
-
-

- Get all delegation information associated with an MSA Id -

-
-
- path - Parameters -
- - - - - - - -
- msaId -
required
-
-
-
- string - <int64> - -
-
-
-

Message Source Account Identifier

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "msaId": - "string", -
    -
  • -
  • -
    - "delegations": - [ -
      -
    • - -
    • -
    - ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get an MSA's delegation information for a specific provider -

-
-
- path - Parameters -
- - - - - - - - - - - -
- msaId -
required
-
-
-
- string - <int64> - -
-
-
-

Message Source Account Identifier

-
-
-
-
- providerId - -
-
- string - <int64> - -
-
-
-

Provider ID

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "msaId": - "string", -
    -
  • -
  • -
    - "delegations": - [ -
      -
    • - -
    • -
    - ] -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- delegation -

-
-
-
-
-
-
-

- Get the delegation information associated with an MSA Id -

-
-
- path - Parameters -
- - - - - - - -
- msaId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "providerId": - "string", -
    -
  • -
  • -
    - "schemaPermissions": - { }, -
    -
  • -
  • -
    - "revokedAt": - { } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get a properly encoded RevokeDelegationPayload that can be signed -

-
-
- path - Parameters -
- - - - - - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
- providerId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "accountId": - "string", -
    -
  • -
  • -
    - "providerId": - "string", -
    -
  • -
  • -
    - "encodedExtrinsic": - { }, -
    -
  • -
  • -
    - "payloadToSign": - { } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Request to revoke a delegation -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
- providerId -
required
-
-
-
- string -
-
-
-
- encodedExtrinsic -
required
-
-
-
- object -
-
-
-
- payloadToSign -
required
-
-
-
- object -
-
-
-
- signature -
required
-
-
-
- object -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "accountId": - "string", -
    -
  • -
  • -
    - "providerId": - "string", -
    -
  • -
  • -
    - "encodedExtrinsic": - { }, -
    -
  • -
  • -
    - "payloadToSign": - { }, -
    -
  • -
  • -
    - "signature": - { } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-

Response samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "referenceId": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- v1/handles -

-
-
-
-
-
-
-

- Request to create a new handle for an account -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
- -
required
-
-
-
- object (HandlePayloadDto) -
-
-
-
- proof -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "accountId": - "string", -
    -
  • -
  • -
    - "payload": - { -
      -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "proof": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Request to change a handle -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
- -
required
-
-
-
- object (HandlePayloadDto) -
-
-
-
- proof -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "accountId": - "string", -
    -
  • -
  • -
    - "payload": - { -
      -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "proof": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Get a properly encoded ClaimHandlePayload that can be signed. -

-
-
- path - Parameters -
- - - - - - - -
- newHandle -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-

- Fetch a handle given an MSA Id -

-
-
- path - Parameters -
- - - - - - - -
- msaId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-

- v1/keys -

-
-
-
-
-
-
-

- Add new control keys for an MSA Id -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - - - - - - - - - -
- msaOwnerAddress -
required
-
-
-
- string -
-
-
-
- msaOwnerSignature -
required
-
-
-
- string -
-
-
-
- newKeyOwnerSignature -
required
-
-
-
- string -
-
-
-
- -
required
-
-
-
- object - (KeysRequestPayloadDto) - -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "msaOwnerAddress": - "string", -
    -
  • -
  • -
    - "msaOwnerSignature": - "string", -
    -
  • -
  • -
    - "newKeyOwnerSignature": - "string", -
    -
  • -
  • -
    - "payload": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - } -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Fetch public keys given an MSA Id -

-
-
- path - Parameters -
- - - - - - - -
- msaId -
required
-
-
-
- string -
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-

- Get a properly encoded StatefulStorageItemizedSignaturePayloadV2 that can be signed. -

-
-
- query - Parameters -
- - - - - - - - - - - -
- msaId -
required
-
-
-
- string -
-
-
-

MSA ID of account to generate a payload for

-
-
-
-
- newKey -
required
-
-
-
- string -
-
-
-

Public key should be a 32 bytes value in hex format!

-
-
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-

- Request to add a new public Key -

-
- Request Body schema: application/json -
required
-
-
- - - - - - - - - - - - - - - -
- accountId -
required
-
-
-
- string -
-
-
-
- -
required
-
-
-
- object - (ItemizedSignaturePayloadDto) - -
-
-
-
- proof -
required
-
-
-
- string -
-
-
-
-
-

Responses

-
- -
-
-
-
-
- - -
-
-

Request samples

-
-
    - -
-
-
-
- Content type -
application/json
-
-
-
-
- -
-
-
- { -
    -
  • -
    - "accountId": - "string", -
    -
  • -
  • -
    - "payload": - { -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    - }, -
    -
  • -
  • -
    - "proof": - "string" -
    -
  • -
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- health -

-
-
-
-
-
-
-

- Check the health status of the service -

-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-

- Check the live status of the service -

-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-

- Check the ready status of the service -

-
-

Responses

-
- -
-
-
-
-
- - -
-
-
-
-
-
-
-
- - + + + + diff --git a/libs/types/src/dtos/account/accounts.request.dto.ts b/libs/types/src/dtos/account/accounts.request.dto.ts index c2ba850a..d61be659 100644 --- a/libs/types/src/dtos/account/accounts.request.dto.ts +++ b/libs/types/src/dtos/account/accounts.request.dto.ts @@ -1,18 +1,27 @@ // eslint-disable-next-line max-classes-per-file import { ApiProperty } from '@nestjs/swagger'; -import { IsHexadecimal, IsNotEmpty } from 'class-validator'; import { HexString } from '@polkadot/util/types'; import { RetireMsaPayloadResponseDto } from './accounts.response.dto'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; +import { IsSignature } from '#utils/decorators/is-signature.decorator'; import { TransactionType } from '#types/account-webhook'; export class RetireMsaRequestDto extends RetireMsaPayloadResponseDto { - @ApiProperty({ type: String }) - @IsNotEmpty() - @IsHexadecimal() + @ApiProperty({ + description: 'signature of the owner', + type: String, + example: + '0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85', + }) + @IsSignature() signature: HexString; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'AccountId in hex or SS58 format', + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() accountId: string; } diff --git a/libs/types/src/dtos/account/accounts.response.dto.ts b/libs/types/src/dtos/account/accounts.response.dto.ts index b624b023..eba75282 100644 --- a/libs/types/src/dtos/account/accounts.response.dto.ts +++ b/libs/types/src/dtos/account/accounts.response.dto.ts @@ -1,7 +1,9 @@ /* eslint-disable max-classes-per-file */ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsHexadecimal, IsNotEmpty, IsOptional } from 'class-validator'; +import { IsNotEmpty, IsOptional } from 'class-validator'; import { HexString } from '@polkadot/util/types'; +import { IsHexValue } from '#utils/decorators'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; export class HandleResponseDto { @ApiProperty() @@ -31,17 +33,27 @@ export class MsaIdResponseDto { } export class RetireMsaPayloadResponseDto { - @ApiProperty({ type: String }) - @IsNotEmpty() - @IsHexadecimal() + @ApiProperty({ + type: String, + description: 'Hex-encoded representation of the "RetireMsa" extrinsic', + example: '0x1234', + }) + @IsHexValue({ minLength: 2 }) encodedExtrinsic: HexString; - @ApiProperty({ type: String }) - @IsNotEmpty() - @IsHexadecimal() + @ApiProperty({ + type: String, + description: 'payload to be signed', + example: '0x1234', + }) + @IsHexValue({ minLength: 2 }) payloadToSign: HexString; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'AccountId in hex or SS58 format', + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() accountId: string; } diff --git a/libs/types/src/dtos/account/delegation.request.dto.ts b/libs/types/src/dtos/account/delegation.request.dto.ts index f5b6e632..5e6ed235 100644 --- a/libs/types/src/dtos/account/delegation.request.dto.ts +++ b/libs/types/src/dtos/account/delegation.request.dto.ts @@ -1,14 +1,20 @@ /* eslint-disable max-classes-per-file */ -import { ApiProperty, OmitType } from '@nestjs/swagger'; -import { IsNumberString } from 'class-validator'; +import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger'; +import { IsOptional } from 'class-validator'; +import { IsMsaId } from '#utils/decorators/is-msa-id.decorator'; export class ProviderDelegationRequestDto { - @IsNumberString({ no_symbols: true }) - @ApiProperty({ description: 'Message Source Account Identifier', type: String, format: 'int64' }) + @ApiProperty({ description: 'MSA Id of the user requesting the delegation', type: String, example: '3' }) + @IsMsaId() msaId: string; - @IsNumberString({ no_symbols: true }) - @ApiProperty({ description: 'Provider ID', type: String, format: 'int64' }) + @ApiPropertyOptional({ + description: 'MSA Id of the provider to whom the requesting user wishes to delegate', + type: String, + example: '1', + }) + @IsOptional() + @IsMsaId() providerId?: string; } diff --git a/libs/types/src/dtos/account/graphs.request.dto.ts b/libs/types/src/dtos/account/graphs.request.dto.ts index a421f19e..b90b9d38 100644 --- a/libs/types/src/dtos/account/graphs.request.dto.ts +++ b/libs/types/src/dtos/account/graphs.request.dto.ts @@ -1,8 +1,14 @@ // eslint-disable-next-line max-classes-per-file -import { IsNotEmpty, IsNumberString } from 'class-validator'; +import { ArrayNotEmpty, IsArray, IsEnum, IsNotEmpty, ValidateIf, ValidateNested } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { HexString } from '@polkadot/util/types'; -import { IsHexPublicKey } from '#account-lib/utils/custom.decorator'; +import { IsHexValue } from '#utils/decorators'; +import { IsIntValue } from '#utils/decorators/is-int-value.decorator'; +import { IsSchemaId } from '#utils/decorators/is-schema-id.decorator'; +import { Type } from 'class-transformer'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; +import { IsMsaId } from '#utils/decorators/is-msa-id.decorator'; +import { IsSignature } from '#utils/decorators/is-signature.decorator'; import { TransactionType } from '#types/account-webhook'; // eslint-disable-next-line no-shadow @@ -11,62 +17,110 @@ export enum ItemActionType { DELETE_ITEM = 'DELETE_ITEM', } -export class AddItemActionDto { - type: ItemActionType.ADD_ITEM; - - @ApiProperty({ type: String }) - @IsNotEmpty() - encodedPayload: HexString; +export class ItemActionDto { + @ApiProperty({ + type: ItemActionType, + description: 'Action Item type', + example: 'ADD_ITEM', + }) + @IsEnum(ItemActionType) + type: ItemActionType; + + @ApiProperty({ + type: String, + description: 'encodedPayload to be added', + example: '0x1234', + }) + @ValidateIf((o) => o.type === ItemActionType.ADD_ITEM) + @IsHexValue({ minLength: 2, maxLength: 2048 }) + encodedPayload?: string; + + @ApiProperty({ + type: 'number', + description: 'index of the item to be deleted', + example: '0', + }) + @ValidateIf((o) => o.type === ItemActionType.DELETE_ITEM) + @IsIntValue({ minValue: 0 }) + index?: number; } -export class DeleteItemActionDto { - type: ItemActionType.DELETE_ITEM; - - @ApiProperty() - @IsNotEmpty() - index: number; -} - -export type ItemActionDto = AddItemActionDto | DeleteItemActionDto; - export class ItemizedSignaturePayloadDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: 'number', + description: 'schemaId related to the payload', + example: '1', + }) + @IsSchemaId() schemaId: number; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: 'number', + description: 'targetHash related to the stateful storage', + example: '1234', + }) + @IsIntValue({ minValue: 0, maxValue: 4_294_967_296 }) targetHash: number; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ type: 'number', description: 'expiration block number for this payload', example: '1' }) + @IsIntValue({ minValue: 0, maxValue: 4_294_967_296 }) expiration: number; - @IsNotEmpty() + @ApiProperty({ + example: [ + { + type: 'ADD_ITEM', + encodedPayload: '0x1122', + }, + ], + }) + @ValidateNested({ each: true }) + @IsArray() + @ArrayNotEmpty() + @Type(() => ItemActionDto) actions: ItemActionDto[]; } export class AddNewPublicKeyAgreementRequestDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'AccountId in hex or SS58 format', + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() accountId: string; - @ApiProperty() + @ApiProperty({ + type: ItemizedSignaturePayloadDto, + }) + @ValidateNested() @IsNotEmpty() + @Type(() => ItemizedSignaturePayloadDto) payload: ItemizedSignaturePayloadDto; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + description: 'proof is the signature for the payload', + type: String, + example: + '0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85', + }) + @IsSignature() proof: HexString; } export class AddNewPublicKeyAgreementPayloadRequest { @ApiProperty() + @ValidateNested() @IsNotEmpty() + @Type(() => ItemizedSignaturePayloadDto) payload: ItemizedSignaturePayloadDto; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'encodedPayload to be added', + example: '0x1234', + }) + @IsHexValue({ minLength: 2, maxLength: 2048 }) encodedPayload: HexString; } @@ -75,11 +129,15 @@ export type PublicKeyAgreementRequestDto = AddNewPublicKeyAgreementRequestDto & }; export class PublicKeyAgreementsKeyPayload { - @IsNumberString() - @ApiProperty({ type: String, description: 'MSA ID of account to generate a payload for' }) + @ApiProperty({ description: 'MSA Id representing the target of this request', type: String, example: '3' }) + @IsMsaId() msaId: string; - @IsHexPublicKey({ message: 'Public key should be a 32 bytes value in hex format!' }) - @ApiProperty({ type: String, description: 'Public key should be a 32 bytes value in hex format!' }) + @ApiProperty({ + type: String, + description: 'New public key to be added to the account (32-byte value in hex format)', + example: '0x0ed2f8c714efcac51ca2325cfe95637e5e0b898ae397aa365978b7348a717d0b', + }) + @IsHexValue({ minLength: 64, maxLength: 64 }) newKey: HexString; } diff --git a/libs/types/src/dtos/account/handles.request.dto.ts b/libs/types/src/dtos/account/handles.request.dto.ts index 45147f61..23257ce6 100644 --- a/libs/types/src/dtos/account/handles.request.dto.ts +++ b/libs/types/src/dtos/account/handles.request.dto.ts @@ -1,40 +1,67 @@ // eslint-disable-next-line max-classes-per-file -import { IsNotEmpty } from 'class-validator'; +import { IsNotEmpty, IsString, MinLength, ValidateNested } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { HexString } from '@polkadot/util/types'; +import { IsIntValue } from '#utils/decorators/is-int-value.decorator'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; +import { Type } from 'class-transformer'; +import { IsHexValue } from '#utils/decorators'; +import { IsSignature } from '#utils/decorators/is-signature.decorator'; import { TransactionType } from '#types/account-webhook'; class HandlePayloadDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: 'string', + description: 'base handle in the request', + example: 'handle', + }) + @MinLength(3) + @IsString() baseHandle: string; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ type: 'number', description: 'expiration block number for this payload', example: '1' }) + @IsIntValue({ minValue: 0, maxValue: 4_294_967_296 }) expiration: number; } export class HandleRequestDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'AccountId in hex or SS58 format', + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() accountId: string; @ApiProperty() + @ValidateNested() @IsNotEmpty() + @Type(() => HandlePayloadDto) payload: HandlePayloadDto; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + description: 'proof is the signature for the payload', + type: String, + example: + '0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85', + }) + @IsSignature() proof: HexString; } export class ChangeHandlePayloadRequest { @ApiProperty() + @ValidateNested() @IsNotEmpty() - payload: HandleRequestDto['payload']; + @Type(() => HandlePayloadDto) + payload: HandlePayloadDto; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + description: 'encodedPayload is scale encoded of payload in hex format', + type: String, + example: '0x012345', + }) + @IsHexValue({ minLength: 2 }) encodedPayload: HexString; } diff --git a/libs/types/src/dtos/account/keys.request.dto.ts b/libs/types/src/dtos/account/keys.request.dto.ts index 74be57ef..25f7178f 100644 --- a/libs/types/src/dtos/account/keys.request.dto.ts +++ b/libs/types/src/dtos/account/keys.request.dto.ts @@ -1,38 +1,64 @@ // eslint-disable-next-line max-classes-per-file import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty } from 'class-validator'; +import { IsNotEmpty, ValidateNested } from 'class-validator'; import { HexString } from '@polkadot/util/types'; +import { IsMsaId } from '#utils/decorators/is-msa-id.decorator'; +import { IsHexValue } from '#utils/decorators'; +import { IsIntValue } from '#utils/decorators/is-int-value.decorator'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; +import { Type } from 'class-transformer'; +import { IsSignature } from '#utils/decorators/is-signature.decorator'; import { TransactionType } from '#types/account-webhook'; class KeysRequestPayloadDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ description: 'MSA Id of the user requesting the new key', type: String, example: '3' }) + @IsMsaId() msaId: string; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ type: 'number', description: 'expiration block number for this payload', example: '1' }) + @IsIntValue({ minValue: 0, maxValue: 4_294_967_296 }) expiration: number; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'newPublicKey in hex format', + example: '0x0ed2f8c714efcac51ca2325cfe95637e5e0b898ae397aa365978b7348a717d0b', + }) + @IsHexValue({ minLength: 64, maxLength: 64 }) newPublicKey: string; } export class KeysRequestDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + description: 'msaOwnerAddress representing the target of this request', + type: String, + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() msaOwnerAddress: string; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + description: 'msaOwnerSignature is the signature by msa owner', + type: String, + example: + '0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85', + }) + @IsSignature() msaOwnerSignature: HexString; - @ApiProperty({ type: String }) - @IsNotEmpty() + @ApiProperty({ + description: 'newKeyOwnerSignature is the signature with new key', + type: String, + example: + '0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85', + }) + @IsSignature() newKeyOwnerSignature: HexString; @ApiProperty() + @ValidateNested() @IsNotEmpty() + @Type(() => KeysRequestPayloadDto) payload: KeysRequestPayloadDto; } diff --git a/libs/types/src/dtos/account/revokeDelegation.request.dto.ts b/libs/types/src/dtos/account/revokeDelegation.request.dto.ts index 6d814e66..0d8e508c 100644 --- a/libs/types/src/dtos/account/revokeDelegation.request.dto.ts +++ b/libs/types/src/dtos/account/revokeDelegation.request.dto.ts @@ -1,30 +1,54 @@ /* eslint-disable max-classes-per-file */ -import { IsNotEmpty } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; import { HexString } from '@polkadot/util/types'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; +import { IsMsaId } from '#utils/decorators/is-msa-id.decorator'; +import { IsHexValue } from '#utils/decorators'; +import { IsSignature } from '#utils/decorators/is-signature.decorator'; import { TransactionType } from '#types/account-webhook'; export class RevokeDelegationPayloadResponseDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'AccountId in hex or SS58 format', + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() accountId: string; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + description: 'MSA Id of the provider to whom the requesting user wishes to delegate', + type: String, + example: '3', + }) + @IsMsaId() providerId: string; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'Hex-encoded representation of the "revokeDelegation" extrinsic', + example: '0x1234', + }) + @IsHexValue({ minLength: 2 }) encodedExtrinsic: HexString; - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + type: String, + description: 'payload to be signed', + example: '0x1234', + }) + @IsHexValue({ minLength: 2 }) payloadToSign: HexString; } export class RevokeDelegationPayloadRequestDto extends RevokeDelegationPayloadResponseDto { - @ApiProperty() - @IsNotEmpty() + @ApiProperty({ + description: 'signature of the owner', + type: String, + example: + '0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85', + }) + @IsSignature() signature: HexString; } diff --git a/libs/types/src/dtos/account/wallet.login.request.dto.ts b/libs/types/src/dtos/account/wallet.login.request.dto.ts index 59cfc45e..50321c8b 100644 --- a/libs/types/src/dtos/account/wallet.login.request.dto.ts +++ b/libs/types/src/dtos/account/wallet.login.request.dto.ts @@ -9,49 +9,82 @@ import { type WalletProxyResponse, } from '@projectlibertylabs/siwf'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsOptional } from 'class-validator'; +import { IsArray, IsNotEmpty, IsOptional, IsString, MinLength, ValidateNested } from 'class-validator'; import { HexString } from '@polkadot/util/types'; +import { IsHexValue } from '#utils/decorators'; +import { Type } from 'class-transformer'; +import { IsSignature } from '#utils/decorators/is-signature.decorator'; import { TransactionType } from '#types/account-webhook'; export class ErrorResponseDto implements ErrorResponse { - @ApiProperty() + @ApiProperty({ + type: String, + description: 'Error message', + example: 'Some error', + }) + @IsNotEmpty() message: string; } export class SiwsPayloadDto implements SiwsPayload { @ApiProperty() + @IsNotEmpty() message: string; - @ApiProperty({ type: String }) + @ApiProperty({ + type: String, + description: 'Signature of the payload', + example: + '0x64f8dd8846ba72cbb1954761ec4b2e44b886abb4b4ef7455b869355f17b4ce4a601ad26eabc57a682244a97bc9a2001b59469ae76fea105b724e988967d4928d', + }) + @IsSignature() signature: string | HexString; } export class SignInResponseDto implements SignInResponse { @ApiPropertyOptional({ type: SiwsPayloadDto }) @IsOptional() + @ValidateNested() + @Type(() => SiwsPayloadDto) siwsPayload?: SiwsPayloadDto | undefined; @ApiPropertyOptional({ type: ErrorResponseDto }) @IsOptional() + @ValidateNested() + @Type(() => ErrorResponseDto) error?: ErrorResponseDto | undefined; } export class EncodedExtrinsicDto implements EncodedExtrinsic { @ApiProperty() + @MinLength(1) + @IsString() pallet: string; @ApiProperty() + @MinLength(1) + @IsString() extrinsicName: string; - @ApiProperty({ type: String }) + @ApiProperty({ + type: String, + description: 'Hex-encoded representation of the extrinsic', + example: '0x00112233', + }) + @IsHexValue({ minLength: 2 }) encodedExtrinsic: string | HexString; } export class SignUpResponseDto implements SignUpResponse { @IsOptional() @ApiPropertyOptional({ type: [EncodedExtrinsicDto] }) + @ValidateNested({ each: true }) + @IsArray() + @Type(() => EncodedExtrinsicDto) extrinsics?: EncodedExtrinsicDto[] | undefined; @IsOptional() @ApiPropertyOptional({ type: ErrorResponseDto }) + @ValidateNested() + @Type(() => ErrorResponseDto) error?: ErrorResponseDto | undefined; } @@ -61,7 +94,8 @@ export class WalletLoginRequestDto implements WalletProxyResponse { example: { siwsPayload: { message: '0x1234567890abcdef', - signature: '0x1234567890abcdef', + signature: + '0x64f8dd8846ba72cbb1954761ec4b2e44b886abb4b4ef7455b869355f17b4ce4a601ad26eabc57a682244a97bc9a2001b59469ae76fea105b724e988967d4928d', }, err: { message: 'Error message', @@ -69,10 +103,14 @@ export class WalletLoginRequestDto implements WalletProxyResponse { }, }) @IsOptional() + @ValidateNested() + @Type(() => SignInResponseDto) signIn: SignInResponseDto; @ApiPropertyOptional() @IsOptional() + @ValidateNested() + @Type(() => SignUpResponseDto) signUp: SignUpResponseDto; } diff --git a/libs/types/src/dtos/common/params.dto.ts b/libs/types/src/dtos/common/params.dto.ts index 7693dfc4..4fc21ee7 100644 --- a/libs/types/src/dtos/common/params.dto.ts +++ b/libs/types/src/dtos/common/params.dto.ts @@ -1,14 +1,21 @@ // eslint-disable-next-line max-classes-per-file import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsUrl } from 'class-validator'; +import { IsNotEmpty, IsString, IsUrl, MinLength } from 'class-validator'; import { IsMsaId } from '#utils/decorators/is-msa-id.decorator'; +import { IsAccountIdOrAddress } from '#utils/decorators/is-account-id-address.decorator'; export class MsaIdDto { @ApiProperty({ name: 'msaId', type: String, description: 'Msa Id of requested account', example: '2' }) - @IsMsaId({ message: 'Msa Id should be a valid positive number' }) + @IsMsaId() msaId: string; } +export class ProviderMsaIdDto { + @ApiProperty({ name: 'providerId', type: String, description: 'Msa Id of provider', example: '1' }) + @IsMsaId() + providerId: string; +} + export class UrlDto { @ApiProperty({ name: 'url', @@ -20,3 +27,24 @@ export class UrlDto { @IsUrl({ require_tld: false, require_protocol: true, require_valid_protocol: true }) url: string; } + +export class HandleDto { + @ApiProperty({ + description: 'newHandle in the request', + type: String, + example: 'handle', + }) + @MinLength(3) + @IsString() + newHandle: string; +} + +export class AccountIdDto { + @ApiProperty({ + type: String, + description: 'AccountId in hex or SS58 format', + example: '1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N', + }) + @IsAccountIdOrAddress() + accountId: string; +} diff --git a/libs/types/src/dtos/graph/connection.dto.ts b/libs/types/src/dtos/graph/connection.dto.ts index b871c2a9..43ed3e93 100644 --- a/libs/types/src/dtos/graph/connection.dto.ts +++ b/libs/types/src/dtos/graph/connection.dto.ts @@ -8,7 +8,7 @@ import { Type } from 'class-transformer'; import { IsMsaId } from '#utils/decorators/is-msa-id.decorator'; export class ConnectionDto { - @IsMsaId({ each: true, message: 'dsnpId should be a valid positive number' }) + @IsMsaId({ each: true }) @ApiProperty({ description: 'MSA Id representing the target of this connection', type: String, example: '3' }) dsnpId: string; diff --git a/libs/types/src/dtos/graph/graph-key-pair.dto.ts b/libs/types/src/dtos/graph/graph-key-pair.dto.ts index 29ac1fab..1b5397c1 100644 --- a/libs/types/src/dtos/graph/graph-key-pair.dto.ts +++ b/libs/types/src/dtos/graph/graph-key-pair.dto.ts @@ -6,7 +6,7 @@ import { IsHexValue } from '#utils/decorators/is-hex-value.decorator'; // DTO for the graph key pair export class GraphKeyPairDto { - @IsHexValue({ minLength: 64, maxLength: 64, message: 'publicKey should be a 32 bytes value in hex format!' }) + @IsHexValue({ minLength: 64, maxLength: 64 }) @ApiProperty({ description: 'Public graph encryption key as a hex string (prefixed with "0x")', type: String, @@ -14,7 +14,7 @@ export class GraphKeyPairDto { }) publicKey: HexString; - @IsHexValue({ minLength: 64, maxLength: 64, message: 'privateKey should be a 32 bytes value in hex format!' }) + @IsHexValue({ minLength: 64, maxLength: 64 }) @ApiProperty({ description: 'Private graph encryption key as a hex string (prefixed with "0x")', type: String, diff --git a/libs/types/src/dtos/graph/graph-query-params.dto.ts b/libs/types/src/dtos/graph/graph-query-params.dto.ts index f4467ce5..fa754afb 100644 --- a/libs/types/src/dtos/graph/graph-query-params.dto.ts +++ b/libs/types/src/dtos/graph/graph-query-params.dto.ts @@ -9,7 +9,7 @@ export class GraphsQueryParamsDto { @IsArray() @ArrayNotEmpty() @ArrayUnique() - @IsMsaId({ each: true, message: 'dsnpId should be a valid positive number' }) + @IsMsaId({ each: true }) @ApiProperty({ description: 'Array of MSA Ids for which to query graphs', example: ['2', '3', '4', '5'], diff --git a/libs/types/src/dtos/graph/provider-graph.dto.ts b/libs/types/src/dtos/graph/provider-graph.dto.ts index 24321a92..320f2e8b 100644 --- a/libs/types/src/dtos/graph/provider-graph.dto.ts +++ b/libs/types/src/dtos/graph/provider-graph.dto.ts @@ -10,7 +10,7 @@ export type ConnectionsDtoWrapper = { }; export class ProviderGraphDto { - @IsMsaId({ message: 'dsnpId should be a valid positive number' }) + @IsMsaId() @ApiProperty({ description: 'MSA Id that owns the connections represented in this object', example: '2', diff --git a/libs/types/src/dtos/graph/watch-graphs.dto.ts b/libs/types/src/dtos/graph/watch-graphs.dto.ts index 2df6fcca..836bc901 100644 --- a/libs/types/src/dtos/graph/watch-graphs.dto.ts +++ b/libs/types/src/dtos/graph/watch-graphs.dto.ts @@ -6,7 +6,7 @@ export class WatchGraphsDto { @IsOptional() @IsArray() @ArrayUnique() - @IsMsaId({ each: true, message: 'dsnpId should be a valid positive number' }) + @IsMsaId({ each: true }) @ApiProperty({ required: false, description: 'MSA Ids for which to watch for graph updates', diff --git a/libs/utils/src/decorators/is-account-id-address.decorator.ts b/libs/utils/src/decorators/is-account-id-address.decorator.ts new file mode 100644 index 00000000..24692e8f --- /dev/null +++ b/libs/utils/src/decorators/is-account-id-address.decorator.ts @@ -0,0 +1,29 @@ +import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator'; + +export function IsAccountIdOrAddress(validationOptions?: ValidationOptions) { + // eslint-disable-next-line func-names + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'IsAccountIdOrAddress', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: unknown, _args: ValidationArguments) { + const hexPattern = /^0x[A-F0-9]{64}$/i; + const ss58Pattern = /^[1-9A-HJ-NP-Za-km-z]{46,50}$/; + + if (typeof value !== 'string') { + return false; + } + + const isValidHex = hexPattern.test(value) && value.length % 2 === 0; + return isValidHex || ss58Pattern.test(value); + }, + defaultMessage(args?: ValidationArguments): string { + return `${args.property} should be a 32 bytes value in Hex or SS58 format!`; + }, + }, + }); + }; +} diff --git a/libs/utils/src/decorators/is-hex-value.decorator.ts b/libs/utils/src/decorators/is-hex-value.decorator.ts index ce666696..8aa727cd 100644 --- a/libs/utils/src/decorators/is-hex-value.decorator.ts +++ b/libs/utils/src/decorators/is-hex-value.decorator.ts @@ -25,6 +25,15 @@ export function IsHexValue(validationOptions?: IsHexValueValidationOption) { // ensure the length is always even return re.test(value) && value.length % 2 === 0; }, + defaultMessage(args?: ValidationArguments): string { + if (validationOptions.minLength === validationOptions.maxLength) { + return `${args.property} should be in hex format with length of ${validationOptions.minLength / 2} bytes!`; + } + if (validationOptions.maxLength === undefined) { + return `${args.property} should be in hex format with a minimum length of ${validationOptions.minLength / 2} bytes!`; + } + return `${args.property} should be be in hex format with a length between ${validationOptions.minLength / 2} and ${validationOptions.maxLength / 2} bytes.`; + }, }, }); }; diff --git a/libs/utils/src/decorators/is-int-value.decorator.ts b/libs/utils/src/decorators/is-int-value.decorator.ts new file mode 100644 index 00000000..b3bbe7f8 --- /dev/null +++ b/libs/utils/src/decorators/is-int-value.decorator.ts @@ -0,0 +1,39 @@ +import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator'; + +interface IsIntValueValidationOption extends ValidationOptions { + minValue: number; + maxValue?: number | undefined; +} + +export function IsIntValue(validationOptions?: IsIntValueValidationOption) { + // eslint-disable-next-line func-names + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'IsIntValue', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: unknown, _args: ValidationArguments) { + const re = /^[+-]?[0-9]+$/; + + if ((typeof value === 'string' && re.test(value)) || typeof value === 'number') { + const numberValue = BigInt(value); + if (validationOptions.minValue !== undefined) { + return validationOptions.minValue <= numberValue && numberValue <= validationOptions.maxValue; + } + return validationOptions.minValue <= numberValue; + } + + return false; + }, + defaultMessage(args?: ValidationArguments): string { + if (validationOptions.maxValue === undefined) { + return `${args.property} should be a number greater than or equal to ${validationOptions.minValue}!`; + } + return `${args.property} should be a number between ${validationOptions.minValue} and ${validationOptions.maxValue}!`; + }, + }, + }); + }; +} diff --git a/libs/utils/src/decorators/is-msa-id.decorator.ts b/libs/utils/src/decorators/is-msa-id.decorator.ts index c7be28d6..f3991757 100644 --- a/libs/utils/src/decorators/is-msa-id.decorator.ts +++ b/libs/utils/src/decorators/is-msa-id.decorator.ts @@ -10,17 +10,18 @@ export function IsMsaId(validationOptions?: ValidationOptions) { options: validationOptions, validator: { validate(value: unknown, _args: ValidationArguments) { - const re = /^[1-9][0-9]*$/; + const re = /^[0-9]+$/; - let numberValue; - if ((typeof value === 'string' && re.test(value)) || typeof value === 'number') { - numberValue = BigInt(value); - } else { - return false; + if (typeof value === 'string' && re.test(value)) { + return Number(value) > 0; } - - // ensure the value is up to u32 - return numberValue <= 4_294_967_296; + if (typeof value === 'number') { + return value > 0; + } + return false; + }, + defaultMessage(args?: ValidationArguments): string { + return `${args.property} should be a valid positive number!`; }, }, }); diff --git a/libs/utils/src/decorators/is-schema-id.decorator.ts b/libs/utils/src/decorators/is-schema-id.decorator.ts new file mode 100644 index 00000000..4704b3c9 --- /dev/null +++ b/libs/utils/src/decorators/is-schema-id.decorator.ts @@ -0,0 +1,29 @@ +import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator'; + +export function IsSchemaId(validationOptions?: ValidationOptions) { + // eslint-disable-next-line func-names + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'IsSchemaId', + target: object.constructor, + propertyName, + options: validationOptions, + validator: { + validate(value: unknown, _args: ValidationArguments) { + const re = /^[0-9]+$/; + + if ((typeof value === 'string' && re.test(value)) || typeof value === 'number') { + const numberValue = Number(value); + // ensure the value is up to u16 + return numberValue > 0 && numberValue <= 65_536; + } + + return false; + }, + defaultMessage(args?: ValidationArguments): string { + return `${args.property} should be a positive number!`; + }, + }, + }); + }; +} diff --git a/libs/account-lib/src/utils/custom.decorator.ts b/libs/utils/src/decorators/is-signature.decorator.ts similarity index 53% rename from libs/account-lib/src/utils/custom.decorator.ts rename to libs/utils/src/decorators/is-signature.decorator.ts index a81c49a5..564dfd33 100644 --- a/libs/account-lib/src/utils/custom.decorator.ts +++ b/libs/utils/src/decorators/is-signature.decorator.ts @@ -1,23 +1,26 @@ import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator'; -export function IsHexPublicKey(validationOptions?: ValidationOptions) { +export function IsSignature(validationOptions?: ValidationOptions) { // eslint-disable-next-line func-names return function (object: object, propertyName: string) { registerDecorator({ - name: 'IsHexPublicKey', + name: 'IsSignature', target: object.constructor, propertyName, options: validationOptions, validator: { validate(value: unknown, _args: ValidationArguments) { - const re = /^0x[A-F0-9]{64}$/i; + const re = /^0x[A-F0-9]{128,130}$/i; if (typeof value !== 'string') { - console.error('Invalid Public key'); return false; } - return re.test(value); + // ensure the length is always even + return re.test(value) && value.length % 2 === 0; + }, + defaultMessage(args?: ValidationArguments): string { + return `${args.property} should be a 64 (or 65 if it is MultiSignature type) bytes value in hex format!`; }, }, }); diff --git a/metadata.raw.ts.template b/metadata.raw.ts.template new file mode 100644 index 00000000..bdd83281 --- /dev/null +++ b/metadata.raw.ts.template @@ -0,0 +1,4 @@ +/* eslint-disable */ +export default async () => { + return {}; +}; \ No newline at end of file diff --git a/openapi-specs/account.openapi.json b/openapi-specs/account.openapi.json index 60b4144e..844bcef4 100644 --- a/openapi-specs/account.openapi.json +++ b/openapi-specs/account.openapi.json @@ -62,6 +62,8 @@ "name": "msaId", "required": true, "in": "path", + "description": "Msa Id of requested account", + "example": "2", "schema": { "type": "string" } @@ -93,6 +95,8 @@ "name": "accountId", "required": true, "in": "path", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N", "schema": { "type": "string" } @@ -124,6 +128,8 @@ "name": "accountId", "required": true, "in": "path", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N", "schema": { "type": "string" } @@ -187,9 +193,9 @@ "name": "msaId", "required": true, "in": "path", - "description": "Message Source Account Identifier", + "description": "MSA Id of the user requesting the delegation", + "example": "3", "schema": { - "format": "int64", "type": "string" } } @@ -220,9 +226,9 @@ "name": "msaId", "required": true, "in": "path", - "description": "Message Source Account Identifier", + "description": "MSA Id of the user requesting the delegation", + "example": "3", "schema": { - "format": "int64", "type": "string" } }, @@ -230,9 +236,9 @@ "name": "providerId", "required": false, "in": "path", - "description": "Provider ID", + "description": "MSA Id of the provider to whom the requesting user wishes to delegate", + "example": "1", "schema": { - "format": "int64", "type": "string" } } @@ -263,6 +269,8 @@ "name": "msaId", "required": true, "in": "path", + "description": "Msa Id of requested account", + "example": "2", "schema": { "type": "string" } @@ -294,6 +302,8 @@ "name": "accountId", "required": true, "in": "path", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N", "schema": { "type": "string" } @@ -302,6 +312,8 @@ "name": "providerId", "required": true, "in": "path", + "description": "Msa Id of provider", + "example": "1", "schema": { "type": "string" } @@ -415,7 +427,10 @@ "name": "newHandle", "required": true, "in": "path", + "description": "newHandle in the request", + "example": "handle", "schema": { + "minLength": 3, "type": "string" } } @@ -439,6 +454,8 @@ "name": "msaId", "required": true, "in": "path", + "description": "Msa Id of requested account", + "example": "2", "schema": { "type": "string" } @@ -488,6 +505,8 @@ "name": "msaId", "required": true, "in": "path", + "description": "Msa Id of requested account", + "example": "2", "schema": { "type": "string" } @@ -512,7 +531,8 @@ "name": "msaId", "required": true, "in": "query", - "description": "MSA ID of account to generate a payload for", + "description": "MSA Id representing the target of this request", + "example": "3", "schema": { "type": "string" } @@ -521,7 +541,8 @@ "name": "newKey", "required": true, "in": "query", - "description": "Public key should be a 32 bytes value in hex format!", + "description": "New public key to be added to the account (32-byte value in hex format)", + "example": "0x0ed2f8c714efcac51ca2325cfe95637e5e0b898ae397aa365978b7348a717d0b", "schema": { "type": "string" } @@ -677,7 +698,9 @@ "type": "string" }, "signature": { - "type": "string" + "type": "string", + "description": "Signature of the payload", + "example": "0x64f8dd8846ba72cbb1954761ec4b2e44b886abb4b4ef7455b869355f17b4ce4a601ad26eabc57a682244a97bc9a2001b59469ae76fea105b724e988967d4928d" } }, "required": [ @@ -689,7 +712,9 @@ "type": "object", "properties": { "message": { - "type": "string" + "type": "string", + "description": "Error message", + "example": "Some error" } }, "required": [ @@ -711,13 +736,17 @@ "type": "object", "properties": { "pallet": { - "type": "string" + "type": "string", + "minLength": 1 }, "extrinsicName": { - "type": "string" + "type": "string", + "minLength": 1 }, "encodedExtrinsic": { - "type": "string" + "type": "string", + "description": "Hex-encoded representation of the extrinsic", + "example": "0x00112233" } }, "required": [ @@ -748,7 +777,7 @@ "example": { "siwsPayload": { "message": "0x1234567890abcdef", - "signature": "0x1234567890abcdef" + "signature": "0x64f8dd8846ba72cbb1954761ec4b2e44b886abb4b4ef7455b869355f17b4ce4a601ad26eabc57a682244a97bc9a2001b59469ae76fea105b724e988967d4928d" }, "err": { "message": "Error message" @@ -786,13 +815,19 @@ "type": "object", "properties": { "encodedExtrinsic": { - "type": "string" + "type": "string", + "description": "Hex-encoded representation of the \"RetireMsa\" extrinsic", + "example": "0x1234" }, "payloadToSign": { - "type": "string" + "type": "string", + "description": "payload to be signed", + "example": "0x1234" }, "accountId": { - "type": "string" + "type": "string", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" } }, "required": [ @@ -805,16 +840,24 @@ "type": "object", "properties": { "encodedExtrinsic": { - "type": "string" + "type": "string", + "description": "Hex-encoded representation of the \"RetireMsa\" extrinsic", + "example": "0x1234" }, "payloadToSign": { - "type": "string" + "type": "string", + "description": "payload to be signed", + "example": "0x1234" }, "accountId": { - "type": "string" + "type": "string", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" }, "signature": { - "type": "string" + "type": "string", + "description": "signature of the owner", + "example": "0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85" } }, "required": [ @@ -911,16 +954,24 @@ "type": "object", "properties": { "accountId": { - "type": "string" + "type": "string", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" }, "providerId": { - "type": "string" + "type": "string", + "description": "MSA Id of the provider to whom the requesting user wishes to delegate", + "example": "3" }, "encodedExtrinsic": { - "type": "object" + "type": "string", + "description": "Hex-encoded representation of the \"revokeDelegation\" extrinsic", + "example": "0x1234" }, "payloadToSign": { - "type": "object" + "type": "string", + "description": "payload to be signed", + "example": "0x1234" } }, "required": [ @@ -934,19 +985,29 @@ "type": "object", "properties": { "accountId": { - "type": "string" + "type": "string", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" }, "providerId": { - "type": "string" + "type": "string", + "description": "MSA Id of the provider to whom the requesting user wishes to delegate", + "example": "3" }, "encodedExtrinsic": { - "type": "object" + "type": "string", + "description": "Hex-encoded representation of the \"revokeDelegation\" extrinsic", + "example": "0x1234" }, "payloadToSign": { - "type": "object" + "type": "string", + "description": "payload to be signed", + "example": "0x1234" }, "signature": { - "type": "object" + "type": "string", + "description": "signature of the owner", + "example": "0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85" } }, "required": [ @@ -961,10 +1022,14 @@ "type": "object", "properties": { "baseHandle": { - "type": "string" + "type": "string", + "description": "base handle in the request", + "example": "handle" }, "expiration": { - "type": "number" + "type": "number", + "description": "expiration block number for this payload", + "example": "1" } }, "required": [ @@ -976,13 +1041,17 @@ "type": "object", "properties": { "accountId": { - "type": "string" + "type": "string", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" }, "payload": { "$ref": "#/components/schemas/HandlePayloadDto" }, "proof": { - "type": "string" + "type": "string", + "description": "proof is the signature for the payload", + "example": "0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85" } }, "required": [ @@ -995,13 +1064,19 @@ "type": "object", "properties": { "msaId": { - "type": "string" + "type": "string", + "description": "MSA Id of the user requesting the new key", + "example": "3" }, "expiration": { - "type": "number" + "type": "number", + "description": "expiration block number for this payload", + "example": "1" }, "newPublicKey": { - "type": "string" + "type": "string", + "description": "newPublicKey in hex format", + "example": "0x0ed2f8c714efcac51ca2325cfe95637e5e0b898ae397aa365978b7348a717d0b" } }, "required": [ @@ -1014,13 +1089,19 @@ "type": "object", "properties": { "msaOwnerAddress": { - "type": "string" + "type": "string", + "description": "msaOwnerAddress representing the target of this request", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" }, "msaOwnerSignature": { - "type": "string" + "type": "string", + "description": "msaOwnerSignature is the signature by msa owner", + "example": "0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85" }, "newKeyOwnerSignature": { - "type": "string" + "type": "string", + "description": "newKeyOwnerSignature is the signature with new key", + "example": "0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85" }, "payload": { "$ref": "#/components/schemas/KeysRequestPayloadDto" @@ -1037,18 +1118,30 @@ "type": "object", "properties": { "schemaId": { - "type": "number" + "type": "number", + "description": "schemaId related to the payload", + "example": "1" }, "targetHash": { - "type": "number" + "type": "number", + "description": "targetHash related to the stateful storage", + "example": "1234" }, "expiration": { - "type": "number" + "type": "number", + "description": "expiration block number for this payload", + "example": "1" }, "actions": { + "example": [ + { + "type": "ADD_ITEM", + "encodedPayload": "0x1122" + } + ], "type": "array", "items": { - "type": "object" + "type": "string" } } }, @@ -1063,13 +1156,17 @@ "type": "object", "properties": { "accountId": { - "type": "string" + "type": "string", + "description": "AccountId in hex or SS58 format", + "example": "1LSLqpLWXo7A7xuiRdu6AQPnBPNJHoQSu8DBsUYJgsNEJ4N" }, "payload": { "$ref": "#/components/schemas/ItemizedSignaturePayloadDto" }, "proof": { - "type": "string" + "type": "string", + "description": "proof is the signature for the payload", + "example": "0x065d733ca151c9e65b78f2ba77348224d31647e6913c44ad2765c6e8ba06f834dc21d8182447d01c30f84a41d90a8f2e58001d825c6f0d61b0afe89f984eec85" } }, "required": [ diff --git a/package.json b/package.json index 6b5f06c6..aa5c6670 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,10 @@ "start:graph-worker:prod": "node dist/apps/graph-worker/main.js", "start:graph-worker:dev": "dotenvx run -f .env.graph -- nest start graph-worker --watch", "start:graph-worker:debug": "dotenvx run -f .env.graph -- nest start graph-worker --debug --watch", + "pregenerate:metadata:account": "cp metadata.raw.ts.template apps/account-api/src/metadata.ts", + "pregenerate:metadata:content-publishing": "cp metadata.raw.ts.template apps/content-publishing-api/src/metadata.ts", + "pregenerate:metadata:content-watcher": "cp metadata.raw.ts.template apps/content-watcher-api/src/metadata.ts", + "pregenerate:metadata:graph": "cp metadata.raw.ts.template apps/graph-api/src/metadata.ts", "generate:metadata:account": "npx ts-node -r tsconfig-paths/register apps/account-api/src/generate-metadata.ts", "generate:metadata:content-publishing": "npx ts-node -r tsconfig-paths/register apps/content-publishing-api/src/generate-metadata.ts", "generate:metadata:content-watcher": "npx ts-node -r tsconfig-paths/register apps/content-watcher/src/generate-metadata.ts",