-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ocean-api): initial commit for
apps/ocean-api
(#926)
* initial package setup * add Actuator, Blockchain and Controller modules * fix README.md * fix test and build * fix error casting
- Loading branch information
Showing
21 changed files
with
4,557 additions
and
71 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,42 @@ | ||
# DeFiChain Ocean API | ||
|
||
DeFiChain Ocean API, next^2 generation API for building scalable Native DeFi Apps. | ||
|
||
## Motivation | ||
|
||
> https://github.com/DeFiCh/jellyfish/issues/580 | ||
As part of [#580](https://github.com/DeFiCh/jellyfish/issues/580) consolidation efforts. We had multiple projects that | ||
were extensions of the jellyfish project. The separated projects allowed us to move quickly initially but proves to be a | ||
bottleneck when it comes to development. | ||
|
||
By including Ocean API development with jellyfish, it creates a better synergy of DeFiChain open source development | ||
across concerns. Singular versioning, source of truth, documentation of entirety of defichain | ||
via [jellyfish.defichain.com](https://jellyfish.defichain.com). | ||
|
||
## `/apps/ocean-api` | ||
|
||
The server for ocean-api, build with @nestjs it uses aspect-oriented programming methodology to allow the modular design | ||
of `ocean-api`. Featuring 2 main directories `/controllers` and `/modules`. | ||
|
||
## Related Packages | ||
|
||
### `/packages/ocean-api-client` | ||
|
||
> Provides the protocol core for communicating between client and server. Within `ocean-api-client`, it contains a shared response and exception structure. | ||
The official JS client for ocean-api. As the development of ocean-api client and server are closely intertwined, this | ||
allows the project to move iteratively together. With them packaged together within the same repo, the server and client | ||
can be released together. This allows us to be the consumer of our own client implementation. Testing each server | ||
endpoint directly with `ocean-api-client`, dogfooding at the maximum. | ||
|
||
### `/packages/playground` | ||
|
||
> This package is not published, for internal use within `@defichain-apps/ocean-api` only. | ||
`@defichain/playground` is a specialized testing blockchain isolated from MainNet for testing DeFi applications. Assets | ||
are not real, they can be minted by anyone. Blocks are configured to generate every 3 seconds, the chain can reset | ||
anytime. | ||
|
||
A bot-like design centers the playground as a mechanism that allows bootstrapping with an interval cycle. This allows | ||
the developer to mock any behaviors they want with a simulated testing blockchain. |
151 changes: 151 additions & 0 deletions
151
apps/ocean-api/__tests__/controllers/ActuatorController.test.ts
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,151 @@ | ||
import { OceanApiTesting } from '../../testing/OceanApiTesting' | ||
import { TestingGroup } from '@defichain/jellyfish-testing' | ||
|
||
describe('no peers', () => { | ||
const apiTesting = OceanApiTesting.create() | ||
|
||
beforeAll(async () => { | ||
await apiTesting.start() | ||
}) | ||
|
||
afterAll(async () => { | ||
await apiTesting.stop() | ||
}) | ||
|
||
it('/_actuator/probes/liveness healthy', async () => { | ||
const res = await apiTesting.app.inject({ | ||
method: 'GET', | ||
url: '/_actuator/probes/liveness' | ||
}) | ||
|
||
expect(res.json()).toStrictEqual({ | ||
data: { | ||
details: { | ||
blockchain: { | ||
status: 'up' | ||
} | ||
}, | ||
error: {}, | ||
info: { | ||
blockchain: { | ||
status: 'up' | ||
} | ||
}, | ||
status: 'ok' | ||
} | ||
}) | ||
expect(res.statusCode).toStrictEqual(200) | ||
}) | ||
|
||
it('/_actuator/probes/readiness unhealthy', async () => { | ||
const res = await apiTesting.app.inject({ | ||
method: 'GET', | ||
url: '/_actuator/probes/readiness' | ||
}) | ||
|
||
expect(res.json()).toStrictEqual({ | ||
data: { | ||
details: { | ||
blockchain: { | ||
blocks: expect.any(Number), | ||
headers: expect.any(Number), | ||
initialBlockDownload: expect.any(Boolean), | ||
peers: 0, | ||
status: 'down' | ||
} | ||
}, | ||
error: { | ||
blockchain: { | ||
blocks: expect.any(Number), | ||
headers: expect.any(Number), | ||
initialBlockDownload: expect.any(Boolean), | ||
peers: 0, | ||
status: 'down' | ||
} | ||
}, | ||
info: {}, | ||
status: 'error' | ||
}, | ||
error: { | ||
at: expect.any(Number), | ||
code: 503, | ||
message: 'Service Unavailable Exception', | ||
type: 'ServiceUnavailable', | ||
url: '/_actuator/probes/readiness' | ||
} | ||
}) | ||
expect(res.statusCode).toStrictEqual(503) | ||
}) | ||
}) | ||
|
||
describe('with peers', () => { | ||
const apiTesting = OceanApiTesting.create(TestingGroup.create(2)) | ||
|
||
beforeAll(async () => { | ||
await apiTesting.start() | ||
await apiTesting.container.waitForWalletCoinbaseMaturity() | ||
}) | ||
|
||
afterAll(async () => { | ||
await apiTesting.stop() | ||
}) | ||
|
||
it('/_actuator/probes/liveness healthy', async () => { | ||
const res = await apiTesting.app.inject({ | ||
method: 'GET', | ||
url: '/_actuator/probes/liveness' | ||
}) | ||
|
||
expect(res.json()).toStrictEqual({ | ||
data: { | ||
details: { | ||
blockchain: { | ||
status: 'up' | ||
} | ||
}, | ||
error: {}, | ||
info: { | ||
blockchain: { | ||
status: 'up' | ||
} | ||
}, | ||
status: 'ok' | ||
} | ||
}) | ||
expect(res.statusCode).toStrictEqual(200) | ||
}) | ||
|
||
it('/_actuator/probes/readiness healthy', async () => { | ||
const res = await apiTesting.app.inject({ | ||
method: 'GET', | ||
url: '/_actuator/probes/readiness' | ||
}) | ||
|
||
expect(res.json()).toStrictEqual({ | ||
data: { | ||
details: { | ||
blockchain: { | ||
blocks: expect.any(Number), | ||
headers: expect.any(Number), | ||
initialBlockDownload: false, | ||
peers: 1, | ||
status: 'up' | ||
} | ||
|
||
}, | ||
error: {}, | ||
info: { | ||
blockchain: { | ||
blocks: expect.any(Number), | ||
headers: expect.any(Number), | ||
initialBlockDownload: false, | ||
peers: 1, | ||
status: 'up' | ||
} | ||
}, | ||
status: 'ok' | ||
} | ||
}) | ||
expect(res.statusCode).toStrictEqual(200) | ||
}) | ||
}) |
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 @@ | ||
{ | ||
"private": false, | ||
"name": "@defichain-apps/ocean-api", | ||
"version": "0.0.0", | ||
"description": "DeFiChain Jellyfish Ecosystem", | ||
"repository": "DeFiCh/jellyfish", | ||
"bugs": "https://github.com/DeFiCh/jellyfish/issues", | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"build": "tsc -b tsconfig.build.json" | ||
}, | ||
"dependencies": { | ||
"@defichain/ocean-api-client": "0.0.0", | ||
"@defichain/jellyfish-api-core": "0.0.0", | ||
"@defichain/jellyfish-api-jsonrpc": "0.0.0", | ||
"@defichain/playground": "0.0.0", | ||
"@nestjs/common": "^8.2.4", | ||
"@nestjs/config": "^1.1.5", | ||
"@nestjs/core": "^8.2.4", | ||
"@nestjs/platform-fastify": "^8.2.4", | ||
"@nestjs/schedule": "^1.0.2", | ||
"@nestjs/terminus": "^8.0.3", | ||
"cache-manager": "^3.6.0", | ||
"class-transformer": "^0.5.1", | ||
"class-validator": "^0.13.2", | ||
"joi": "^17.5.0" | ||
}, | ||
"devDependencies": { | ||
"@defichain/ocean-api-client": "0.0.0", | ||
"@nestjs/cli": "^8.1.6", | ||
"@nestjs/schematics": "^8.0.5", | ||
"@nestjs/testing": "^8.2.4", | ||
"@types/cache-manager": "^3.4.2", | ||
"@types/cron": "^1.7.3" | ||
} | ||
} |
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,37 @@ | ||
import { Controller, Get } from '@nestjs/common' | ||
import { HealthCheck, HealthCheckResult, HealthCheckService } from '@nestjs/terminus' | ||
import { ActuatorProbes } from '../modules/ActuatorModule' | ||
|
||
@Controller('/_actuator') | ||
export class ActuatorController { | ||
constructor ( | ||
private readonly probes: ActuatorProbes, | ||
private readonly health: HealthCheckService) { | ||
} | ||
|
||
/** | ||
* Indicates whether the service is running. | ||
* If the liveness probe fails, the service should be killed and subjected to a restart policy. | ||
* @see https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#when-should-you-use-a-liveness-probe | ||
*/ | ||
@Get('/probes/liveness') | ||
@HealthCheck() | ||
async liveness (): Promise<HealthCheckResult> { | ||
return await this.health.check(this.probes.map(probe => { | ||
return async () => await probe.liveness() | ||
})) | ||
} | ||
|
||
/** | ||
* Indicates whether the service is ready to respond to requests. | ||
* If the readiness probe fails, the endpoints are not ready to receive and respond to any request. | ||
* @see https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#when-should-you-use-a-readiness-probe | ||
*/ | ||
@Get('/probes/readiness') | ||
@HealthCheck() | ||
async readiness (): Promise<HealthCheckResult> { | ||
return await this.health.check(this.probes.map(probe => { | ||
return async () => await probe.readiness() | ||
})) | ||
} | ||
} |
Oops, something went wrong.