Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New Frontend Dashboard #155

Merged
merged 24 commits into from
Jun 13, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
12296d2
feat: add @best/frontend and @best/api-db packages
Jun 7, 2019
9abce33
chore: cleaning up
Jun 7, 2019
aec5e26
chore: update version numbers to match the rest of best
Jun 7, 2019
20a6df8
wip: begin work to integrate api-db into the cli
Jun 8, 2019
fabeaa6
chore: remove eslint in api-db
Jun 10, 2019
a5d6d83
feat: initial implementation of api-db into cli
Jun 10, 2019
ec96612
fix: wrong boolean for temporary on snapshot
Jun 10, 2019
4af65bd
feat: add ability to configure api-db by best config file
Jun 10, 2019
05d493e
chore: remove testing username from best.config.js
Jun 10, 2019
f59cf6a
fix: metrics not get properly normalized for the database
Jun 10, 2019
7eee3e4
fix: get commit date directly from git
Jun 11, 2019
c7e1514
feat: you can now select commits when viewing a benchmark
Jun 11, 2019
2cab84a
wip: begin implementation of selecting commits to show more info
Jun 11, 2019
a3e83ba
wip: designing commit info popover
Jun 11, 2019
2d92caf
chore: clean up some old comments/todos
Jun 11, 2019
0f42aa9
fix: clean up graphs with better ticks
Jun 12, 2019
384a937
fix: improve the format that view.zoom is being stored in urlstorage
Jun 12, 2019
c4649b8
fix: changing selected metric now re-renders benchmarks
Jun 12, 2019
0c2a55a
feat: commit info now shows up in the correct position
Jun 12, 2019
15b2e74
fix: close button for commit info
Jun 12, 2019
0a68e19
feat: refactor benchmarks and create new graph component
Jun 12, 2019
e56a031
fix: relayout handler now always get attached
Jun 12, 2019
23baca6
fix: graphs now get properly resized when window resizes
Jun 13, 2019
12a4aca
fix: non-temporary snapshots now must be unique
Jun 13, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@commitlint/cli": "^8.0.0",
"@commitlint/config-conventional": "^8.0.0",
"@types/express": "^4.17.0",
"@types/globby": "^9.1.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where this comes from?

Copy link
Contributor Author

@jasonsilberman jasonsilberman Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My build was failing because TypeScript couldn't find the types for the globby module which is used in @best/cli and @best/store-fs. I would happily remove it if TypeScript would build without it.

"@types/jest": "^24.0.13",
"@types/json2md": "^1.5.0",
"@types/micromatch": "^3.1.0",
Expand All @@ -51,8 +52,8 @@
"lerna": "^3.14.1",
"prettier": "^1.17.1",
"rimraf": "^2.6.3",
"typescript": "^3.5.1",
"ts-jest": "^24.0.2"
"ts-jest": "^24.0.2",
"typescript": "^3.5.1"
},
"config": {
"commitizen": {
Expand Down
6 changes: 6 additions & 0 deletions packages/@best/api-db/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../../../.eslintrc",
jasonsilberman marked this conversation as resolved.
Show resolved Hide resolved
"rules": {
"@typescript-eslint/camelcase": "off"
}
}
13 changes: 13 additions & 0 deletions packages/@best/api-db/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# API DB

This is the database adapter that the frontend uses to display results. The results are stored whenever a benchmark is run.

There is an associated Postgres db which is the only type of database currently supported. In the future we could add more supported databases.

## Migrations

In order to run the migrations required for the database you can run the following command:

```
yarn migrate up
```
16 changes: 16 additions & 0 deletions packages/@best/api-db/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@best/api-db",
"version": "0.7.1",
"author": "Jason Silberman",
"dependencies": {
"node-pg-migrate": "^3.21.1",
"pg": "^7.11.0"
},
"devDependencies": {
"@types/pg": "^7.4.14"
},
"main": "build/index.js",
"scripts": {
"migrate": "node-pg-migrate -m src/migrations"
}
}
21 changes: 21 additions & 0 deletions packages/@best/api-db/src/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Pool, QueryResult } from 'pg'

const pool = new Pool()

