diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 0000000..0611fa8 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,119 @@ +# Registry Demo + +## Setup + +* Install laconic CLI globally: + + ```bash + # In laconic-registry-cli repo root + yarn && yarn build + yarn global add file:$PWD + ``` + +* Run the laconicd chain: + + ```bash + # In laconci2d repo + make install + ./scripts/init.sh clean + ``` + +* Create and populate `config.yml` following [config.example.yml](./config.example.yml): + + ```bash + # In laconic-registry-cli repo root + cp config.example.yml config.yml + + # Update the gas value in config.yml + # gas: 500000 + + # Get user private key + laconicd keys export alice --unarmored-hex --unsafe --keyring-backend test --home ~/.laconicd + + # Set the output as 'userKey' in config.yml + # userKey: + + # Create a bond + laconic --config config.yml registry bond create --type photon --quantity 100000000000 + + # Get the bond id + laconic --config config.yml registry bond list | jq -r '.[].id' + + # Set the output as 'bondId' in config.yml + # bondId: + ``` + +## Run + +* Publish records: + + ```bash + # Publishes records and corresponding 'deployment' records from given directory + + # In laconic-registry-cli repo root + # Use records dir path for '--records' as required + yarn ts-node demo/scripts/publish-records.ts --config config.yml --records + ``` + +### Example + +* Query for `ajna-watcher` deployment(s): + + * Find the `WatcherRecord` for `ajna-watcher`: + + ```bash + WATCHER_RECORD_ID=$(laconic registry record list --all --type WatcherRecord --name ajna-watcher | jq -r '.[].id') + ``` + + * Find corresponding deployment(s): + + ```bash + laconic registry record list --all --type WatcherDeploymentRecord watcher $WATCHER_RECORD_ID + + # Get the deployment URL(s) + laconic registry record list --all --type WatcherDeploymentRecord watcher $WATCHER_RECORD_ID | jq -r '.[].attributes.url' + + # Expected output: + https://ajna-watcher-endpoint.example.com + ``` + +* Query for `sushiswap-v3-subgraph` deployment(s): + + * Find the `SubgraphRecord` for `sushiswap-v3-subgraph`: + + ```bash + SUBGRAPH_RECORD_ID=$(laconic registry record list --all --type SubgraphRecord --name sushiswap-v3-subgraph | jq -r '.[].id') + ``` + + * Find corresponding deployment(s): + + ```bash + laconic registry record list --all --type SubgraphDeploymentRecord subgraph $SUBGRAPH_RECORD_ID + + # Get the deployment URL(s) + laconic registry record list --all --type SubgraphDeploymentRecord subgraph $SUBGRAPH_RECORD_ID | jq -r '.[].attributes.url' + + # Expected output: + # https://sushiswap-v3-subgraph-endpoint.example.com + ``` + +* Query for `geth` service deployment(s): + + * Find the `ServiceRecord` for `geth`: + + ```bash + SERVICE_RECORD_ID=$(laconic registry record list --all --type ServiceRecord --name geth | jq -r '.[].id') + ``` + + * Find corresponding deployment(s): + + ```bash + laconic registry record list --all --type ServiceDeploymentRecord service $SERVICE_RECORD_ID + + # Get the deployment URL(s) + laconic registry record list --all --type ServiceDeploymentRecord service $SERVICE_RECORD_ID | jq -r '.[].attributes.url' + + # Expected output: + # https://geth-rpc-endpoint-1.example.com + # https://geth-rpc-endpoint-2.example.com + ``` diff --git a/demo/scripts/publish-records.ts b/demo/scripts/publish-records.ts new file mode 100644 index 0000000..efb2a72 --- /dev/null +++ b/demo/scripts/publish-records.ts @@ -0,0 +1,170 @@ +import yargs from 'yargs'; +import fs from 'fs'; +import path from 'path'; +import assert from 'assert'; +import { hideBin } from 'yargs/helpers'; + +import { Registry } from '@cerc-io/registry-sdk'; + +import { getConfig, getGasAndFees, getConnectionInfo, txOutput } from '../../src/util'; + +const recordTypeToRecordField = new Map([ + ['WatcherRecord', 'watcher'], + ['SubgraphRecord', 'subgraph'], + ['ServiceRecord', 'service'] +]); + +let registry: Registry; +let fee: any; +let userKey: string; +let bondId: string; + +async function main () { + const argv = getArgs(); + const { records: recordsDir, config } = argv; + + const { services: { registry: registryConfig } } = getConfig(config as string); + + if (registryConfig.userKey == null) { + throw new Error('userKey not set in config'); + } + + if (registryConfig.bondId == null) { + throw new Error('bondId not set in config'); + } + + let rpcEndpoint, gqlEndpoint, chainId: string; + ({ rpcEndpoint, gqlEndpoint, userKey, bondId, chainId } = getConnectionInfo(argv, registryConfig)); + + registry = new Registry(gqlEndpoint, rpcEndpoint, chainId); + fee = getGasAndFees(argv, registryConfig); + + await processDir(path.resolve(recordsDir)); +} + +async function processDir (directoryPath: string): Promise { + const files = fs.readdirSync(directoryPath); + + // Check if any JSON record file exists in the directory + if (files.some(file => file.endsWith('.json'))) { + await publishRecordsFromDir(directoryPath); + + // Skip further recursion in the current dir + return; + } + + // Recursively iterate through subdirectories + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const filePath = path.join(directoryPath, file); + + if (fs.statSync(filePath).isDirectory()) { + await processDir(filePath); + } + } +} + +async function publishRecordsFromDir (recordsDir: string): Promise { + // List record files + const files = fs.readdirSync(recordsDir); + const jsonFiles = files.filter(file => path.extname(file).toLowerCase() === '.json'); + + // Read record from each JSON file + console.log('**************************************'); + console.log(`Publishing records from ${recordsDir}`); + + let recordType; + for (let i = 0; i < jsonFiles.length; i++) { + const file = jsonFiles[i]; + + const filePath = path.resolve(recordsDir, file); + const record = readRecord(filePath); + + // Publish record + const result = await registry.setRecord({ privateKey: userKey, record, bondId }, userKey, fee); + + console.log(`Published record ${file}`); + txOutput(result, JSON.stringify(result, undefined, 2), '', false); + + recordType = record.type; + } + + // Check if deployment record files exist + const deploymentRecordsDir = path.resolve(recordsDir, 'deployments'); + if (!fs.statSync(deploymentRecordsDir).isDirectory()) { + return; + } + console.log('--------------------------------------'); + console.log(`Publishing deployment records from ${deploymentRecordsDir}`); + + // List record files + const deploymentFiles = fs.readdirSync(deploymentRecordsDir); + const deploymentJsonFiles = deploymentFiles.filter(file => path.extname(file).toLowerCase() === '.json'); + + for (let i = 0; i < deploymentJsonFiles.length; i++) { + const file = deploymentJsonFiles[i]; + + const filePath = path.resolve(deploymentRecordsDir, file); + const deploymentRecord = readRecord(filePath); + + // Find record using name and given type + const recordName = deploymentRecord.name; + assert(recordType, 'recordType could not be resolved'); + const queryResult = await registry.queryRecords({ type: recordType, name: recordName }, true); + if (queryResult.length === 0) { + throw new Error(`Record not found, type: ${recordType}, name: ${recordName}`); + } + + // Assume the first query result + const recordId = queryResult[0].id; + + // Set record field to record id + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + deploymentRecord[recordTypeToRecordField.get(recordType)!] = recordId; + + // Publish record + const deploymentResult = await registry.setRecord({ privateKey: userKey, record: deploymentRecord, bondId }, userKey, fee); + + console.log(`Published record ${file}`); + txOutput(deploymentResult, JSON.stringify(deploymentResult, undefined, 2), '', false); + } +} + +function readRecord (filePath: string): any { + let record; + try { + const data = fs.readFileSync(filePath, 'utf8'); + record = JSON.parse(data); + } catch (err) { + console.error(`Error reading file ${filePath}:`, err); + } + + return record; +} + +function getArgs (): any { + return yargs(hideBin(process.argv)).parserConfiguration({ + 'parse-numbers': false + }).usage('Usage: $0 [options]') + .option('config', { + alias: 'c', + describe: 'Config', + type: 'string', + demandOption: true + }) + .option('records', { + alias: 'r', + describe: 'Records dir path', + type: 'string', + demandOption: true + }) + .help().argv; +} + +main() + .catch(err => { + console.error(err); + }) + .finally(() => { + console.log('Done'); + }); diff --git a/package.json b/package.json index b9d7c80..aa277ce 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "husky": "^9.0.2", "jest": "29.0.0", "ts-jest": "^29.0.2", + "ts-node": "^10.2.1", "typescript": "^4.6.3" }, "dependencies": { diff --git a/src/types/common/main.d.ts b/src/types/common/main.d.ts new file mode 100644 index 0000000..c9cad7c --- /dev/null +++ b/src/types/common/main.d.ts @@ -0,0 +1 @@ +declare module 'lodash-clean'; diff --git a/src/types/common/package.json b/src/types/common/package.json new file mode 100644 index 0000000..5861d0f --- /dev/null +++ b/src/types/common/package.json @@ -0,0 +1,6 @@ +{ + "name": "common", + "version": "0.1.0", + "license": "AGPL-3.0", + "typings": "main.d.ts" +} diff --git a/tsconfig.json b/tsconfig.json index 7e6e42a..91d62b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,10 @@ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + "typeRoots": [ + "node_modules/@types", + "src/types" + ], // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "resolveJsonModule": true, /* Enable importing .json files */ diff --git a/yarn.lock b/yarn.lock index a21f606..7b55ecb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -562,6 +562,13 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.32.3.tgz#5dcaee6dd7cc846cdc073e9a7a7f63242f5f7e31" integrity sha512-WCZK4yksj2hBDz4w7xFZQTRZQ/RJhBX26uFHmmQFIcNUUVAihrLO+RerqJgk0dZqC42wstM9pEUQGtPmLcIYvg== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1186,6 +1193,11 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" @@ -1201,6 +1213,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": version "0.3.22" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" @@ -1378,6 +1398,26 @@ link-module-alias "^1.2.0" shx "^0.3.4" +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -1648,7 +1688,12 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.9.0: +acorn-walk@^8.1.1: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +acorn@^8.4.1, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -1707,6 +1752,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2242,6 +2292,11 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -2331,6 +2386,11 @@ diff-sequences@^29.6.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4072,7 +4132,7 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" -make-error@1.x: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -4968,6 +5028,25 @@ ts-jest@^29.0.2: semver "^7.5.3" yargs-parser "^21.0.1" +ts-node@^10.2.1: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -5111,6 +5190,11 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-to-istanbul@^9.0.1: version "9.2.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" @@ -5254,6 +5338,11 @@ yargs@^17.4.1: y18n "^5.0.5" yargs-parser "^21.0.0" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"