diff --git a/docs/Ocean Node.postman_collection.json b/docs/Ocean Node.postman_collection.json index b9775715d..c50f8b9d9 100644 --- a/docs/Ocean Node.postman_collection.json +++ b/docs/Ocean Node.postman_collection.json @@ -85,7 +85,7 @@ "" ] }, - "description": "Returns node identity and the list of available service endpoints (nodeId, chainIds, providerAddress, nodePublicKey, serviceEndpoints, software, version)." + "description": "Returns node identity (nodeId, chainIds, providerAddress, nodePublicKey, software, version)." } } ] diff --git a/src/@types/express.ts b/src/@types/express.ts deleted file mode 100644 index 06c5bfd61..000000000 --- a/src/@types/express.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface RouteOptions { - path: string - method: string -} diff --git a/src/components/httpRoutes/index.ts b/src/components/httpRoutes/index.ts index 75e06cc02..9b0893a93 100644 --- a/src/components/httpRoutes/index.ts +++ b/src/components/httpRoutes/index.ts @@ -10,7 +10,6 @@ import { fileInfoRoute } from './fileInfo.js' import { computeRoutes } from './compute.js' import { queueRoutes } from './queue.js' import { jobsRoutes } from './jobs.js' -import { addMapping, allRoutesMapping, findPathName } from './routeUtils.js' import { PolicyServerPassthroughRoute } from './policyServer.js' import { authRoutes } from './auth.js' import { adminConfigRoutes } from './adminConfig.js' @@ -71,20 +70,3 @@ httpRoutes.use(accessListRoutes) // escrow events routes // /api/services/escrow/events httpRoutes.use(escrowRoutes) - -export function getAllServiceEndpoints() { - httpRoutes.stack.forEach(addMapping.bind(null, [])) - const data: any = {} - const keys = allRoutesMapping.keys() - for (const key of keys) { - const pathData = allRoutesMapping.get(key) - const name = findPathName(pathData[0], pathData[1]) - if (name) { - data[name] = pathData - } else { - // use the key - data[key] = pathData - } - } - return data -} diff --git a/src/components/httpRoutes/jobs.ts b/src/components/httpRoutes/jobs.ts index 03dc13609..4965bb73f 100644 --- a/src/components/httpRoutes/jobs.ts +++ b/src/components/httpRoutes/jobs.ts @@ -5,11 +5,11 @@ import { SERVICES_API_BASE_PATH } from '../../utils/constants.js' export const jobsRoutes = express.Router() -jobsRoutes.get(`${SERVICES_API_BASE_PATH}/jobs/:job`, (req, res) => { +jobsRoutes.get(`${SERVICES_API_BASE_PATH}/jobs/:jobId`, (req, res) => { try { const indexer: OceanIndexer = req.oceanNode.getIndexer() if (indexer) { - const jobs = indexer.getJobsPool((req.params.job as string) || null) + const jobs = indexer.getJobsPool((req.params.jobId as string) || null) res.header('Content-Type', 'application/json') res.status(200).send(JSON.stringify({ jobs })) } else { diff --git a/src/components/httpRoutes/rootEndpoint.ts b/src/components/httpRoutes/rootEndpoint.ts index fe4fd05bc..05bf69189 100644 --- a/src/components/httpRoutes/rootEndpoint.ts +++ b/src/components/httpRoutes/rootEndpoint.ts @@ -1,6 +1,5 @@ import express from 'express' import { HTTP_LOGGER } from '../../utils/logging/common.js' -import { getAllServiceEndpoints } from './index.js' export const rootEndpointRoutes = express.Router() rootEndpointRoutes.get('/', (req, res) => { @@ -14,7 +13,6 @@ rootEndpointRoutes.get('/', (req, res) => { chainIds: config.supportedNetworks ? Object.keys(config.supportedNetworks) : [], providerAddress: keyManager.getEthAddress(), nodePublicKey: keyManager.getPublicKey(), - serviceEndpoints: getAllServiceEndpoints(), software: 'Ocean-Node', version: '0.0.1' }) diff --git a/src/components/httpRoutes/routeUtils.ts b/src/components/httpRoutes/routeUtils.ts deleted file mode 100644 index 0445fd027..000000000 --- a/src/components/httpRoutes/routeUtils.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { AQUARIUS_API_BASE_PATH } from './aquarius.js' -import { SERVICES_API_BASE_PATH } from '../../utils/index.js' -import { RouteOptions } from '../../@types/express.js' -// express does not support 'names' or 'descriptions' for routes -// only a path and a method, so if we want a custom name/description (the JSON field), we need to create a mapping of names -// if the name for a path/API is available we use it, otherwise we supply a default one extracted from the path itself -// this way, we always have dynamic routes, even if a route name is not supplied explicitly - -// NOTE that none of the bellow is required/mandatory, its just helpful to have more meaningful/pretty names on the response -// we could even use the string to provide a small description instead of just a name/word, for example: - -// routesNames.set('computeStart - API that starts a C2D job', { -// path: `${SERVICES_API_BASE_PATH}/compute`, -// method: 'post' -// }) - -// would return: -// {'computeStart - API that starts a C2D job': ['POST','/api/services/compute']} -// -// AND/OR we can also extend RouteOptions in the future and add more fields like a short API description for instance: -// { path, method, description'}: -// Example: -// {'computeStart': ['POST','/api/services/compute','This API allows to start a C2D job]} -// - -// C2D -export const routesNames: Map = new Map() -export const allRoutesMapping = new Map() -// these are normalized names for our routes, not mandatory for having dynamic routes, but can add context/detail -routesNames.set('computeEnvironments', { - path: `${SERVICES_API_BASE_PATH}/computeEnvironments`, - method: 'get' -}) -routesNames.set('computeResult', { - path: `${SERVICES_API_BASE_PATH}/computeResult`, - method: 'get' -}) - -routesNames.set('initializeCompute', { - path: `${SERVICES_API_BASE_PATH}/initializeCompute`, - method: 'post' -}) - -routesNames.set('computeStart', { - path: `${SERVICES_API_BASE_PATH}/compute`, - method: 'post' -}) - -routesNames.set('freeCompute', { - path: `${SERVICES_API_BASE_PATH}/freeCompute`, - method: 'post' -}) - -routesNames.set('computeStreamableLogs', { - path: `${SERVICES_API_BASE_PATH}/computeStreamableLogs`, - method: 'GET' -}) - -routesNames.set('computeStatus', { - path: `${SERVICES_API_BASE_PATH}/compute`, - method: 'get' -}) - -routesNames.set('computeDelete', { - path: `${SERVICES_API_BASE_PATH}/compute`, - method: 'delete' -}) - -routesNames.set('computeStop', { - path: `${SERVICES_API_BASE_PATH}/compute`, - method: 'put' -}) - -// assets / ddo -routesNames.set('getDDO', { - path: `${AQUARIUS_API_BASE_PATH}/assets/ddo/:did/:force?`, - method: 'get' -}) - -routesNames.set('getDDOMetadata', { - path: `${AQUARIUS_API_BASE_PATH}/assets/metadata/:did/:force?`, - method: 'get' -}) - -routesNames.set('ddoMetadataQuery', { - path: `${AQUARIUS_API_BASE_PATH}/assets/metadata/query`, - method: 'post' -}) - -routesNames.set('getDDOState', { - path: `${AQUARIUS_API_BASE_PATH}/state/ddo`, - method: 'get' -}) - -routesNames.set('validateDDO', { - path: `${AQUARIUS_API_BASE_PATH}/assets/ddo/validate`, - method: 'post' -}) -// direct commands (http + p2p) -routesNames.set('directCommand', { - path: '/directCommand', - method: 'post' -}) - -// fileInfo -routesNames.set('fileInfo', { - path: `${SERVICES_API_BASE_PATH}/fileInfo`, - method: 'post' -}) -// p2p -routesNames.set('getOceanPeers', { - path: '/getOceanPeers', - method: 'get' -}) - -routesNames.set('getP2PPeers', { - path: '/getP2PPeers', - method: 'get' -}) - -routesNames.set('getP2PPeer', { - path: '/getP2PPeer', - method: 'get' -}) -// p2p / did -routesNames.set('advertiseDid', { - path: '/advertiseDid', - method: 'post' -}) - -routesNames.set('getProvidersForDid', { - path: '/getProvidersForDid', - method: 'get' -}) - -// logs -routesNames.set('logs', { - path: '/logs', - method: 'post' -}) - -routesNames.set('log', { - path: '/log/:id', - method: 'post' -}) -// jobs -routesNames.set('jobs', { - path: `${SERVICES_API_BASE_PATH}/jobs/:job`, - method: 'get' -}) -// provider -routesNames.set('download', { - path: `${SERVICES_API_BASE_PATH}/download`, - method: 'get' -}) - -routesNames.set('encrypt', { - path: `${SERVICES_API_BASE_PATH}/encrypt`, - method: 'post' -}) - -routesNames.set('decrypt', { - path: `${SERVICES_API_BASE_PATH}/decrypt`, - method: 'post' -}) - -routesNames.set('encryptFile', { - path: `${SERVICES_API_BASE_PATH}/encryptFile`, - method: 'post' -}) - -routesNames.set('initialize', { - path: `${SERVICES_API_BASE_PATH}/initialize`, - method: 'get' -}) - -routesNames.set('nonce', { - path: `${SERVICES_API_BASE_PATH}/nonce`, - method: 'get' -}) - -routesNames.set('indexQueue', { - path: `${SERVICES_API_BASE_PATH}/indexQueue`, - method: 'get' -}) - -routesNames.set('PolicyServerPassthrough', { - path: `${SERVICES_API_BASE_PATH}/PolicyServerPassthrough`, - method: 'post' -}) - -routesNames.set('initializePSVerification', { - path: `${SERVICES_API_BASE_PATH}/initializePSVerification`, - method: 'post' -}) - -routesNames.set('generateAuthToken', { - path: `${SERVICES_API_BASE_PATH}/auth/token`, - method: 'post' -}) - -routesNames.set('invalidateAuthToken', { - path: `${SERVICES_API_BASE_PATH}/auth/token/invalidate`, - method: 'post' -}) - -export function addMapping(path: any, layer: any) { - if (layer.route) { - layer.route.stack.forEach(addMapping.bind(null, path.concat(split(layer.route.path)))) - } else if (layer.name === 'router' && layer.handle.stack) { - layer.handle.stack.forEach(addMapping.bind(null, path.concat(split(layer.regexp)))) - } else if (layer.method) { - const method = layer.method.toUpperCase() - const pathName = '/' + path.concat(split(layer.regexp)).filter(Boolean).join('/') - // skip the root path - if (pathName.length > 1 && pathName !== '/') { - if (allRoutesMapping.has(pathName)) { - const existingData = allRoutesMapping.get(pathName) - if (existingData[0] !== method) { - // add with a new name - const defaultName = pathName + '_' + method - allRoutesMapping.set(defaultName, [method, pathName]) - } - } else { - allRoutesMapping.set(pathName, [method, pathName]) - } - } - } -} - -function split(thing: any) { - if (typeof thing === 'string') { - return thing.split('/') - } else if (thing.fast_slash) { - return '' - } else { - const match = thing - .toString() - .replace('\\/?', '') - .replace('(?=\\/|$)', '$') - .match(/^\/\^((?:\\[.*+?^${}()|[\]\\/]|[^.*+?^${}()|[\]\\/])*)\$\//) - return match - ? match[1].replace(/\\(.)/g, '$1').split('/') - : '' - } -} - -/** - * - * @param method http method - * @param path path to find - * @returns path name or null - */ -export function findPathName(method: string, path: string): string | null { - const entries = routesNames.entries() - for (const entry of entries) { - const data: RouteOptions = entry[1] - if (data.path === path && data.method.toUpperCase() === method) { - return entry[0] - } - } - - return null -} diff --git a/src/test/performance/util.js b/src/test/performance/util.js index ecc3f8dee..8a5092dd3 100644 --- a/src/test/performance/util.js +++ b/src/test/performance/util.js @@ -6,7 +6,7 @@ import exec from 'k6/execution' // LIST OF TESTS TO EXECUTE // ----------------------------------------------------------------- -// - Call node root enpoint (get a list of all endpoints) +// - Call node root enpoint (health check before targeting the known endpoints) // - Call all HTTP endpoints (with & without proper params) // - Execute requests with & without RATE limits on the node instance // - Call directCommand enpoint with all supported commands @@ -115,21 +115,109 @@ export async function targetEndpoint(api, method, path) { }) } -// 1st step - get root enpoint and call all paths +// static list of the HTTP endpoints mounted by the node +// (see src/components/httpRoutes/index.ts). The node root endpoint no longer +// advertises the available routes, so the paths are hardcoded here. +const SERVICE_ENDPOINTS = [ + // p2p + ['getP2pNetworkStats', 'GET', '/getP2pNetworkStats'], + ['findPeer', 'GET', '/findPeer'], + ['getP2PPeers', 'GET', '/getP2PPeers'], + ['getP2PPeer', 'GET', '/getP2PPeer'], + // p2p / did + ['getProvidersForString', 'GET', '/getProvidersForString'], + ['getProvidersForStrings', 'POST', '/getProvidersForStrings'], + // direct commands (http + p2p) + ['directCommand', 'POST', '/directCommand'], + // logs + ['logs', 'POST', '/logs'], + ['log', 'POST', '/log/:id'], + // fileInfo + ['fileInfo', 'POST', '/api/services/fileInfo'], + // provider + ['decrypt', 'POST', '/api/services/decrypt'], + ['encrypt', 'POST', '/api/services/encrypt'], + ['encryptFile', 'POST', '/api/services/encryptFile'], + ['initialize', 'GET', '/api/services/initialize'], + ['nonce', 'GET', '/api/services/nonce'], + ['download', 'GET', '/api/services/download'], + // aquarius / assets / ddo + ['getDDO', 'GET', '/api/aquarius/assets/ddo/:did/:force?'], + ['getDDOMetadata', 'GET', '/api/aquarius/assets/metadata/:did/:force?'], + ['ddoMetadataQuery', 'POST', '/api/aquarius/assets/metadata/query'], + ['getDDOState', 'GET', '/api/aquarius/state/ddo'], + ['validateDDO', 'POST', '/api/aquarius/assets/ddo/validate'], + // compute + ['computeEnvironments', 'GET', '/api/services/computeEnvironments'], + ['computeStart', 'POST', '/api/services/compute'], + ['freeCompute', 'POST', '/api/services/freeCompute'], + ['computeStop', 'PUT', '/api/services/compute'], + ['computeStatus', 'GET', '/api/services/compute'], + ['computeResult', 'GET', '/api/services/computeResult'], + ['computeStreamableLogs', 'GET', '/api/services/computeStreamableLogs'], + ['initializeCompute', 'POST', '/api/services/initializeCompute'], + ['computeDelete', 'DELETE', '/api/services/compute'], + // service on demand + ['serviceTemplates', 'GET', '/api/services/serviceTemplates'], + ['serviceStart', 'POST', '/api/services/serviceStart'], + ['serviceStop', 'POST', '/api/services/serviceStop'], + ['serviceExtend', 'POST', '/api/services/serviceExtend'], + ['serviceRestart', 'POST', '/api/services/serviceRestart'], + ['serviceStatus', 'GET', '/api/services/serviceStatus'], + ['serviceList', 'GET', '/api/services/serviceList'], + ['serviceStreamableLogs', 'GET', '/api/services/serviceStreamableLogs'], + // queue + ['indexQueue', 'GET', '/api/services/indexQueue'], + // jobs + ['jobs', 'GET', '/api/services/jobs/:jobId'], + // policy server passthrough + ['PolicyServerPassthrough', 'POST', '/api/services/PolicyServerPassthrough'], + ['initializePSVerification', 'POST', '/api/services/initializePSVerification'], + // auth + ['generateAuthToken', 'POST', '/api/services/auth/token'], + ['invalidateAuthToken', 'POST', '/api/services/auth/token/invalidate'], + // admin config + ['adminConfig', 'GET', '/api/admin/config'], + ['adminConfigUpdate', 'POST', '/api/admin/config/update'], + // persistent storage + ['createBucket', 'POST', '/api/services/persistentStorage/buckets'], + ['updateBucket', 'PATCH', '/api/services/persistentStorage/buckets/:bucketId'], + ['listBuckets', 'GET', '/api/services/persistentStorage/buckets'], + ['listBucketFiles', 'GET', '/api/services/persistentStorage/buckets/:bucketId/files'], + [ + 'getBucketFileObject', + 'GET', + '/api/services/persistentStorage/buckets/:bucketId/files/:fileName/object' + ], + [ + 'uploadBucketFile', + 'POST', + '/api/services/persistentStorage/buckets/:bucketId/files/:fileName' + ], + [ + 'deleteBucketFile', + 'DELETE', + '/api/services/persistentStorage/buckets/:bucketId/files/:fileName' + ], + // access list + ['accessLists', 'GET', '/api/services/accesslists'], + ['accessList', 'GET', '/api/services/accesslists/:chainId/:contractAddress'], + // escrow events + ['escrowEvents', 'GET', '/api/services/escrow/events'] +] + +// 1st step - check the node is up and call all known paths export async function stepRootEndpoint() { const response = http.get(TARGET_URL) + if (response.status !== 200) { + exec.test.abort('Check if your node is running before calling this script!') + return + } try { - if (response.status === 200) { - const data = JSON.parse(response.body) - const endpoints = Object.keys(data.serviceEndpoints) - //query all endpoints, exclude params - for (const endpointName of endpoints) { - const apiData = data.serviceEndpoints[endpointName] - console.log('Targeting endpoint: ', endpointName, 'Method/path:', apiData) - await targetEndpoint(endpointName, apiData[0], apiData[1]) - } - } else { - exec.test.abort('Check if your node is running before calling this script!') + // query all endpoints, exclude params + for (const [endpointName, method, path] of SERVICE_ENDPOINTS) { + console.log('Targeting endpoint: ', endpointName, 'Method/path:', method, path) + await targetEndpoint(endpointName, method, path) } } catch (error) { console.error('Endpoint error:', error)