export default {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
query: (text: string, params: any[]): Promise<QueryResult> => {
console.warn('[DB] Directly using db.query is discouraged.')
return pool.query(text, params)
},
fetchProjects: (): Promise<QueryResult> => {
return pool.query('SELECT * FROM projects')
},
fetchSnapshots: (projectId: number, since: string): Promise<QueryResult> => {
if (since) {
return pool.query('SELECT * FROM snapshots WHERE "project_id" = $1 and "commit_date" > $2 ORDER BY commit_date, name', [projectId, since])
}

return pool.query('SELECT * FROM snapshots WHERE "project_id" = $1 ORDER BY commit_date, name', [projectId])
}
}
4 changes: 4 additions & 0 deletions packages/@best/api-db/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import PostgresDB from './postgres';
import saveBenchmarkSummaryInDB from './store';

export { PostgresDB, saveBenchmarkSummaryInDB }
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
exports.shorthands = undefined;

exports.up = pgm => {
pgm.createTable('projects', {
id: 'id',
name: { type: 'varchar(100)', notNull: true },
created_at: {
type: 'timestamp',
notNull: true,
default: pgm.func('current_timestamp'),
},
});
};

exports.down = pgm => {
pgm.dropTable('projects');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
exports.shorthands = undefined;

exports.up = pgm => {
pgm.createTable('snapshots', {
id: 'id',
project_id: {
type: 'integer',
notNull: true,
references: '"projects"',
onDelete: 'cascade',
},
name: { type: 'varchar(200)', notNull: true },
metrics: { type: 'varchar(2000)', notNull: true },
environment_hash: { type: 'varchar(100)', notNull: true },
similarity_hash: { type: 'varchar(100)', notNull: true },
commit: { type: 'varchar(100)', notNull: true },
commit_date: {
type: 'timestamp',
notNull: true,
},
created_at: {
type: 'timestamp',
notNull: true,
default: pgm.func('current_timestamp'),
},
temporary: 'boolean',
});

pgm.createIndex('snapshots', 'project_id');
};

exports.down = pgm => {
pgm.dropTable('snapshots');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
exports.shorthands = undefined;

exports.up = pgm => {
pgm.addColumns('snapshots', {
updated_at: {
type: 'timestamp',
notNull: true,
default: pgm.func('current_timestamp'),
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
exports.shorthands = undefined;

exports.up = pgm => {
pgm.addColumns('projects', {
last_release_date: {
type: 'timestamp',
},
});
};
23 changes: 23 additions & 0 deletions packages/@best/api-db/src/postgres.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ApiDB, Project, TemporarySnapshot, Snapshot } from './types';
import db from './db';
import transformer from './transformer';

export default class PostgresDB extends ApiDB {
async fetchProjects(): Promise<Project[]> {
const results = await db.fetchProjects()

return transformer.projects(results)
}

async fetchSnapshots(projectId: number, since: string): Promise<Snapshot[]> {
const results = await db.fetchSnapshots(projectId, since)

return transformer.snapshots(results)
}

async saveSnapshots(snapshots: TemporarySnapshot[], projectName: string): Promise<Snapshot[]> {
// TODO: implement saving into database
// TODO: figure out if we want some type of unique-ness or want to add indexes
return [];
}
}
63 changes: 63 additions & 0 deletions packages/@best/api-db/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import crypto from 'crypto';
import PostgresDB from './postgres';
import { TemporarySnapshot } from './types';

function md5(data: string) {
return crypto
.createHash('md5')
.update(data)
.digest('hex');
}

export default (benchmarkResults: any, globalConfig: any) => {
const db = new PostgresDB();
return Promise.all(
benchmarkResults.map(async (benchmarkResult: any) => {
const { benchmarkSignature, projectConfig, environment, stats } = benchmarkResult;
const { projectName } = projectConfig;
const { gitCommit, gitLocalChanges } = globalConfig;

const snapshotEnvironment = {
hardware: environment.hardware,
browser: environment.browser
}

const environmentHash = md5(JSON.stringify(snapshotEnvironment));

const runSettings = {
similarityHash: benchmarkSignature,
commit: gitCommit,
// TODO: get commit date from github API
commitDate: (new Date()).toISOString(),
environmentHash,
// TODO: not sure if this is exactly what we want to determine here
temporary: !gitLocalChanges
}

const snapshotsToSave: TemporarySnapshot[] = [];

stats.benchmarks.forEach((element: any) => {
element.benchmarks.forEach((bench: any) => {
const metricKeys = Object.keys(bench).filter(key => key !== 'name')
const metrics = metricKeys.map(name => ({
name,
duration: bench[name].median,
stdDeviation: bench[name].medianAbsoluteDeviation,
}))

const snapshot = {
...runSettings,
name: `${element.name}/${bench.name}`,
metrics: metrics
}
snapshotsToSave.push(snapshot);

});
});

const snapshots = await db.saveSnapshots(snapshotsToSave, projectName);
console.log(snapshots);

}),
);
}
36 changes: 36 additions & 0 deletions packages/@best/api-db/src/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { QueryResult } from 'pg';
import { Project, Snapshot, Metric } from './types';

const normalizeMetrics = (metrics: any): Metric[] => {
return Object.keys(metrics).map((key): Metric => ({
name: key,
duration: metrics[key][0],
stdDeviation: metrics[key][1]
}));
};

export default {
projects: (query: QueryResult): Project[] => {
return query.rows.map((row): Project => ({
id: row.id,
name: row.name,
createdAt: row.created_at,
lastReleaseDate: row.last_release_date
}));
},
snapshots: (query: QueryResult): Snapshot[] => {
return query.rows.map((row): Snapshot => ({
id: row.id,
projectId: row.project_id,
name: row.name,
metrics: normalizeMetrics(JSON.parse(row.metrics)),
environmentHash: row.environment_hash,
similarityHash: row.similarity_hash,
commit: row.commit,
commitDate: row.commit_date,
temporary: row.temporary,
createdAt: row.created_at,
updatedAt: row.updated_at
}));
}
};
35 changes: 35 additions & 0 deletions packages/@best/api-db/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export interface Project {
id: number;
name: string;
createdAt: string;
lastReleaseDate: string;
}

export interface Metric {
name: string;
duration: number;
stdDeviation: number;
}

export interface TemporarySnapshot {
name: string;
metrics: Metric[];
environmentHash: string;
similarityHash: string;
commit: string;
commitDate: string;
temporary: boolean;
}

export interface Snapshot extends TemporarySnapshot {
id: number;
projectId: number;
createdAt: string;
updatedAt: string;
}

export abstract class ApiDB {
abstract fetchProjects(): Promise<Project[]>;
abstract fetchSnapshots(projectId: number, since: string): Promise<Snapshot[]>;
abstract saveSnapshots(snapshots: TemporarySnapshot[], projectName: string): Promise<Snapshot[]>;
}
8 changes: 8 additions & 0 deletions packages/@best/api-db/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.settings.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "build",
}
}

1 change: 1 addition & 0 deletions packages/@best/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"main": "build/index.js",
"dependencies": {
"@best/analyzer": "0.7.1",
"@best/api-db": "0.7.1",
"@best/build": "0.7.1",
"@best/compare": "0.7.1",
"@best/config": "0.7.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/@best/cli/src/run_best.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { buildBenchmarks } from '@best/builder';
import { runBenchmarks } from '@best/runner';
import { BuildStateMessager, RunnerMessager } from '@best/messager';
import { storeBenchmarkResults } from '@best/store';
import { saveBenchmarkSummaryInDB } from '@best/api-db';
import { analyzeBenchmarks } from '@best/analyzer';
import path from 'path';
import micromatch from 'micromatch';
Expand Down Expand Up @@ -108,6 +109,7 @@ export async function runBest(globalConfig: any, configs: any, outputStream: any

await analyzeBenchmarks(benchmarkBundleResults);
await storeBenchmarkResults(benchmarkBundleResults, globalConfig);
await saveBenchmarkSummaryInDB(benchmarkBundleResults, globalConfig);

return benchmarkBundleResults;
}
1 change: 1 addition & 0 deletions packages/@best/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
"references": [
{ "path": "../analyzer" },
{ "path": "../api-db" },
{ "path": "../builder" },
{ "path": "../compare" },
{ "path": "../config" },
Expand Down
14 changes: 14 additions & 0 deletions packages/@best/frontend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["@salesforce/eslint-config-lwc/recommended", "plugin:@typescript-eslint/recommended", "plugin:eslint-comments/recommended"],
"rules": {
"@lwc/lwc/no-async-operation": "warn",
"@lwc/lwc/no-inner-html": "warn",
"@lwc/lwc/no-document-query": "warn",
"@typescript-eslint/camelcase": "off"
},
"parserOptions": {
"ecmaVersion": 2018
}
}
Loading