Skip to content
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(watcher): create a route to healthcheck /version #26

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,807 changes: 1,277 additions & 530 deletions watcher/package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion watcher/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@
"debug": "^4.3.4",
"dotenv": "^16.0.3",
"ethers": "^5.7.2",
"kcors": "^2.2.2",
"koa": "^2.15.3",
"koa-router": "^12.0.1",
"mongoose": "^6.8.3",
"redis": "^4.5.1",
"socket.io": "^4.5.4"
"socket.io": "^4.5.4",
"supertest": "^7.0.0"
},
"devDependencies": {
"eslint": "^8.31.0",
Expand Down
15 changes: 14 additions & 1 deletion watcher/src/app.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const http = require('http');
const Koa = require('koa');
const corsMiddleware = require('kcors')();
const { router } = require('./controllers/router');
const config = require('./config');
const ethereum = require('./loaders/ethereum');
const socket = require('./loaders/socket');
Expand Down Expand Up @@ -29,7 +33,15 @@ if (!wsHost) throw Error('missing wsHost');
if (!httpHost) throw Error('missing httpHost');
if (!hubAddress) throw Error('missing hubAddress');

socket.init();
const app = new Koa();
const server = http.createServer(app.callback());

socket.init(server);

app
.use(corsMiddleware)
.use(router.routes())
.use(router.allowedMethods());

const start = async ({ replayer = true, syncWatcher = true } = {}) => {
try {
Expand Down Expand Up @@ -106,4 +118,5 @@ const stop = async () => {
module.exports = {
stop,
start,
server,
};
5 changes: 5 additions & 0 deletions watcher/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const { getLogger } = require('./utils/logger');
const logger = getLogger('config');

const {
PORT,
MONGO_HOST,
REDIS_HOST,
FLAVOUR,
Expand Down Expand Up @@ -113,11 +114,14 @@ if (!chain.httpHost) {
throw Error('missing ethereum RPC endpoint ETH_RPC_HOST');
}

const serverPort = parseInt(PORT, 10) || 3000;

logger.log('chain', chain);
logger.log('flavour', flavour);
logger.log('mongo', mongo);
logger.log('redis', redis);
logger.log('runtime', runtime);
logger.log('serverPort', serverPort);

module.exports = {
abi,
Expand All @@ -126,4 +130,5 @@ module.exports = {
mongo,
redis,
runtime,
serverPort,
};
19 changes: 19 additions & 0 deletions watcher/src/controllers/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const Router = require('koa-router');
const { getVersion } = require('../services/version');
const { getLogger } = require('../utils/logger');
const logger = getLogger('controllers:router');


const router = new Router();

// version
router.get('/version', (ctx) => {
logger.log('GET /version');
const version = getVersion();
ctx.body = { ok: true, version };
});


module.exports = {
router,
};
8 changes: 7 additions & 1 deletion watcher/src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
const { start } = require('./app');
const { start, server } = require('./app');
const { serverPort } = require('./config');
const { getLogger, APP_NAMESPACE } = require('./utils/logger');
const logger = getLogger(APP_NAMESPACE);

server.listen(serverPort);
logger.log(`Server listening on port ${serverPort}`);

start();
7 changes: 2 additions & 5 deletions watcher/src/loaders/socket.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const http = require('http');
const socketIo = require('socket.io');
const { createClient } = require('redis');
const { createAdapter } = require('@socket.io/redis-adapter');
Expand All @@ -8,16 +7,14 @@ const { throwIfMissing, errorHandler } = require('../utils/error');

const logger = getLogger('socket');

const server = http.createServer();

let ws = null;

const getWs = () => {
if (ws === null) throw new Error('socket not initialized');
return ws;
};

const init = async () => {
const init = async (server = throwIfMissing()) => {
try {
logger.log('init socket');
const redisConfig = config.redis;
Expand All @@ -31,7 +28,7 @@ const init = async () => {
subClient.on('end', () => logger.log('subClient end'));
await Promise.all[(pubClient.connect(), subClient.connect())];
const redisAdapter = createAdapter(pubClient, subClient);
ws = socketIo(server);
Yagora marked this conversation as resolved.
Show resolved Hide resolved
ws = socketIo(server, { path: '/ws', cors: { origin: '*' } });
ws.adapter(redisAdapter);
logger.log('socket initialized');
} catch (error) {
Expand Down
7 changes: 7 additions & 0 deletions watcher/src/services/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { version } = require('../../package.json');

const getVersion = () => version;

module.exports = {
getVersion,
};
19 changes: 19 additions & 0 deletions watcher/src/utils/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ class Web3ProviderError extends InternalError {
}
}

class OperationalError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}

class ObjectNotFoundError extends OperationalError {
constructor(message, originalError) {
super(message);
this.name = this.constructor.name;
this.originalError = originalError;
if (originalError && typeof originalError === 'object') {
Object.assign(this, getPropsToCopy(originalError));
}
}
}

const wrapEthCall = async (promise) => {
try {
return await promise;
Expand Down Expand Up @@ -75,6 +93,7 @@ const errorHandler = async (error, context) => {
};

module.exports = {
ObjectNotFoundError,
InternalError,
Web3ProviderError,
wrapEthCall,
Expand Down
3 changes: 3 additions & 0 deletions watcher/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ const setCheckpointToLastBlock = async (dbName) => {
);
};

const parseResult = (res) => ({ ...res, data: JSON.parse(res.text) });

module.exports = {
addApporders,
addDatasetorders,
Expand All @@ -354,6 +356,7 @@ module.exports = {
getMatchableRequestorder,
transferResourceERC721,
timestampRegex,
parseResult,
bytes32Regex,
addressRegex,
APPORDERS_COLLECTION,
Expand Down
30 changes: 29 additions & 1 deletion watcher/test/watcher-common.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const supertest = require('supertest');
const ethers = require('ethers');
const { utils, IExec } = require('iexec');
const socket = require('../src/loaders/socket');
// jest spies
const socketEmitSpy = jest.spyOn(socket, 'emit');
const { start, stop } = require('../src/app');
const { start, stop, server } = require('../src/app');
const { replayPastOnly } = require('../src/controllers/replayer');
const { chain } = require('../src/config');
const { sleep } = require('../src/utils/utils');
Expand All @@ -21,6 +22,7 @@ const {
getMatchableRequestorder,
transferResourceERC721,
timestampRegex,
parseResult,
bytes32Regex,
APPORDERS_COLLECTION,
DATASETORDERS_COLLECTION,
Expand All @@ -36,6 +38,8 @@ const { init: ethereumInit } = require('../src/loaders/ethereum');
jest.setTimeout(120000);

const PROCESS_TRIGGERED_EVENT_TIMEOUT = 1000;
const NOT_FOUND_ERROR_STATUS = 404;
const OK_STATUS = 200;

let chainId;
const chainUrl = chain.httpHost;
Expand All @@ -46,6 +50,8 @@ const rpc = new ethers.providers.JsonRpcProvider(chainUrl);
const wallet = new ethers.Wallet(PRIVATE_KEY, rpc);

let iexec;
let serverHttp;
let request;
const signer = utils.getSignerFromPrivateKey(chainUrl, PRIVATE_KEY);

beforeAll(async () => {
Expand All @@ -67,6 +73,28 @@ beforeAll(async () => {
await iexec.wallet.getAddress(),
);
await iexec.account.withdraw(stake);
serverHttp = server.listen();
request = supertest(serverHttp);
});

afterAll(async () => {
serverHttp.close();
});

describe('API', () => {
describe('Common', () => {
test('GET /version', async () => {
const { data, status } = await request.get('/version').then(parseResult);
expect(status).toBe(OK_STATUS);
expect(data.ok).toBe(true);
expect(data.version).toBeDefined();
});
test('GET /foo (not found)', async () => {
const res = await request.get('/foo');
expect(res.status).toBe(NOT_FOUND_ERROR_STATUS);
expect(res.text).toBe("Not Found");
});
});
});

describe('Watcher', () => {
Expand Down