From 3e92f4b1228920ee2ca064cee71abe9e287a76a8 Mon Sep 17 00:00:00 2001 From: Carly Doppelheuer Date: Tue, 5 Sep 2023 14:28:55 -0500 Subject: [PATCH] add route to search event versions --- .../event-version/eventVersionPayloads.ts | 13 +++ .../event-version/eventVersionRoutes.ts | 21 ++++- core-api/src/services/searchEventVersions.ts | 90 +++++++++++++++++++ core-api/src/utils/paths.ts | 1 + 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 core-api/src/services/searchEventVersions.ts diff --git a/core-api/src/routes/event-version/eventVersionPayloads.ts b/core-api/src/routes/event-version/eventVersionPayloads.ts index 13efb370..ab1cb70b 100644 --- a/core-api/src/routes/event-version/eventVersionPayloads.ts +++ b/core-api/src/routes/event-version/eventVersionPayloads.ts @@ -13,6 +13,10 @@ export interface GetEventVersionDataAfterPayload { cursors: StringKeyMap } +export interface SearchEventVersionPayload { + name: string +} + export function parseResolveEventVersionsPayload( data: StringKeyMap ): ValidatedPayload { @@ -78,4 +82,13 @@ export function parseGetEventVersionDataAfterPayload( cursors, }, } +} + +export function parseSearchEventVersionPayload(data: StringKeyMap): ValidatedPayload { + const name = data?.name || null + + return { + isValid: true, + payload: { name, }, + } } \ No newline at end of file diff --git a/core-api/src/routes/event-version/eventVersionRoutes.ts b/core-api/src/routes/event-version/eventVersionRoutes.ts index 62b98718..b814922b 100644 --- a/core-api/src/routes/event-version/eventVersionRoutes.ts +++ b/core-api/src/routes/event-version/eventVersionRoutes.ts @@ -1,8 +1,9 @@ import { app } from '../express' import paths from '../../utils/paths' -import { parseResolveEventVersionsPayload, parseResolveEventVersionCursorsPayload, parseGetEventVersionDataAfterPayload } from './eventVersionPayloads' +import { parseResolveEventVersionsPayload, parseResolveEventVersionCursorsPayload, parseGetEventVersionDataAfterPayload, parseSearchEventVersionPayload } from './eventVersionPayloads' import { codes, errors, authorizeRequestWithProjectApiKey } from '../../utils/requests' import { resolveEventVersionNames, resolveEventVersionCursors, getPublishedEventsAfterEventCursors } from '../../../../shared' +import { searchEventVersions } from '../../services/searchEventVersions' /** * Resolve full event version names for a set of given event "inputs". @@ -62,4 +63,22 @@ app.post(paths.RESOLVE_EVENT_VERSION_CURSORS, async (req, res) => { } return res.status(codes.SUCCESS).json({ events }) +}) + +/** + * Search event versions. + */ +app.get(paths.EVENT_VERSIONS_SEARCH, async (req, res) => { + // Parse & validate payload. + const { payload, isValid, error } = parseSearchEventVersionPayload(req.query) + if (!isValid) { + return res.status(codes.BAD_REQUEST).json({ error: error || errors.INVALID_PAYLOAD }) + } + const { name} = payload + + // Find event versions by search terms. + const { data, error: searchError } = await searchEventVersions(name) + return searchError + ? res.status(codes.INTERNAL_SERVER_ERROR).json({ error: searchError || errors.UNKNOWN_ERROR }) + : res.status(codes.SUCCESS).json(data) }) \ No newline at end of file diff --git a/core-api/src/services/searchEventVersions.ts b/core-api/src/services/searchEventVersions.ts new file mode 100644 index 00000000..fc7943de --- /dev/null +++ b/core-api/src/services/searchEventVersions.ts @@ -0,0 +1,90 @@ +import { AbiItemType, CoreDB, camelizeKeys, getContractGroupAbi, logger } from '../../../shared' +import { StringKeyMap } from '../types' + +export async function searchEventVersions(name: string): Promise { + let results + let uniqueContractGroupsSet = new Set() + let contractGroupAbisMap = {} + + // Query database. + try { + results = await CoreDB.query( + `SELECT DISTINCT ON (search_id) + name, + version, + search_id, + contract_group + FROM + (SELECT + nsp, + name, + version, + CASE + WHEN nsp LIKE '%.%' + THEN CONCAT(ARRAY_TO_STRING((STRING_TO_ARRAY(nsp, '.'))[3:4], '.'), '.', name, '@', version) + ELSE CONCAT(nsp, '.', name, '@', version) + END as search_id, + CASE + WHEN nsp LIKE '%.%' + THEN ARRAY_TO_STRING((STRING_TO_ARRAY(nsp, '.'))[3:4], '.') + ELSE nsp + END as contract_group + FROM event_versions + WHERE + CASE + WHEN $1::text IS NOT NULL + THEN (nsp || '.' || name) ILIKE CONCAT('%.contracts.', $1, '%') + ELSE TRUE + END) AS uev + LIMIT 5;`, [name] + ) + } catch (err) { + logger.error(`Error searching event versions: ${err}`) + return { error: err?.message || err } + } + + // Camelize result keys. + results = camelizeKeys(results) + + // Store unique contract groups. + results.forEach(result => uniqueContractGroupsSet.add(result.contractGroup)) + const uniqueContractGroups = Array.from(uniqueContractGroupsSet) as string[] + + // Retrieve and store redis abi data for each contract group. + const abis = await Promise.all( + uniqueContractGroups.map((group: string) => getContractGroupAbi(group)) + ) + + for (let i = 0; i < uniqueContractGroups.length; i++) { + const group = uniqueContractGroups[i] + const abi = abis[i] + contractGroupAbisMap[group] = abi + } + + // Format results for CLI. + function formatForCLI(result) { + let finalResult = { + searchId: result.searchId, + addressProperties: [] + } + + const abi = contractGroupAbisMap[result.contractGroup] + if (!abi?.length) return finalResult + + const event = abi.find(item => ( + item.type === AbiItemType.Event && + item.name === result.name && + item.signature === result.version + )) + if (!event) return finalResult + + const addressProperties = event.inputs + .filter(input => input.type === 'address') + .map(input => input.name) + if (!addressProperties.length) return finalResult + + return {...finalResult, addressProperties} + } + + return { data: results.map(formatForCLI) } +} \ No newline at end of file diff --git a/core-api/src/utils/paths.ts b/core-api/src/utils/paths.ts index 99db1c44..c5e86c1f 100644 --- a/core-api/src/utils/paths.ts +++ b/core-api/src/utils/paths.ts @@ -60,6 +60,7 @@ const paths = { RESOLVE_EVENT_VERSIONS: prefix.EVENT_VERSION + 's' + '/resolve', RESOLVE_EVENT_VERSION_CURSORS: prefix.EVENT_VERSION + 's' + '/resolve/cursors', GET_EVENT_VERSION_DATA_AFTER: prefix.EVENT_VERSION + 's' + '/data/after', + EVENT_VERSIONS_SEARCH: prefix.EVENT_VERSION + 's' + '/search', // Call Version paths. RESOLVE_CALL_VERSIONS: prefix.CALL_VERSION + 's' + '/resolve',