diff --git a/src/modules/account/account.ts b/src/modules/account/account.ts index fe3d112..c8fda2d 100644 --- a/src/modules/account/account.ts +++ b/src/modules/account/account.ts @@ -7,26 +7,29 @@ import { import { paths } from '../../api'; import { GTWError } from '../../helpers/custom-error'; import { CryptoService } from '../../services/crypto-service'; +import { Wallet } from './wallet'; export class Account { private client: OpenAPIClient; private cryptoService: CryptoService; + protected wallet: Wallet; constructor(client: OpenAPIClient) { this.client = client; this.cryptoService = new CryptoService(); + this.wallet = new Wallet(client); } /** - * The `createAccount` function in TypeScript asynchronously creates a new account by verifying a - * message, sending a POST request to an endpoint, and returning a token upon success. - * @param {AccountCreateRequest} - The `createAccount` function takes in an `AccountCreateRequest` - * object with the following parameters: - * @returns The `createAccount` function returns a token from the `data` object after successfully - * verifying the message signature and creating an account with the provided username and wallet - * address. + * The `create` function in TypeScript asynchronously verifies a message, then sends a POST request to + * create an account and returns the generated token. + * @param {AccountCreateRequest} - The `create` method takes in an `AccountCreateRequest` object with + * the following parameters: + * @returns The `create` function is returning a token from the `data` object after successfully + * verifying the message signature and creating a new account with the provided information (message, + * signature, username, wallet address). */ - async createAccount({ + async create({ message, signature, username, @@ -46,13 +49,13 @@ export class Account { } /** - * This async function retrieves account information by making a GET request to '/accounts/me' and - * handles errors by throwing a custom GTWError if any occur. - * @returns The `getAccountInfo` function is returning the `data` object fetched from the - * `/accounts/me` endpoint. If there is an error during the API call, a `GTWError` is thrown with the - * error and response details. + * This async function retrieves the account information for the currently authenticated user. + * @returns The `getMe` function is returning a Promise that resolves to a `MyAccountResponse` object. + * This object is obtained by making a GET request to the '/accounts/me' endpoint using + * `this.client.GET('/accounts/me')`. If there is an error during the request, a `GTWError` is thrown + * with the error and response details. Otherwise, the function returns the data obtained from */ - async getAccountInfo(): Promise { + async getMe(): Promise { const { data, response, error } = await this.client.GET('/accounts/me'); if (error) { @@ -63,15 +66,19 @@ export class Account { } /** - * The function `updateAccount` asynchronously updates the profile picture and username of the current - * account using a PATCH request. - * @param - The `updateAccount` function takes in an object with two properties: `profile_picture` and - * `username`, both of type string. These values are used to update the user's account information by - * making a PATCH request to the `/accounts/me` endpoint with the provided data. If there is an error - * @returns The `updateAccount` function is returning the `data` object after making a PATCH request to - * update the account information (profile picture and username). + * This TypeScript function updates the profile picture and username of the current user's account + * using a PATCH request. + * @param {string} [profile_picture] - The `profile_picture` parameter in the `updateMe` function is + * used to update the profile picture of the current user's account. It is an optional parameter, + * meaning you can choose to provide a new profile picture URL or leave it empty to not update the + * profile picture. + * @param {string} [username] - The `username` parameter in the `updateMe` function is used to update + * the username of the account. If a new `username` value is provided when calling this function, it + * will be used to update the username associated with the account. + * @returns The `updateMe` function is returning the `data` object after making a PATCH request to + * update the user's profile picture and username. */ - async updateAccount(profile_picture?: string, username?: string) { + async updateMe(profile_picture?: string, username?: string) { const { data, error, response } = await this.client.PATCH('/accounts/me', { body: { profile_picture, username }, }); diff --git a/src/modules/account/wallet.ts b/src/modules/account/wallet.ts new file mode 100644 index 0000000..010adb7 --- /dev/null +++ b/src/modules/account/wallet.ts @@ -0,0 +1,67 @@ +import { MediaType } from 'openapi-typescript-helpers'; +import { OpenAPIClient, MyAccountResponse } from '../../common/types'; +import { paths } from '../../api'; +import { GTWError } from '../../helpers/custom-error'; +import { ValidationService } from '../../services/validator-service'; + +export class Wallet { + private client: OpenAPIClient; + private validationService: ValidationService; + + constructor(client: OpenAPIClient) { + this.client = client; + this.validationService = new ValidationService(); + } + + /** + * The `add` function in TypeScript adds a new wallet address to the user's account asynchronously. + * @param {string} address - The `add` function in the code snippet you provided is an asynchronous + * function that takes a `string` parameter called `address`. The function first checks if the + * `address` string is empty using a validation service method `isEmptyString(address)`. Then, it makes + * a POST request to a specific endpoint + * @returns The `add` function is returning a `Promise` that resolves to a `MyAccountResponse` object. + */ + + async add(address: string): Promise { + this.validationService.isEmptyString(address); + const { data, error, response } = await this.client.POST( + '/accounts/me/wallets', + { + body: { address: address }, + }, + ); + + if (error) { + throw new GTWError(error, response); + } + + return data; + } + + /** + * This TypeScript function asynchronously removes a wallet associated with a specific address from the + * user's account. + * @param {string} address - The `remove` function is an asynchronous function that takes a `string` + * parameter called `address`. This function sends a DELETE request to the endpoint + * `/accounts/me/wallets/{address}` with the provided `address` parameter as part of the path. It then + * returns a `Promise` that resolves + * @returns The `remove` function is returning a `Promise` that resolves to a `MyAccountResponse` + * object. + */ + async remove(address: string): Promise { + this.validationService.isEmptyString(address); + + const { data, error, response } = await this.client.DELETE( + '/accounts/me/wallets/{address}', + { + params: { path: { address: address } }, + }, + ); + + if (error) { + throw new GTWError(error, response); + } + + return data; + } +} diff --git a/src/modules/data-model/data-model.ts b/src/modules/data-model/data-model.ts index 9d2013c..ca331bd 100644 --- a/src/modules/data-model/data-model.ts +++ b/src/modules/data-model/data-model.ts @@ -20,23 +20,20 @@ export class DataModel { this.client = client; this.validationService = validationService; } - /** - * This TypeScript function asynchronously fetches data models from a server using GET request with - * optional pagination parameters. - * @param {number} [page=1] - The `page` parameter is used to specify the page number of the data - * models to retrieve. By default, it is set to 1, meaning that the function will retrieve the data - * models from the first page. - * @param {number} [page_size=10] - The `page_size` parameter in the `getDataModels` function - * specifies the number of data models to be retrieved per page. By default, it is set to 10, meaning - * that when the function is called without providing a specific `page_size` value, it will retrieve - * 10 data models per - * @returns The `getDataModels` function is returning the data fetched from the API endpoint - * `/data-models` with the specified pagination parameters `page` and `page_size`. If there is an - * error during the API request, a `GTWError` is thrown with the error and response details. If there - * is no error, the function returns the fetched data. + * This async function retrieves a paginated list of data models from a server using GET request. + * @param {number} [page=1] - The `page` parameter in the `getAll` function is used to specify the page + * number of the data to retrieve. By default, it is set to 1 if not provided when calling the + * function. + * @param {number} [page_size=10] - The `page_size` parameter in the `getAll` function specifies the + * number of items to be displayed per page when fetching data from the `/data-models` endpoint. By + * default, it is set to 10, meaning that the API will return a maximum of 10 data items per page + * unless + * @returns The `getAll` function is returning a `HelperPaginatedResponse` object containing data of + * type `DataModelType`. */ - async getDataModels(page: number = 1, page_size: number = 10) { + + async getAll(page: number = 1, page_size: number = 10) { const { data, error, response } = await this.client.GET('/data-models', { params: { query: { page, page_size } }, }); @@ -48,17 +45,16 @@ export class DataModel { } /** - * This TypeScript function creates a data model by sending a POST request to a specified endpoint. - * @param {DataModelRequest} dataModelInput - The `dataModelInput` parameter in the `createDataModel` - * function is of type `DataModelRequest`. It is the input data that will be used to create a new - * data model. This input likely contains information such as the name, fields, and other properties - * of the data model that will - * @returns The `createDataModel` function is returning the `data` object after making a POST request - * to create a data model. + * This TypeScript function creates a new data model by sending a POST request to a specified endpoint. + * @param {DataModelRequest} dataModelInput - The `dataModelInput` parameter in the `create` function + * is of type `DataModelRequest`. This parameter likely contains the data needed to create a new data + * model, such as the attributes and properties of the data model. + * @returns The `create` method is returning a Promise that resolves to a `DataModelType` object. The + * method makes a POST request to the '/data-models' endpoint with the `dataModelInput` as the request + * body. If there is an error during the POST request, a `GTWError` is thrown with the error and + * response details. Otherwise, the method returns the `data` */ - async createDataModel( - dataModelInput: DataModelRequest, - ): Promise { + async create(dataModelInput: DataModelRequest): Promise { const { data, error, response } = await this.client.POST('/data-models', { body: dataModelInput, }); @@ -69,25 +65,14 @@ export class DataModel { return data!; } - /** - * This TypeScript function updates a data model using a PUT request with error handling. - * @param {number} dataModelId - The `dataModelId` parameter is the unique identifier of the data - * model that you want to update. It is a number that specifies which data model in the system you - * are targeting for the update operation. - * @param {DataModelRequest} dataModelInput - The `dataModelInput` parameter in the `updateDataModel` - * function is of type `DataModelRequest`. It is the data that will be used to update the data model - * with the specified `dataModelId`. - * @returns The `updateDataModel` function is returning the updated data model after making a PUT - * request to the server with the provided `dataModelInput` for the specified `dataModelId`. - */ - // async updateDataModel( + // async update( // dataModelId: number, // dataModelInput: DataModelRequest, // ): Promise { // const { data, error, response } = await this.client.PUT( // '/data-models/{id}', // { - // body: {}, + // body: { ...dataModelInput }, // params: { path: { id: dataModelId } }, // }, // ); @@ -100,17 +85,16 @@ export class DataModel { // } /** - * This TypeScript function asynchronously fetches a data model by its ID using a GET request. + * This function asynchronously retrieves a data model by its ID using a GET request. * @param {number} dataModelId - The `dataModelId` parameter is a number that represents the unique - * identifier of a data model. This function `getDataModelById` is an asynchronous function that - * retrieves a data model by its ID using an HTTP GET request to a specific endpoint. It uses the - * `dataModelId` parameter to specify - * @returns The `getDataModelById` function is returning the data fetched from the API endpoint for - * the specified `dataModelId`. If there is an error during the API request, it will throw a - * `GTWError` with the error and response details. If there is no error, it will return the retrieved - * data. + * identifier of a data model. This identifier is used to retrieve a specific data model from the + * server. + * @returns The `getById` function is returning a Promise that resolves to a `DataModelType` object. + * The function makes an asynchronous GET request to retrieve a data model by its ID, and if + * successful, it returns the data model. If there is an error during the request, it throws a + * `GTWError` with the error and response details. */ - async getDataModelById(dataModelId: number): Promise { + async getById(dataModelId: number): Promise { const { data, error, response } = await this.client.GET( '/data-models/{id}', { @@ -125,20 +109,17 @@ export class DataModel { } /** - * This TypeScript function retrieves data models specific to the current user with optional - * pagination parameters. - * @param {number} [page=1] - The `page` parameter in the `getMyDataModels` function is used to - * specify the page number of the data models to retrieve. By default, it is set to 1 if not - * provided. - * @param {number} [page_size=10] - The `page_size` parameter in the `getMyDataModels` function - * specifies the number of data models to be retrieved per page. By default, it is set to 10, meaning - * that the function will retrieve 10 data models per page unless specified otherwise. - * @returns The `getMyDataModels` function returns the data fetched from the API endpoint - * `/data-models/me` based on the provided `page` and `page_size` parameters. If there is an error - * during the API request, a `GTWError` is thrown with the error and response details. If the request - * is successful, the function returns the retrieved data. + * This TypeScript function asynchronously retrieves paginated data models specific to the current + * user. + * @param {number} [page=1] - The `page` parameter in the `getMy` function is used to specify the page + * number of the data to retrieve. By default, it is set to 1 if not provided. + * @param {number} [page_size=10] - The `page_size` parameter in the `getMy` function specifies the + * number of items to be displayed per page when fetching data from the endpoint `/data-models/me`. By + * default, if not provided, the `page_size` is set to 10. This means that the API will return + * @returns The `getMy` function returns a `HelperPaginatedResponse` object containing data of type + * `DataModelType`. */ - async getMyDataModels(page: number = 1, page_size: number = 10) { + async getMy(page: number = 1, page_size: number = 10) { const { data, response, error } = await this.client.GET('/data-models/me', { params: { query: { page, page_size } }, }); diff --git a/src/services/validator-service.ts b/src/services/validator-service.ts index 520da48..4814d6a 100644 --- a/src/services/validator-service.ts +++ b/src/services/validator-service.ts @@ -20,6 +20,25 @@ export class ValidationService { return true; } + /** + * The function checks if a string is empty or contains only whitespace. + * @param {string} value - The `value` parameter in the `isEmptyString` function is a string that you + * want to check for emptiness or containing only whitespace characters. The function will return + * `true` if the string is not empty or does not contain only whitespace characters, otherwise it will + * throw an error indicating that the string + * @returns The `isEmptyString` function is returning a boolean value, either `true` if the provided + * string is not empty or contains only whitespace, or it will throw an error if the string is empty or + * contains only whitespace. + */ + public isEmptyString(value: string): boolean { + if (value.trim().length === 0) { + throw new Error( + 'The provided string is empty or contains only whitespace', + ); + } + return true; + } + /** * The function `validateString` checks if a given string meets a minimum length requirement and * returns a boolean value accordingly. diff --git a/test/account.test.ts b/test/account.test.ts index 4eab9be..069f94e 100644 --- a/test/account.test.ts +++ b/test/account.test.ts @@ -28,7 +28,7 @@ describe('Account', () => { it('should return with new account with given credentials', async () => { mockPost.mockResolvedValue(successMessage({ data: { token: 'test' } })); - const result = await account.createAccount( + const result = await account.create( authDetails({ username: 'testuser' }), ); @@ -42,7 +42,7 @@ describe('Account', () => { mockPost.mockResolvedValue(errorMessage()); await expect( - account.createAccount(authDetails({ username: 'testuser' })), + account.create(authDetails({ username: 'testuser' })), ).rejects.toThrow(GTWError); expect(mockPost).toHaveBeenCalledWith(routes.CreateAccount, { body: authDetails({ username: 'testuser' }), @@ -62,7 +62,7 @@ describe('Account', () => { error: null, }); - const result = await account.getAccountInfo(); + const result = await account.getMe(); expect(result).toEqual(mockData); expect(mockClient.GET).toHaveBeenCalledWith(routes.GetMyAccount); @@ -71,7 +71,7 @@ describe('Account', () => { it('should throw GTWError when API call fails', async () => { mockGet.mockResolvedValue(errorMessage()); - await expect(account.getAccountInfo()).rejects.toThrow(GTWError); + await expect(account.getMe()).rejects.toThrow(GTWError); expect(mockClient.GET).toHaveBeenCalledWith(routes.GetMyAccount); }); }); @@ -95,7 +95,7 @@ describe('Account', () => { error: null, }); - const result = await account.updateAccount( + const result = await account.updateMe( 'https://example.com/profile-picture.png', ); @@ -117,7 +117,7 @@ describe('Account', () => { error: { error: 'Unauthorized' }, }); - await expect(account.updateAccount()).rejects.toThrow(GTWError); + await expect(account.updateMe()).rejects.toThrow(GTWError); expect(mockClient.PATCH).toHaveBeenCalledWith(routes.UpdateAccount, { body: { profile_picture: 'https://example.com/profile-picture.png', diff --git a/test/data-model.test.ts b/test/data-model.test.ts index 949db31..723e177 100644 --- a/test/data-model.test.ts +++ b/test/data-model.test.ts @@ -1,8 +1,8 @@ import { ValidationService } from '../src/services/validator-service'; import { GTWError } from '../src/helpers/custom-error'; import { DataModel } from '../src/modules/data-model/data-model'; -import { Config, DataModelRequest } from '../src/common/types'; -import { mockClient, mockGet, mockPost, mockPut } from './stubs/common.stub'; +import { DataModelRequest } from '../src/common/types'; +import { mockClient, mockGet, mockPost } from './stubs/common.stub'; import { routes } from '../src/common/routes'; const mockValidationService = {} as ValidationService; @@ -29,7 +29,7 @@ describe('DataModel', () => { response: {} as Response, }); - const result = await dataModel.getDataModels(); + const result = await dataModel.getAll(); expect(result).toEqual(mockResponse); expect(mockClient.GET).toHaveBeenCalledWith(routes.GetDataModels, { @@ -46,12 +46,12 @@ describe('DataModel', () => { response: mockResponse, }); - await expect(dataModel.getDataModels()).rejects.toThrow(GTWError); - await expect(dataModel.getDataModels()).rejects.toHaveProperty( + await expect(dataModel.getAll()).rejects.toThrow(GTWError); + await expect(dataModel.getAll()).rejects.toHaveProperty( 'statusCode', 400, ); - await expect(dataModel.getDataModels()).rejects.toHaveProperty( + await expect(dataModel.getAll()).rejects.toHaveProperty( 'message', 'API Error', ); @@ -67,7 +67,7 @@ describe('DataModel', () => { response: {} as Response, }); - const result = await dataModel.getDataModelById(1); + const result = await dataModel.getById(1); expect(result).toEqual(mockResponse); expect(mockClient.GET).toHaveBeenCalledWith(routes.GetDataModelByID, { @@ -84,12 +84,12 @@ describe('DataModel', () => { response: mockResponse, }); - await expect(dataModel.getDataModelById(1)).rejects.toThrow(GTWError); - await expect(dataModel.getDataModelById(1)).rejects.toHaveProperty( + await expect(dataModel.getById(1)).rejects.toThrow(GTWError); + await expect(dataModel.getById(1)).rejects.toHaveProperty( 'statusCode', 404, ); - await expect(dataModel.getDataModelById(1)).rejects.toHaveProperty( + await expect(dataModel.getById(1)).rejects.toHaveProperty( 'message', 'Not Found', ); @@ -110,7 +110,7 @@ describe('DataModel', () => { response: {} as Response, }); - const result = await dataModel.getMyDataModels(); + const result = await dataModel.getMy(); expect(result).toEqual(mockResponse); expect(mockClient.GET).toHaveBeenCalledWith(routes.GetDataModelsByUser, { @@ -127,12 +127,9 @@ describe('DataModel', () => { response: mockResponse, }); - await expect(dataModel.getMyDataModels()).rejects.toThrow(GTWError); - await expect(dataModel.getMyDataModels()).rejects.toHaveProperty( - 'statusCode', - 401, - ); - await expect(dataModel.getMyDataModels()).rejects.toHaveProperty( + await expect(dataModel.getMy()).rejects.toThrow(GTWError); + await expect(dataModel.getMy()).rejects.toHaveProperty('statusCode', 401); + await expect(dataModel.getMy()).rejects.toHaveProperty( 'message', 'Unauthorized', ); @@ -158,7 +155,7 @@ describe('DataModel', () => { mockPost.mockResolvedValue({ data: expectedOutput, error: null }); - const result = await dataModel.createDataModel(input); + const result = await dataModel.create(input); expect(result).toEqual(expectedOutput); expect(mockClient.POST).toHaveBeenCalledWith(routes.CreateDataModel, { @@ -181,7 +178,7 @@ describe('DataModel', () => { response: {}, }); - await expect(dataModel.createDataModel(input)).rejects.toThrow(GTWError); + await expect(dataModel.create(input)).rejects.toThrow(GTWError); expect(mockClient.POST).toHaveBeenCalledWith(routes.CreateDataModel, { body: input, }); @@ -208,7 +205,7 @@ describe('DataModel', () => { // mockPut.mockResolvedValue({ data: expectedOutput, error: null }); - // const result = await dataModel.updateDataModel(dataModelId, input); + // const result = await dataModel.update(dataModelId, input); // expect(result).toEqual(expectedOutput); // expect(mockClient.PUT).toHaveBeenCalledWith(routes.UpdateDataModel, { @@ -234,7 +231,7 @@ describe('DataModel', () => { // }); // await expect( - // dataModel.updateDataModel(dataModelId, input), + // dataModel.update(dataModelId, input), // ).rejects.toThrow(GTWError); // expect(mockClient.PUT).toHaveBeenCalledWith(routes.UpdateDataModel, { // body: input, diff --git a/test/stubs/common.stub.ts b/test/stubs/common.stub.ts index e71f722..694bb2d 100644 --- a/test/stubs/common.stub.ts +++ b/test/stubs/common.stub.ts @@ -18,6 +18,29 @@ export const errorMessage = (overrideError?: any) => ({ export const ID = 1; +export const mockWalletResponse = (overrideParams?: any) => ({ + data: { + created_at: '2023-01-01', + did: 'did:something', + profile_picture: 'profile_picture_url', + storage_size: 1000, + updated_at: '2023-01-02', + username: 'user', + username_updated_at: '2023-01-03', + wallet_addresses: [ + { + account_id: 1, + address: '7z6D7cT8W5BXpHeEwr51wwheFzY7C5L1Jo7f6y9vER4T', + chain: 'eth', + created_at: '2023-01-01', + id: 1, + updated_at: '2023-01-02', + }, + ], + }, + ...overrideParams, +}); + export const authDetails = (overrideAuth?: any) => ({ message: 'test', signature: diff --git a/test/utils.test.ts b/test/utils.test.ts index 8a94c75..265cd95 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -39,6 +39,17 @@ describe('UTILS VALIDATORS TESTING', () => { ).toThrow('f17ac10b-58cc-4372-a567 is not valid'); }); + it('isEmptyString validator', () => { + const result = validationService.isEmptyString('test pda'); + expect(result).toBeDefined(); + expect(() => validationService.isEmptyString('')).toThrow( + 'The provided string is empty or contains only whitespace', + ); + expect(() => validationService.isEmptyString(' ')).toThrow( + 'The provided string is empty or contains only whitespace', + ); + }); + it('string validator', () => { const result = validationService.validateString('test pda'); expect(result).toBeDefined(); diff --git a/test/wallet.test.ts b/test/wallet.test.ts new file mode 100644 index 0000000..2dd28d4 --- /dev/null +++ b/test/wallet.test.ts @@ -0,0 +1,69 @@ +import { OpenAPIClient } from '../src/common/types'; +import { paths } from '../src/api'; + +import { GTWError } from '../src/helpers/custom-error'; +import { MediaType } from 'openapi-typescript-helpers'; +import { + errorMessage, + mockClient, + mockDelete, + mockPost, + successMessage, + mockWalletResponse, +} from './stubs/common.stub'; +import { routes } from '../src/common/routes'; +import { Wallet } from '../src/modules/account/wallet'; + +describe('Wallet', () => { + let wallet: Wallet; + + beforeEach(() => { + wallet = new Wallet( + mockClient as unknown as OpenAPIClient, + ); + }); + + describe('add wallet', () => { + it('should return account with added new wallet', async () => { + mockPost.mockReturnValue(successMessage(mockWalletResponse)); + + const result = await wallet.add( + '7z6D7cT8W5BXpHeEwr51wwheFzY7C5L1Jo7f6y9vER4T', + ); + + expect(result).toBeDefined(); + expect(mockPost).toHaveBeenCalledWith(routes.AddWallet, { + body: { + address: mockWalletResponse().data.wallet_addresses[0].address, + }, + }); + }); + + it('should throw GTWError when API call fails', async () => { + mockPost.mockResolvedValue(errorMessage()); + + await expect(wallet.add('7z6D7cT8W5BXpHeE')).rejects.toThrow(GTWError); + expect(mockPost).toHaveBeenCalledWith(routes.AddWallet, { + body: { + address: mockWalletResponse().data.wallet_addresses[0].address, + }, + }); + }); + }); + + describe('remove wallet', () => { + it('should return account without given wallet address', async () => { + const address = '7z6D7cT8W5BXpHeEwr51wwheFzY7C5L1Jo7f6y9vER4T'; + mockDelete.mockReturnValue( + successMessage(mockWalletResponse({ wallet_addresses: '' })), + ); + + const result = await wallet.remove(address); + + expect(result).toBeDefined(); + expect(mockDelete).toHaveBeenCalledWith(routes.RemoveWallet, { + params: { path: { address } }, + }); + }); + }); +});