Add CLI tool to compare watcher GQL responses (#494)

* Integrate gql response compare tool

* Add README for compare-gql tool

* Update readme

* Update CLI output logs

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2023-11-28 09:51:26 +05:30 committed by GitHub
parent dab2d6d3e7
commit ceb0a3de23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 238 additions and 2 deletions

View File

@ -54,3 +54,43 @@ A basic CLI to pass messages between peers using `stdin`/`stdout`
* `enable-debug-info`: Whether to broadcast node's info over pubsub on request
* The process starts reading from `stdin` and outputs messages from others peers over the `/chat/1.0.0` protocol to `stdout`.
## compare-gql
A basic CLI to to fetch and compare watcher GQL response.
* Create a `config.yaml` file in the following format in `packages/cli`:
```yaml
# Watcher URL
url: ""
# Boolean to specify if it is a subgraph watcher
isSubgraph: false
# File path for saving GQL result
gqlResultFilepath: "./result.json"
# Optional parameter to override default GQL query
graphqlQuery: ""
```
* Run the following command to fetch and save first GQL response:
```
yarn compare-gql --config config.yaml
```
* On running CLI for the first time
```
vulcanize:compare-gql Fetching response for the first time, re run CLI to compare with latest GQL response
```
* Re run CLI after some time (average time taken for new block in Ethereum: `~12 seconds`)
```
yarn compare-gql --config config.yaml
```
* The diff for latest and previous GQL responses is shown

12
packages/cli/config.yaml Normal file
View File

@ -0,0 +1,12 @@
# Watcher URL
url: ""
# Boolean to specify if it is a subgraph watcher
isSubgraph: false
# File path for saving GQL result
gqlResultFilepath: "./result.json"
# Optional parameter to override default GQL query
# Set GQL filepath containing query
graphqlQuery: ""

View File

@ -0,0 +1,34 @@
{
azimuthGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
claimsGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
censuresGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
conditionalStarReleaseGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
delegatedSendingGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
eclipticGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
linearStarReleaseGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
pollsGetSyncStatus {
latestProcessedBlockNumber
latestIndexedBlockNumber
}
}

View File

@ -0,0 +1,12 @@
{
getSyncStatus {
initialIndexedBlockHash
initialIndexedBlockNumber
latestCanonicalBlockHash
latestIndexedBlockHash
latestCanonicalBlockNumber
latestIndexedBlockNumber
latestProcessedBlockHash
latestProcessedBlockNumber
}
}

View File

@ -0,0 +1,19 @@
{
getSyncStatus {
initialIndexedBlockHash
initialIndexedBlockNumber
latestCanonicalBlockHash
latestIndexedBlockHash
latestCanonicalBlockNumber
latestIndexedBlockNumber
latestProcessedBlockHash
latestProcessedBlockNumber
}
_meta {
block {
hash
number
timestamp
}
}
}

View File

@ -8,7 +8,8 @@
"build": "yarn clean && tsc && yarn copy-assets",
"clean": "rm -rf ./dist",
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/",
"chat": "DEBUG='vulcanize:*, laconic:*' node dist/chat.js"
"chat": "DEBUG='vulcanize:*, laconic:*' node dist/chat.js",
"compare-gql": "DEBUG='vulcanize:*' node dist/compare-gql.js"
},
"dependencies": {
"@apollo/client": "^3.7.1",
@ -27,7 +28,11 @@
"debug": "^4.3.1",
"ethers": "^5.4.4",
"express": "^4.18.2",
"graphql": "^15.5.0",
"graphql-request": "^6.1.0",
"graphql-subscriptions": "^2.0.0",
"js-yaml": "^4.0.0",
"json-diff": "^0.5.4",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"reflect-metadata": "^0.1.13",
@ -36,6 +41,8 @@
},
"devDependencies": {
"@types/express": "^4.17.14",
"@types/js-yaml": "^4.0.3",
"@types/json-diff": "^0.5.2",
"@types/node": "16.11.7",
"@types/pluralize": "^0.0.29",
"@types/yargs": "^17.0.0",

View File

@ -0,0 +1,90 @@
import { request } from 'graphql-request';
import * as fs from 'fs';
import jsonDiff from 'json-diff';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import path from 'path';
import yaml from 'js-yaml';
import debug from 'debug';
const SUBGRAPH_QUERY_FILEPATH = 'graphql/subgraph-query.graphql';
const NON_SUBGRAPH_QUERY_FILEPATH = 'graphql/non-subgraph-query.graphql';
const log = debug('vulcanize:compare-gql');
function readFromJSONFile (filename: string): {[key: string]: any} | null {
const fileContents = fs.readFileSync(filename, 'utf-8');
if (fileContents !== '') {
return JSON.parse(fileContents);
}
return null;
}
function getQuery (isSubgraph: boolean, queryFilepath?: string): any {
if (queryFilepath) {
return fs.readFileSync(queryFilepath, 'utf-8');
}
if (isSubgraph) {
return fs.readFileSync(SUBGRAPH_QUERY_FILEPATH, 'utf-8');
}
return fs.readFileSync(NON_SUBGRAPH_QUERY_FILEPATH, 'utf-8');
}
async function main (): Promise<void> {
const argv = await yargs(hideBin(process.argv))
.option('config', {
alias: 'c',
demandOption: true,
describe: 'Watcher config file path (yaml)',
type: 'string'
}).argv;
const configFilePath = path.resolve(argv.config);
const inputConfig = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as any;
log('Config:', inputConfig);
const isSubgraph = inputConfig.isSubgraph;
const watcherUrl = inputConfig.url;
const configFileDirectory = path.dirname(configFilePath);
const gqlResultFilepath = path.resolve(configFileDirectory, inputConfig.gqlResultFilepath);
const graphqlQueryFilepath = inputConfig.graphqlQuery ? path.resolve(configFileDirectory, inputConfig.graphqlQuery) : undefined;
if (!fs.existsSync(gqlResultFilepath)) {
fs.writeFileSync(gqlResultFilepath, '', 'utf-8');
}
const query = getQuery(isSubgraph, graphqlQueryFilepath);
let gqlResponse;
try {
gqlResponse = await request(watcherUrl, query);
} catch (err) {
throw new Error('Error in GQL request: ' + (err as Error).message);
}
const readOutputData = readFromJSONFile(gqlResultFilepath);
if (readOutputData !== null) {
const diff = jsonDiff.diffString(readOutputData, gqlResponse);
if (diff !== '') {
log('Diff detected', diff);
} else {
log('No diff detected, GQL response', gqlResponse);
}
} else {
log('Fetching response for the first time, re run CLI to compare with latest GQL response');
}
fs.writeFileSync(gqlResultFilepath, JSON.stringify(gqlResponse, null, 2));
}
main().catch(err => {
log(err);
});

View File

@ -1226,7 +1226,7 @@
"@graphql-typed-document-node/core" "^3.1.1"
tslib "^2.4.0"
"@graphql-typed-document-node/core@^3.1.1":
"@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
@ -6512,6 +6512,13 @@ cross-fetch@^3.1.4:
dependencies:
node-fetch "2.6.7"
cross-fetch@^3.1.5:
version "3.1.8"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82"
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
dependencies:
node-fetch "^2.6.12"
cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz"
@ -9021,6 +9028,14 @@ graphql-compose@^9.0.3:
dependencies:
graphql-type-json "0.3.2"
graphql-request@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f"
integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==
dependencies:
"@graphql-typed-document-node/core" "^3.2.0"
cross-fetch "^3.1.5"
graphql-subscriptions@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-2.0.0.tgz"
@ -12223,6 +12238,13 @@ node-fetch@^2.6.1:
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
node-fetch@~1.7.1:
version "1.7.3"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz"