Skip to content

Commit

Permalink
feat(status-api): Overall Status endpoint (#1338)
Browse files Browse the repository at this point in the history
* /overall/status controller

Signed-off-by: Suraj Auwal <[email protected]>

* Minor changes to README.md

Signed-off-by: Suraj Auwal <[email protected]>

* Updated Imports

Signed-off-by: Suraj Auwal <[email protected]>

* Changed endpoint to stay consistent.

Signed-off-by: Suraj Auwal <[email protected]>

* Updated tests

Signed-off-by: Suraj Auwal <[email protected]>

* Refactored AgregateStatusController

Signed-off-by: Suraj Auwal <[email protected]>

* Refactored AgregateStatusController

Signed-off-by: Suraj Auwal <[email protected]>

* Removed redundant code

Signed-off-by: Suraj Auwal <[email protected]>

* Test fixtures and documentation

Signed-off-by: Suraj Auwal <[email protected]>

* Renamed files

Signed-off-by: Suraj Auwal <[email protected]>

* Updated test fixtures

Signed-off-by: Suraj Auwal <[email protected]>

* Removed any in test files

Signed-off-by: Suraj Auwal <[email protected]>

* Controller should return outage when  ocean is not ready.

Signed-off-by: Suraj Auwal <[email protected]>

* Added comments for better clarity

Signed-off-by: Suraj Auwal <[email protected]>

* Updated as per Eli's comment

Co-authored-by: Joel-David Wong <[email protected]>
  • Loading branch information
siradji and joeldavidw authored Apr 19, 2022
1 parent 54a37cb commit 6d2148f
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 8 deletions.
6 changes: 3 additions & 3 deletions apps/status-api/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DeFiChain Status API

DeFiChain Status API, providing the statuses of different DeFiChain services
DeFiChain Status API, providing the statuses of different DeFiChain services.


## Motivation
Expand All @@ -11,10 +11,10 @@ To decouple the DeFiChain products from the status page, the approach of having
### `/blockchain/status`
>https://github.com/DeFiCh/jellyfish/issues/1271
To provide the status of the blockchain based on the block creation time interval
To provide the status of the blockchain based on the block creation time interval.

### `/overall/status`
>https://github.com/DeFiCh/jellyfish/issues/1274
To provide the aggregated status of all services required by Light Wallet & Scan (Blockchain & Ocean)
To provide the aggregated status of all services required by Light Wallet & Scan (Blockchain & Ocean).

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StatusApiTesting } from '../../testing/StatusApiTesting'
import { ApiPagedResponse, WhaleApiClient } from '@defichain/whale-api-client'
import { Block } from '@defichain/whale-api-client/dist/api/Blocks'

