Skip to content

Commit

Permalink
Feature/validator (subquery#219)
Browse files Browse the repository at this point in the history
* add validator package

* add cli validate command

* add skip in the report

* update readme

* update tsconfig reference

* change await code style
  • Loading branch information
zhex authored Mar 10, 2021
1 parent 9015c91 commit 8632abc
Show file tree
Hide file tree
Showing 27 changed files with 550 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Check our query component [`@subql/query`](packages/query)
* [`@subql/query`](packages/query)
* [`@subql/common`](packages/common)
* [`@subql/types`](packages/types)
* [`@subql/validator`](packages/validator)

## More Documentation
For more documentation, visit [doc.subquery.network](https://doc.subquery.network/)
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"@oclif/config": "^1.17.0",
"@oclif/plugin-help": "^3.2.1",
"@subql/common": "workspace:*",
"@subql/validator": "workspace:*",
"@types/ejs": "^3.0.5",
"chalk": "^4.1.0",
"cli-ux": "^5.5.1",
"ejs": "^3.1.5",
"rimraf": "^3.0.2",
Expand Down
47 changes: 47 additions & 0 deletions packages/cli/src/commands/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import {Command, flags} from '@oclif/command';
import {commonRules, Validator} from '@subql/validator';
import chalk from 'chalk';

export default class Validate extends Command {
static description = 'check a folder or github repo is a validate subquery project';

static flags = {
location: flags.string({char: 'l', description: 'local folder or github repo url'}),
silent: flags.boolean(),
};

async run(): Promise<void> {
const {flags} = this.parse(Validate);
const v = new Validator(flags.location ?? process.cwd());
v.addRule(...commonRules);

const reports = await v.validate();
const passed = reports.filter((r) => r.valid).length;
const skipped = reports.filter((r) => r.skipped).length;
const failed = reports.length - passed - skipped;

if (!flags.silent) {
for (const r of reports) {
if (r.valid) {
this.log(`${chalk.bgGreen.bold(' PASS ')} ${r.name}`);
} else if (r.skipped) {
this.log(`${chalk.yellow.bold(' SKIP ')} ${r.name}`);
} else {
this.log(`${chalk.bgRed.bold(' FAIL ')} ${r.name}`);
this.log(` ${chalk.redBright(r.description)}`);
}
}

this.log('');
this.log(`Result: ${passed} passed, ${failed} failed, ${skipped} skipped`);
this.log('');
}

if (failed > 0) {
this.exit(1);
}
}
}
3 changes: 2 additions & 1 deletion packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"outDir": "lib"
},
"references": [
{"path": "../common"}
{"path": "../common"},
{"path": "../validator"}
],
"include": [
"src/**/*"
Expand Down
1 change: 1 addition & 0 deletions packages/validator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @subql/validator
28 changes: 28 additions & 0 deletions packages/validator/fixtures/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "subquery-starter",
"version": "0.0.3",
"description": "This project can be use as a starting point for developing your SubQuery project",
"main": "dist/index.js",
"scripts": {
"build": "tsc -b",
"prepack": "rm -rf dist && npm build",
"test": "jest",
"codegen": "./node_modules/.bin/subql codegen"
},

"homepage": "https://github.com/subquery/subql-starter",
"repository": "github:subquery/subql-starter",
"files": [
"dist",
"schema.graphql",
"project.yaml"
],
"author": "Ian He & Jay Ji",
"license": "Apache-2.0",
"devDependencies": {
"@polkadot/api": "^3",
"@subql/types": "^0.6.0",
"typescript": "^4.1.3",
"@subql/cli": "^0.7.3"
}
}
24 changes: 24 additions & 0 deletions packages/validator/fixtures/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
specVersion: "0.0.1"
description: ""
repository: "https://github.com/subquery/subql-starter"

schema: "./schema.graphql"

network:
endpoint: "wss://polkadot.api.onfinality.io/public-ws"

dataSources:
- name: main
kind: substrate/Runtime
startBlock: 1
mapping:
handlers:
- handler: handleBlock
kind: substrate/BlockHandler
- handler: handleEvent
kind: substrate/EventHandler
filter: #Filter is optional
module: balances
method: Deposit
- handler: handleCall
kind: substrate/CallHandler
26 changes: 26 additions & 0 deletions packages/validator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@subql/validator",
"version": "0.1.0",
"description": "to validate subquery project",
"homepage": "https://github.com/subquery/subql",
"repository": "github:subquery/subql",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rm -rf dist && tsc -b"
},
"author": "ZheX",
"main": "dist/index.js",
"license": "Apache-2.0",
"files": [
"src/global.d.ts",
"/dist"
],
"dependencies": {
"axios": "^0.21.1",
"js-yaml": "^4.0.0",
"package-json-type": "^1.0.3"
},
"devDependencies": {
"@types/js-yaml": "^4.0.0"
}
}
15 changes: 15 additions & 0 deletions packages/validator/src/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import {IPackageJson} from 'package-json-type';

export interface ContextData {
projectPath: string;
pkg: IPackageJson;
schema?: unknown;
}

export interface Context {
data: ContextData;
logger: Console;
}
6 changes: 6 additions & 0 deletions packages/validator/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

export * from './validator';
export * from './readers';
export * from './rules';
24 changes: 24 additions & 0 deletions packages/validator/src/readers/github-reader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import {GithubReader} from './github-reader';
import {Reader} from './reader';

