Skip to content

Commit

Permalink
feat: implement open api (#4)
Browse files Browse the repository at this point in the history
* feat: add @nestjs/swagger and implement basic docs

* refactor: convert open api dtos to classes to allow for decorators

* refactor: change the versions responses to use dtos and add descriptions

* feat: add new /api/v1 prefix to all routes
  • Loading branch information
kieranroneill authored Mar 18, 2024
1 parent 9ba3430 commit 3f90eac
Show file tree
Hide file tree
Showing 42 changed files with 400 additions and 128 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
- [2.2. Setting up environment variables (optional)](#22-setting-up-environment-variables-optional)
- [2.3. Running locally](#23-running-locally)
* [3. Appendix](#-3-appendix)
- [3.1. Useful commands](#31-useful-commands)
- [3.2. Docker Compose service directory](#32-docker-compose-service-directory)
- [3.1. Documentation](#31-documentation)
- [3.2. Useful commands](#32-useful-commands)
- [3.3. Docker Compose service directory](#33-docker-compose-service-directory)
* [4. How To Contribute](#-4-how-to-contribute)
* [5. License](#-5-license)

Expand Down Expand Up @@ -85,13 +86,19 @@ yarn start

> ⚠️ **NOTE:** The `yarn start` command will run/re-run the setup script, but will not overwrite the file `.env` that was created and edited in section [1.2.](#22-setting-up-environment-variables-optional)
2. Navigate to [http://localhost:3000/versions](http://localhost:3000/versions) to make sure everything is runnning.
2. Navigate to [http://localhost:3000/api/v1/versions](http://localhost:3000/api/v1/versions) to make sure everything is running.

<sup>[Back to top ^][table-of-contents]</sup>

## 📑 3. Appendix

### 3.1. Useful commands
### 3.1. Documentation

The API comes with some OpenAPI documentation. This can be accessed at [http://localhost:3000/api](http://localhost:3000/api).

This documentation outlines the available endpoints available.

### 3.2. Useful commands

| Command | Description |
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------|
Expand All @@ -100,7 +107,7 @@ yarn start

<sup>[Back to top ^][table-of-contents]</sup>

### 3.2. Docker Compose service directory
### 3.3. Docker Compose service directory

Here is a list of all the localhost port mappings for each of the apps

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"@nestjs/core": "^10.0.0",
"@nestjs/event-emitter": "^2.0.4",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.3.0",
"@typegoose/typegoose": "^12.2.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
Expand Down
1 change: 1 addition & 0 deletions src/enums/APIPathEnum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
enum APIPathEnum {
API = 'api',
Chains = 'chains',
Fees = 'fees',
Versions = 'versions',
Expand Down
29 changes: 28 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { LoggerService, ValidationPipe } from '@nestjs/common';
import { NestApplication, NestFactory } from '@nestjs/core';
import { ConfigService } from '@nestjs/config';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import morgan from 'morgan';

// enums
import { EnvironmentVariableKeyEnum } from '@app/enums';
import { APIPathEnum, EnvironmentVariableKeyEnum } from '@app/enums';

// modules
import AppModule from '@app/modules/app/module';
Expand All @@ -14,11 +15,13 @@ import type { IEnvironmentVariables, ILogLevel } from '@app/types';

// utils
import createLoggerService from '@app/utils/createLoggerService';
import parseVersion from '@app/utils/parseVersion';

(async () => {
let app: NestApplication;
let configService: ConfigService<IEnvironmentVariables, true>;
let logger: LoggerService;
let versions: string[];

try {
app = await NestFactory.create(AppModule);
Expand All @@ -27,6 +30,9 @@ import createLoggerService from '@app/utils/createLoggerService';
configService.get<string>(EnvironmentVariableKeyEnum.AppName),
configService.get<ILogLevel>(EnvironmentVariableKeyEnum.LogLevel)
);
versions = parseVersion(
configService.get<string>(EnvironmentVariableKeyEnum.AppVersion)
);

// setup middleware
app.useLogger(logger);
Expand All @@ -39,8 +45,29 @@ import createLoggerService from '@app/utils/createLoggerService';
},
})
);
app.setGlobalPrefix(`${APIPathEnum.API}/v${versions[0]}`);
app.useGlobalPipes(new ValidationPipe()); // for validating query params

// setup open api
SwaggerModule.setup(
APIPathEnum.API,
app,
SwaggerModule.createDocument(
app,
new DocumentBuilder()
.setTitle(
configService.get<string>(EnvironmentVariableKeyEnum.AppName)
)
.setDescription(
`The ${configService.get<string>(EnvironmentVariableKeyEnum.AppName)} API description`
)
.setVersion(
configService.get<string>(EnvironmentVariableKeyEnum.AppVersion)
)
.build()
)
);

await app.listen(
configService.get<number>(EnvironmentVariableKeyEnum.AppPort)
);
Expand Down
13 changes: 9 additions & 4 deletions src/modules/chains/controller.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import { Controller, Get } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';

// configs
import { chains } from '@app/configs';

// dtos
import { GetChainsResponseBodyDTO } from './dtos';

// enums
import { APIPathEnum } from '@app/enums';

// types
import type { IChainResponseBody } from '@app/types';

// utils
import mapChainConfigToChainResponseBody from '@app/utils/mapChainConfigToChainResponseBody';

@Controller(APIPathEnum.Chains)
export default class ChainsController {
@Get()
public async get(): Promise<IChainResponseBody[]> {
@ApiOkResponse({
description: 'Gets the list of the available chains.',
type: [GetChainsResponseBodyDTO],
})
public async get(): Promise<GetChainsResponseBodyDTO[]> {
return chains.map(mapChainConfigToChainResponseBody);
}
}
48 changes: 48 additions & 0 deletions src/modules/chains/dtos/GetChainsResponseBodyDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ApiProperty } from '@nestjs/swagger';

interface IProps {
canonicalName: string;
chainId: string;
namespace: string;
reference: string;
testnet: boolean;
}

export default class GetChainsResponseBodyDTO {
@ApiProperty({
description: 'The canonical name for the chain.',
})
public readonly canonicalName: string;
@ApiProperty({
description:
'The ID of the chain. This is the concatenation of the chain namespace and reference as specified in CAIP-2.',
})
public readonly chainId: string;
@ApiProperty({
description: 'The namespace covers the class of blockchain.',
})
public readonly namespace: string;
@ApiProperty({
description:
'The reference identifies the blockchain within a given namespace.',
})
public readonly reference: string;
@ApiProperty({
description: 'Whether the chain is testnet or not.',
})
public readonly testnet: boolean;

constructor({
canonicalName,
chainId,
namespace,
reference,
testnet,
}: IProps) {
this.canonicalName = canonicalName;
this.chainId = chainId;
this.namespace = namespace;
this.reference = reference;
this.testnet = testnet;
}
}
1 change: 1 addition & 0 deletions src/modules/chains/dtos/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as GetChainsResponseBodyDTO } from './GetChainsResponseBodyDTO';
1 change: 1 addition & 0 deletions src/modules/chains/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dtos';
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface IProps {
token: string;
}

export default class CreateDTO {
export default class CreateOptionsDTO {
public readonly blockNumber: string;
public readonly chainId: string;
public readonly integrator: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ interface IProps {
page?: number;
}

export default class FindByPageDTO {
export default class FindByPageOptionsDTO {
public readonly chainId: string;
public readonly limit?: number;
public readonly page?: number;
Expand Down
23 changes: 23 additions & 0 deletions src/modules/fee-repository/dtos/FindByPageResultDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// types
import type { IFeeDocument } from '@app/types';

interface IProps {
data: IFeeDocument[];
limit: number;
page: number;
total: number;
}

export default class FindByPageResultDTO {
public readonly data: IFeeDocument[];
public readonly limit: number;
public readonly page: number;
public readonly total: number;

constructor({ data, limit, page, total }: IProps) {
this.data = data;
this.limit = limit;
this.page = page;
this.total = total;
}
}
5 changes: 3 additions & 2 deletions src/modules/fee-repository/dtos/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as CreateDTO } from './CreateDTO';
export { default as FindByPageDTO } from './FindByPageDTO';
export { default as CreateOptionsDTO } from './CreateOptionsDTO';
export { default as FindByPageOptionsDTO } from './FindByPageOptionsDTO';
export { default as FindByPageResultDTO } from './FindByPageResultDTO';
18 changes: 11 additions & 7 deletions src/modules/fee-repository/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import { Model } from 'mongoose';
import { FEE_PAGINATION_MAX_LIMIT } from '@app/constants';

// dtos
import { CreateDTO, FindByPageDTO } from './dtos';
import {
CreateOptionsDTO,
FindByPageOptionsDTO,
FindByPageResultDTO,
} from './dtos';

// enums
import { ProviderNameEnum } from '@app/enums';

// types
import type { IFeeDocument } from '@app/types';
import type { IFindByPageAggregateResult, IFindByPageResult } from './types';
import type { IFindByPageAggregateResult } from './types';

@Injectable()
export default class FeeRepositoryService {
Expand All @@ -21,23 +25,23 @@ export default class FeeRepositoryService {
private readonly model: Model<IFeeDocument>
) {}

public async bulkCreate(dtos: CreateDTO[]): Promise<IFeeDocument[]> {
public async bulkCreate(dtos: CreateOptionsDTO[]): Promise<IFeeDocument[]> {
return await this.model.create(dtos);
}

public async countByChainId(chainId: string): Promise<number> {
return await this.model.countDocuments({ chainId }).exec();
}

public async create(dto: CreateDTO): Promise<IFeeDocument> {
public async create(dto: CreateOptionsDTO): Promise<IFeeDocument> {
return await this.model.create(dto);
}

public async findByPage({
chainId,
limit = FEE_PAGINATION_MAX_LIMIT,
page = 1,
}: FindByPageDTO): Promise<IFindByPageResult> {
}: FindByPageOptionsDTO): Promise<FindByPageResultDTO> {
const result: IFindByPageAggregateResult[] =
await this.model.aggregate<IFindByPageAggregateResult>([
{
Expand All @@ -64,12 +68,12 @@ export default class FeeRepositoryService {
},
]);

return {
return new FindByPageResultDTO({
data: result[0].data,
limit,
page,
total: result[0].metadata[0].total,
};
});
}

public async findLatestBlockNumberForChainId(
Expand Down
11 changes: 0 additions & 11 deletions src/modules/fee-repository/types/IFindByPageResult.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/modules/fee-repository/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { default as IFindByPageAggregateResult } from './IFindByPageAggregateResult';
export { default as IFindByPageResult } from './IFindByPageResult';
4 changes: 2 additions & 2 deletions src/modules/fees-collected-event-listener/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@app/constants';

// dtos
import { CreateDTO as CreateFeeDTO } from '@app/modules/fee-repository';
import { CreateOptionsDTO } from '@app/modules/fee-repository';
import { FeesCollectedQueryEventPayloadDTO } from './dtos';

// enums
Expand Down Expand Up @@ -203,7 +203,7 @@ export default class FeeCollectdEventListenerService implements OnModuleInit {
await this.feeRepositoryService.bulkCreate(
events.map(
({ blockNumber, integrator, integratorFee, lifiFee, token }) =>
new CreateFeeDTO({
new CreateOptionsDTO({
blockNumber: String(blockNumber),
chainId,
integrator,
Expand Down
Loading

0 comments on commit 3f90eac

Please sign in to comment.