-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/metrics controller #23
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
9034051
feat(wip): metrics controller with ecosystem ep
0xnigir1 0396c89
feat: zkchain-info ep mock
0xnigir1 705376e
feat: adjust some return values on mocks
0xnigir1 40050f1
test: unit and e2e tests for metrics
0xnigir1 0ee1757
docs: add jsdocs to dtos and services
0xnigir1 fa0b6f6
test: change afterAll for afterEach
0xnigir1 08b9ca3
docs: update jsdocs
0xnigir1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from "@nestjs/common"; | ||
|
||
/** | ||
* A pipe that transforms and validates input strings to positive integers. | ||
* @implements {PipeTransform<string, number>} | ||
*/ | ||
@Injectable() | ||
export class ParsePositiveIntPipe implements PipeTransform<string, number> { | ||
/** | ||
* Transforms and validates the input value. | ||
* | ||
* @param {string} value - The input value to be transformed and validated. | ||
* @param {ArgumentMetadata} metadata - Metadata about the transformed argument. | ||
* @returns {number} The parsed positive integer. | ||
* @throws {BadRequestException} If the input is not a valid positive integer. | ||
*/ | ||
transform(value: string, metadata: ArgumentMetadata): number { | ||
const parsedValue = parseInt(value, 10); | ||
if (isNaN(parsedValue) || parsedValue < 0) { | ||
throw new BadRequestException( | ||
`Validation failed: Parameter ${metadata.data} must be a positive integer`, | ||
); | ||
} | ||
return parsedValue; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
|
||
import { MetricsController } from "./metrics.controller"; | ||
import { getEcosystemInfo, getZKChainInfo } from "./mocks/metrics.mock"; | ||
|
||
describe("MetricsController", () => { | ||
let controller: MetricsController; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [MetricsController], | ||
}).compile(); | ||
|
||
controller = module.get<MetricsController>(MetricsController); | ||
}); | ||
|
||
it("should be defined", () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
|
||
describe("getEcosystem", () => { | ||
it("should return the ecosystem information", async () => { | ||
const expectedInfo = getEcosystemInfo(); | ||
|
||
const result = await controller.getEcosystem(); | ||
|
||
expect(result).toEqual(expectedInfo); | ||
}); | ||
}); | ||
|
||
describe("getChain", () => { | ||
it("should return the chain information for the specified chain ID", async () => { | ||
const chainId = 123; | ||
const expectedInfo = getZKChainInfo(chainId); | ||
|
||
const result = await controller.getChain(chainId); | ||
|
||
expect(result).toEqual(expectedInfo); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Controller, Get, Param } from "@nestjs/common"; | ||
import { ApiResponse, ApiTags } from "@nestjs/swagger"; | ||
import { ZKChainInfo } from "@shared/dtos/dto/chain.dto"; | ||
|
||
import { ParsePositiveIntPipe } from "../common/pipes/parsePositiveInt.pipe"; | ||
import { getEcosystemInfo, getZKChainInfo } from "./mocks/metrics.mock"; | ||
|
||
@ApiTags("metrics") | ||
@Controller("metrics") | ||
/** | ||
* Controller for handling metrics related endpoints. | ||
*/ | ||
export class MetricsController { | ||
/** | ||
* Retrieves the ecosystem information. | ||
* @returns {Promise<EcosystemInfo>} The ecosystem information. | ||
*/ | ||
@Get("/ecosystem") | ||
public async getEcosystem() { | ||
return getEcosystemInfo(); | ||
} | ||
|
||
/** | ||
* Retrieves the chain information for the specified chain ID. | ||
* @param {number} chainId - The ID of the chain. | ||
* @returns {Promise<ZKChainInfo>} The chain information. | ||
*/ | ||
|
||
@ApiResponse({ status: 200, type: ZKChainInfo }) | ||
@Get("zkchain/:chainId") | ||
public async getChain(@Param("chainId", new ParsePositiveIntPipe()) chainId: number) { | ||
return getZKChainInfo(chainId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { EcosystemInfo } from "@shared/dtos"; | ||
import { ZKChainInfo } from "@shared/dtos/dto/chain.dto"; | ||
import { L2ChainInfo } from "@shared/dtos/dto/l2Metrics.dto"; | ||
import { Metadata } from "@shared/dtos/dto/metadata.dto"; | ||
|
||
export const getEcosystemInfo = () => { | ||
const mock = new EcosystemInfo({ | ||
l1Tvl: { ETH: 1000000, USDC: 500000 }, | ||
ethGasInfo: { | ||
gasPrice: 50, | ||
ethTransfer: 21000, | ||
erc20Transfer: 65000, | ||
}, | ||
zkChains: [ | ||
{ | ||
chainId: 0, | ||
chainType: "Rollup", | ||
nativeToken: "ETH", | ||
tvl: 1000000, | ||
metadata: true, | ||
rpc: true, | ||
}, | ||
{ | ||
chainId: 1, | ||
chainType: "Validium", | ||
nativeToken: "ETH", | ||
tvl: 500000, | ||
metadata: true, | ||
rpc: false, | ||
}, | ||
{ | ||
chainId: 2, | ||
chainType: "Rollup", | ||
tvl: 300000, | ||
metadata: false, | ||
rpc: true, | ||
}, | ||
{ | ||
chainId: 3, | ||
chainType: "Rollup", | ||
tvl: 10000, | ||
metadata: false, | ||
rpc: false, | ||
}, | ||
], | ||
}); | ||
return mock; | ||
}; | ||
|
||
const mockMetadata: Metadata = { | ||
iconUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/24091.png", | ||
chainName: "ZKsyncERA", | ||
publicRpcs: [ | ||
{ url: "https://mainnet.era.zksync.io", status: true }, | ||
{ url: "https://1rpc.io/zksync2-era", status: true }, | ||
{ url: "https://zksync.drpc.org", status: false }, | ||
], | ||
explorerUrl: "https://explorer.zksync.io/", | ||
launchDate: 1679626800, | ||
environment: "mainnet", | ||
nativeToken: "ETH", | ||
}; | ||
|
||
const mockL2Info: L2ChainInfo = { | ||
tps: 10000000, | ||
avgBlockTime: 12, | ||
lastBlock: 1000000, | ||
lastBlockVerified: 999999, | ||
}; | ||
|
||
export const getZKChainInfo = (chainId: number): ZKChainInfo => { | ||
const mock = new ZKChainInfo({ | ||
chainType: "Rollup", | ||
tvl: { ETH: 1000000, USDC: 500000 }, | ||
batchesInfo: { | ||
commited: 100, | ||
verified: 90, | ||
proved: 80, | ||
}, | ||
feeParams: { | ||
batchOverheadL1Gas: 50000, | ||
maxPubdataPerBatch: 120000, | ||
maxL2GasPerBatch: 10000000, | ||
priorityTxMaxPubdata: 15000, | ||
minimalL2GasPrice: 0.25, | ||
}, | ||
}); | ||
switch (chainId) { | ||
case 0: | ||
mock.metadata = mockMetadata; | ||
mock.l2ChainInfo = mockL2Info; | ||
break; | ||
case 1: | ||
mock.metadata = mockMetadata; | ||
break; | ||
case 2: | ||
mock.l2ChainInfo = mockL2Info; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
return mock; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { INestApplication } from "@nestjs/common"; | ||
import { Test, TestingModule } from "@nestjs/testing"; | ||
import request from "supertest"; | ||
|
||
import { ApiModule } from "../src/api.module"; | ||
|
||
describe("MetricsController (e2e)", () => { | ||
let app: INestApplication; | ||
|
||
beforeEach(async () => { | ||
const moduleFixture: TestingModule = await Test.createTestingModule({ | ||
imports: [ApiModule], | ||
}).compile(); | ||
|
||
app = moduleFixture.createNestApplication(); | ||
await app.init(); | ||
}); | ||
|
||
afterEach(async () => { | ||
await app.close(); | ||
}); | ||
|
||
describe("/ecosystem (GET)", () => { | ||
it("/ecosystem (GET)", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/ecosystem") | ||
.expect(200) | ||
.expect(({ body }) => { | ||
expect(body.l1Tvl).toBeDefined(); | ||
expect(body.ethGasInfo).toBeDefined(); | ||
expect(body.zkChains).toBeDefined(); | ||
expect(body.zkChains.length).toBeGreaterThan(0); | ||
}); | ||
}); | ||
}); | ||
|
||
describe("/chain/:chainId (GET)", () => { | ||
it("correct request for RPC + METADATA", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/zkchain/0") | ||
.expect(200) | ||
.expect(({ body }) => { | ||
expect(body.chainType).toBeDefined(); | ||
expect(body.tvl).toBeDefined(); | ||
expect(body.batchesInfo).toBeDefined(); | ||
expect(body.feeParams).toBeDefined(); | ||
expect(body.metadata).toBeDefined(); | ||
expect(body.l2ChainInfo).toBeDefined(); | ||
}); | ||
}); | ||
|
||
it("correct request for METADATA", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/zkchain/1") | ||
.expect(200) | ||
.expect(({ body }) => { | ||
expect(body.chainType).toBeDefined(); | ||
expect(body.tvl).toBeDefined(); | ||
expect(body.batchesInfo).toBeDefined(); | ||
expect(body.feeParams).toBeDefined(); | ||
expect(body.metadata).toBeDefined(); | ||
expect(body.l2ChainInfo).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
it("correct request for RPC", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/zkchain/2") | ||
.expect(200) | ||
.expect(({ body }) => { | ||
expect(body.chainType).toBeDefined(); | ||
expect(body.tvl).toBeDefined(); | ||
expect(body.batchesInfo).toBeDefined(); | ||
expect(body.feeParams).toBeDefined(); | ||
expect(body.metadata).toBeUndefined(); | ||
expect(body.l2ChainInfo).toBeDefined(); | ||
}); | ||
}); | ||
|
||
it("correct request for NO RPC + METADATA", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/zkchain/3") | ||
.expect(200) | ||
.expect(({ body }) => { | ||
expect(body.chainType).toBeDefined(); | ||
expect(body.tvl).toBeDefined(); | ||
expect(body.batchesInfo).toBeDefined(); | ||
expect(body.feeParams).toBeDefined(); | ||
expect(body.metadata).toBeUndefined(); | ||
expect(body.l2ChainInfo).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
it("invalid negative number for chain id", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/zkchain/-1") | ||
.expect(400) | ||
.expect(({ body }) => { | ||
expect(body.message).toEqual( | ||
"Validation failed: Parameter chainId must be a positive integer", | ||
); | ||
}); | ||
}); | ||
|
||
it("not a number for chain id", () => { | ||
return request(app.getHttpServer()) | ||
.get("/metrics/zkchain/notanumber") | ||
.expect(400) | ||
.expect(({ body }) => { | ||
expect(body.message).toEqual( | ||
"Validation failed: Parameter chainId must be a positive integer", | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
natspec missing here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on this .module you say we add the Natspec due to the consumer applied? because in general .module doesn't have anything to be documented