describe('BlockchainController - Status test', () => {
const apiTesting = StatusApiTesting.create()
Expand All @@ -12,6 +13,7 @@ describe('BlockchainController - Status test', () => {
})

it('/blockchain - should get operational', async () => {
// Set blocktime to be less than 30 minutes
jest
.spyOn(apiTesting.app.get(WhaleApiClient).blocks, 'list')
.mockReturnValueOnce(getBlockResponseWithPresetTime(25))
Expand All @@ -28,6 +30,7 @@ describe('BlockchainController - Status test', () => {
})

it('/blockchain - should get degraded', async () => {
// Set blocktime to be 30-45 minutes
jest
.spyOn(apiTesting.app.get(WhaleApiClient).blocks, 'list')
.mockReturnValueOnce(getBlockResponseWithPresetTime(36))
Expand All @@ -44,6 +47,7 @@ describe('BlockchainController - Status test', () => {
})

it('/blockchain - should get outage', async () => {
// Set blocktime to be greater than 45 minutes
jest
.spyOn(apiTesting.app.get(WhaleApiClient).blocks, 'list')
.mockReturnValueOnce(getBlockResponseWithPresetTime(46))
Expand All @@ -60,7 +64,7 @@ describe('BlockchainController - Status test', () => {
})
})

async function getBlockResponseWithPresetTime (minutesDiff: number): Promise<ApiPagedResponse<any>> {
async function getBlockResponseWithPresetTime (minutesDiff: number): Promise<ApiPagedResponse<Block>> {
const blockTime = Date.now() / 1000 - (minutesDiff * 60)

return new ApiPagedResponse({
Expand Down
137 changes: 137 additions & 0 deletions apps/status-api/__test__/controllers/OverallStatusController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { StatusApiTesting } from '../../testing/StatusApiTesting'
import { ApiPagedResponse, WhaleApiClient } from '@defichain/whale-api-client'
import { WhaleApiProbeIndicator } from '../../src/modules/WhaleApiModule'
import { HealthIndicatorResult, HealthIndicatorStatus } from '@nestjs/terminus'
import { Block } from '@defichain/whale-api-client/dist/api/Blocks'

describe('AggregateController - Status test', () => {
const apiTesting = StatusApiTesting.create()
beforeAll(async () => {
await apiTesting.start()
})

afterAll(async () => {
await apiTesting.stop()
})

it('/overall - should get operational when Ocean and blockchain are up', async () => {
jest
.spyOn(apiTesting.app.get(WhaleApiProbeIndicator), 'liveness')
.mockReturnValueOnce(getWhaleStatus('up'))
jest
.spyOn(apiTesting.app.get(WhaleApiProbeIndicator), 'readiness')
.mockReturnValueOnce(getWhaleStatus('up'))
jest
.spyOn(apiTesting.app.get(WhaleApiClient).blocks, 'list')
.mockReturnValueOnce(getBlockResponseWithPresetTime(25))

const res = await apiTesting.app.inject({
method: 'GET',
url: '/overall'
})

expect(res.statusCode).toStrictEqual(200)
expect(res.json()).toStrictEqual({
status: 'operational'
})
})

it('/overall - should get outage when blockchain is down', async () => {
jest
.spyOn(apiTesting.app.get(WhaleApiClient).blocks, 'list')
.mockReturnValueOnce(getBlockResponseWithPresetTime(46))

const res = await apiTesting.app.inject({
method: 'GET',
url: '/overall'
})

expect(res.statusCode).toStrictEqual(200)
expect(res.json()).toStrictEqual({
status: 'outage'
})
})

it('/overall - should get degraded when blockchain degraded', async () => {
jest
.spyOn(apiTesting.app.get(WhaleApiClient).blocks, 'list')
.mockReturnValueOnce(getBlockResponseWithPresetTime(36))

const res = await apiTesting.app.inject({
method: 'GET',
url: '/overall'
})

expect(res.statusCode).toStrictEqual(200)
expect(res.json()).toStrictEqual({
status: 'degraded'
})
})

it('/overall - should get outage when Ocean down', async () => {
jest
.spyOn(apiTesting.app.get(WhaleApiProbeIndicator), 'liveness')
.mockReturnValueOnce(getWhaleStatus('down'))

const res = await apiTesting.app.inject({
method: 'GET',
url: '/overall'
})

expect(res.statusCode).toStrictEqual(200)
expect(res.json()).toStrictEqual({
status: 'outage'
})
})

it('/overall - should get outage when Ocean is not ready ', async () => {
jest
.spyOn(apiTesting.app.get(WhaleApiProbeIndicator), 'readiness')
.mockReturnValueOnce(getWhaleStatus('down'))

const res = await apiTesting.app.inject({
method: 'GET',
url: '/overall'
})

expect(res.statusCode).toStrictEqual(200)
expect(res.json()).toStrictEqual({
status: 'outage'
})
})
})

async function getWhaleStatus (status: HealthIndicatorStatus): Promise<HealthIndicatorResult> {
return {
whale: {
status
}
}
}

async function getBlockResponseWithPresetTime (minutesDiff: number): Promise<ApiPagedResponse<Block>> {
const blockTime = Date.now() / 1000 - (minutesDiff * 60)

return new ApiPagedResponse({
data: [{
time: blockTime,
id: '',
hash: '',
previousHash: '',
height: 0,
version: 0,
medianTime: 0,
transactionCount: 0,
difficulty: 0,
masternode: '',
minter: '',
minterBlockCount: 0,
reward: '',
stakeModifier: '',
merkleroot: '',
size: 0,
sizeStripped: 0,
weight: 0
}]
}, 'GET', 'blocks')
}
6 changes: 3 additions & 3 deletions apps/status-api/src/controllers/BlockchainStatusController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class BlockchainStatusController {
export type BlockchainStatus = 'outage' | 'degraded' | 'operational'

export const StatusToThresholdInMs: Record<BlockchainStatus, number> = {
outage: 45 * 60 * 1000,
degraded: 30 * 60 * 1000,
operational: 0
outage: 45 * 60 * 1000, // > 45 minutes
degraded: 30 * 60 * 1000, // 30-45 minutes
operational: 0 // < 30 minutes
}
40 changes: 40 additions & 0 deletions apps/status-api/src/controllers/OverallStatusController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Controller, Get } from '@nestjs/common'
import { WhaleApiClient } from '@defichain/whale-api-client'
import { BlockchainStatus, BlockchainStatusController } from './BlockchainStatusController'
import { WhaleApiProbeIndicator } from '../modules/WhaleApiModule'

@Controller('overall')
export class OverallStatusController {
constructor (
private readonly client: WhaleApiClient,
private readonly probe: WhaleApiProbeIndicator,
private readonly blockchainStatusController: BlockchainStatusController
) {
}

/**
* To provide overall status for Ocean and blockchain services. Returns
* 'operational' when both services are up,
* 'outage' when either service is down and
* 'degraded' when blockchain service is degraded.
*
* @return {Promise<{ status: BlockchainStatus }>}
*/

@Get()
async getOverallStatus (): Promise<{ status: BlockchainStatus }> {
if (
(await this.probe.liveness()).whale.status === 'down' ||
(await this.probe.readiness()).whale.status === 'down'
) {
return {
status: 'outage'
}
}

const blockchain = await this.blockchainStatusController.getBlockChainStatus()
return {
status: blockchain.status
}
}
}
5 changes: 4 additions & 1 deletion apps/status-api/src/modules/ControllerModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ActuatorController } from '@defichain-apps/libs/actuator'
import { BlockchainStatusController } from '../controllers/BlockchainStatusController'
import { WhaleApiClient } from '@defichain/whale-api-client'
import { ConfigService } from '@nestjs/config'
import { OverallStatusController } from '../controllers/OverallStatusController'

/**
* Exposed ApiModule for public interfacing
Expand All @@ -13,9 +14,11 @@ import { ConfigService } from '@nestjs/config'
],
controllers: [
BlockchainStatusController,
ActuatorController
ActuatorController,
OverallStatusController
],
providers: [
BlockchainStatusController,
{
provide: WhaleApiClient,
useFactory: (configService: ConfigService): WhaleApiClient => {
Expand Down
1 change: 1 addition & 0 deletions apps/status-api/src/modules/WhaleApiModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class WhaleApiProbeIndicator extends ProbeIndicator {
}
],
exports: [
WhaleApiProbeIndicator
]
})
export class WhaleApiModule {
Expand Down

0 comments on commit 6d2148f

Please sign in to comment.