describe('GithubReader', () => {
let reader: Reader;

beforeAll(() => {
const key = 'subquery/subql-starter';
reader = new GithubReader(key);
});

it('should return the package json object', async () => {
const data = await reader.getPkg();
expect(data.name).toBe('subquery-starter');
});

it('should return the project schema object', async () => {
const data: any = await reader.getProjectSchema();
expect(data.repository).toBe('https://github.com/subquery/subql-starter');
});
});
47 changes: 47 additions & 0 deletions packages/validator/src/readers/github-reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import axios, {AxiosInstance} from 'axios';
import yaml from 'js-yaml';
import {IPackageJson} from 'package-json-type';
import {Reader} from './reader';

export class GithubReader implements Reader {
private readonly api: AxiosInstance;
private defaultBranch: string;

constructor(private readonly key: string) {
this.api = axios.create({
baseURL: `https://raw.githubusercontent.com/${key}`,
});
}

async getPkg(): Promise<IPackageJson | undefined> {
try {
const branch = await this.getDefaultBranch();
const {data} = await this.api.get(`${branch}/package.json`);
return data;
} catch (err) {
return undefined;
}
}

async getProjectSchema(): Promise<unknown | undefined> {
try {
const branch = await this.getDefaultBranch();
const {data} = await this.api.get(`${branch}/project.yaml`);
return yaml.load(data);
} catch (err) {
return undefined;
}
}

private async getDefaultBranch(): Promise<string> {
if (this.defaultBranch) {
return this.defaultBranch;
}
const {data} = await axios.get(`https://api.github.com/repos/${this.key}`);
this.defaultBranch = data.default_branch;
return this.defaultBranch;
}
}
6 changes: 6 additions & 0 deletions packages/validator/src/readers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

export * from './reader';
export * from './github-reader';
export * from './local-reader';
25 changes: 25 additions & 0 deletions packages/validator/src/readers/local-reader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import * as path from 'path';
import {LocalReader} from './local-reader';
import {Reader} from './reader';

describe('LocalReader', () => {
let reader: Reader;

beforeAll(() => {
const loc = path.join(__dirname, '../../fixtures');
reader = new LocalReader(loc);
});

it('should return the package json object', async () => {
const data = await reader.getPkg();
expect(data.name).toBe('subquery-starter');
});

it('should return the project schema object', async () => {
const data: any = await reader.getProjectSchema();
expect(data.repository).toBe('https://github.com/subquery/subql-starter');
});
});
40 changes: 40 additions & 0 deletions packages/validator/src/readers/local-reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import * as fs from 'fs';
import * as path from 'path';
import yaml from 'js-yaml';
import {IPackageJson} from 'package-json-type';
import {Reader} from './reader';

export class LocalReader implements Reader {
constructor(private readonly projectPath: string) {}

async getPkg(): Promise<IPackageJson | undefined> {
const file = path.join(this.projectPath, 'package.json');
if (!fs.existsSync(file)) {
return Promise.resolve(undefined);
}

try {
const data = JSON.parse(fs.readFileSync(file).toString());
return Promise.resolve(data);
} catch (err) {
return Promise.resolve(undefined);
}
}

async getProjectSchema(): Promise<unknown | undefined> {
const file = path.join(this.projectPath, 'project.yaml');
if (!fs.existsSync(file)) {
return Promise.resolve(undefined);
}

try {
const data = yaml.load(fs.readFileSync(file).toString());
return Promise.resolve(data);
} catch (err) {
return Promise.resolve(undefined);
}
}
}
21 changes: 21 additions & 0 deletions packages/validator/src/readers/reader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import path from 'path';
import {GithubReader} from './github-reader';
import {LocalReader} from './local-reader';
import {ReaderFactory} from './reader';

describe('ReaderFactory', () => {
it('should return the Github Reader', () => {
const url = 'https://github.com/subquery/subql-starter';
const reader = ReaderFactory.create(url);
expect(reader instanceof GithubReader).toBeTruthy();
});

it('should return the Local Reader', () => {
const loc = path.join(__dirname, '../../fixtures');
const reader = ReaderFactory.create(loc);
expect(reader instanceof LocalReader).toBeTruthy();
});
});
29 changes: 29 additions & 0 deletions packages/validator/src/readers/reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

import * as fs from 'fs';
import {IPackageJson} from 'package-json-type';
import {GithubReader} from './github-reader';
import {LocalReader} from './local-reader';

export interface Reader {
getProjectSchema(): Promise<unknown | undefined>;
getPkg(): Promise<IPackageJson | undefined>;
}

export class ReaderFactory {
static create(location: string): Reader {
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
const githubMatch = location.match(/https:\/\/github.com\/([\w-_]+\/[\w-_]+)/i);
if (githubMatch) {
return new GithubReader(githubMatch[1]);
}

const stats = fs.statSync(location);
if (stats.isDirectory()) {
return new LocalReader(location);
}

throw new Error(`unknown location: ${location}`);
}
}
7 changes: 7 additions & 0 deletions packages/validator/src/rules/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright 2020-2021 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0

export * from './rule';
export * from './require-build-script';
export * from './require-codegen-script';
export * from './require-cli-dep';
Loading

0 comments on commit 8632abc

Please sign in to comment.