Skip to content

Commit 97b7478

Browse files
committed
refactor extra_arg parsing into QueryReader
1 parent 193e519 commit 97b7478

File tree

7 files changed

+153
-89
lines changed

7 files changed

+153
-89
lines changed

.eslintrc.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
"commonjs": true,
44
"es6": true
55
},
6-
"extends": ["airbnb-base", "plugin:import/typescript", "plugin:@typescript-eslint/recommended", "plugin:jsdoc/recommended"],
6+
"extends": [
7+
"airbnb-base",
8+
"plugin:import/typescript",
9+
"plugin:@typescript-eslint/recommended",
10+
"plugin:jsdoc/recommended"
11+
],
712
"parser": "@typescript-eslint/parser",
8-
"plugins": ["jsdoc"],
913
"rules": {
1014
"import/extensions": 0,
1115
"jsdoc/no-undefined-types": 0,

lib/compare/compare.ts

+57-52
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
/* eslint-disable camelcase */
2-
import * as queryString from 'query-string';
32
import * as Bluebird from 'bluebird';
43

54
import * as jsondiffpatch from 'jsondiffpatch';
65
import { AxiosResponse } from 'axios';
76
import { ApiEnv, argvToApiEnv } from '../apiEnv';
8-
import runQuery from '../run-query';
7+
import runQuery, { AxiosResponseWithDuration } from '../run-query';
98
import { Change } from './change';
109
import { CompareFormatter } from './formatters/compare-formatter';
1110
import {
@@ -15,12 +14,16 @@ import getFormatter from './formatters/get-formatter';
1514
import QueryReader from './query-reader';
1615
import { Query } from './query';
1716

17+
type CompareArgs = Pick<ParsedArgs, 'concurrency' | 'ignored_fields' | 'extra_params' | 'timeout'>;
18+
1819
/**
19-
* @param root0
20-
* @param root0.oldApiEnv
21-
* @param root0.newApiEnv
22-
* @param root0.query
23-
* @param root0.argv
20+
* Compare one query against two ApiEnvs, returning a Change object
21+
*
22+
* @param {ApiEnv} oldApiEnv old env to run query against
23+
* @param {ApiEnv} newApiEnv new env to run query against
24+
* @param {Query} query query to compare
25+
* @param {CompareArgs} argv global command line args that affect running queries
26+
* @returns {Promise<Change>} output of running query against both envs
2427
*/
2528
async function compareQuery({
2629
oldApiEnv,
@@ -31,23 +34,14 @@ async function compareQuery({
3134
oldApiEnv: ApiEnv;
3235
newApiEnv: ApiEnv;
3336
query: Query;
34-
argv: ParsedArgs;
37+
argv: CompareArgs;
3538
}): Promise<Change> {
36-
const extraParams = queryString.parse(argv.extra_params.join('&')) as Record<string, string>;
37-
delete extraParams.undefined;
38-
const params = { ...query.params, ...extraParams };
39-
const queryWithExtraParams = {
40-
endpoint: query.endpoint,
41-
method: query.method,
42-
params,
43-
};
44-
4539
// if the query has a baseline response (running from a golden json file), use that
4640
// otherwise run it against the old server
4741
const oldResponse = query.baselineResponse
48-
? ({ data: query.baselineResponse } as AxiosResponse<any>)
49-
: await runQuery(oldApiEnv, queryWithExtraParams, argv.timeout);
50-
const newResponse = await runQuery(newApiEnv, queryWithExtraParams, argv.timeout);
42+
? ({ data: query.baselineResponse } as AxiosResponse<unknown>)
43+
: await runQuery(oldApiEnv, query, argv.timeout);
44+
const newResponse = await runQuery(newApiEnv, query, argv.timeout);
5145

5246
const differ = jsondiffpatch.create({
5347
propertyFilter(name) {
@@ -58,20 +52,23 @@ async function compareQuery({
5852
const delta = differ.diff(oldResponse.data, newResponse.data);
5953

6054
return {
61-
query: queryWithExtraParams,
55+
query,
6256
delta,
6357
oldResponse,
6458
newResponse,
6559
};
6660
}
6761

6862
/**
69-
* @param root0
70-
* @param root0.oldApiEnv
71-
* @param root0.newApiEnv
72-
* @param root0.queries
73-
* @param root0.argv
74-
* @param root0.formatter
63+
* Compare and output many queries against two ApiEnvs. Passing along
64+
* the changes for output to a ChangeFormatter.
65+
*
66+
* @param {ApiEnv} oldApiEnv old env to run query against
67+
* @param {ApiEnv} newApiEnv new env to run query against
68+
* @param {Query[]} queries queries to compare
69+
* @param {CompareArgs} argv global command line args that affect running queries
70+
* @param {CompareFormatter} formatter formatter to use for output
71+
* @returns {Promise<void>} side effect only - outputs to output_file or stdout
7572
*/
7673
async function compareQueries({
7774
oldApiEnv,
@@ -83,9 +80,9 @@ async function compareQueries({
8380
oldApiEnv: ApiEnv;
8481
newApiEnv: ApiEnv;
8582
queries: Query[];
86-
argv: ParsedArgs;
83+
argv: CompareArgs;
8784
formatter: CompareFormatter;
88-
}) {
85+
}): Promise<void> {
8986
const oldResponseTimes: number[] = [];
9087
const newResponseTimes: number[] = [];
9188
const oldStatusCodes: Record<number, number> = {};
@@ -100,12 +97,11 @@ async function compareQueries({
10097
query,
10198
argv,
10299
});
103-
formatter.queryRan();
104-
if (change.delta || argv.unchanged) {
105-
formatter.logChange(change);
106-
}
107-
oldResponseTimes.push((change.oldResponse as any).duration);
108-
newResponseTimes.push((change.newResponse as any).duration);
100+
101+
formatter.queryCompleted(change);
102+
103+
oldResponseTimes.push((change.oldResponse as AxiosResponseWithDuration).duration);
104+
newResponseTimes.push((change.newResponse as AxiosResponseWithDuration).duration);
109105

110106
if (!oldStatusCodes[change.oldResponse.status]) {
111107
oldStatusCodes[change.oldResponse.status] = 0;
@@ -131,24 +127,33 @@ async function compareQueries({
131127
});
132128
}
133129

134-
const argv = parseArgv([OLD_KEY, NEW_KEY]) as ParsedArgs;
130+
/** Main logic - parses command line, creates a formatter, runs queries and
131+
* generates output.
132+
*
133+
* @returns {Promise<void>} on completion
134+
*/
135+
function main(): Promise<void> {
136+
const argv = parseArgv([OLD_KEY, NEW_KEY]) as ParsedArgs;
135137

136-
const oldApiEnv = argvToApiEnv(argv[OLD_KEY]);
137-
const newApiEnv = argvToApiEnv(argv[NEW_KEY]);
138+
const oldApiEnv = argvToApiEnv(argv[OLD_KEY]);
139+
const newApiEnv = argvToApiEnv(argv[NEW_KEY]);
138140

139-
const queries = QueryReader(argv);
141+
const queries = QueryReader(argv);
140142

141-
const formatter = getFormatter(argv.output_mode, {
142-
oldApiEnv,
143-
newApiEnv,
144-
argv,
145-
totalQueries: queries.length,
146-
});
143+
const formatter = getFormatter(argv.output_mode, {
144+
oldApiEnv,
145+
newApiEnv,
146+
argv,
147+
totalQueries: queries.length,
148+
});
147149

148-
compareQueries({
149-
oldApiEnv,
150-
newApiEnv,
151-
queries,
152-
argv,
153-
formatter,
154-
});
150+
return compareQueries({
151+
oldApiEnv,
152+
newApiEnv,
153+
queries,
154+
argv,
155+
formatter,
156+
});
157+
}
158+
159+
main();

lib/compare/formatters/compare-formatter.ts

+47-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
/* eslint-disable no-console */
12
import * as fs from 'fs';
23
import * as stats from 'stats-lite';
34
import { Change } from '../change';
45
import { ApiEnv } from '../../apiEnv';
56
import { ParsedArgs } from '../argv';
67

7-
export type FormatterArgv = Pick<ParsedArgs, 'output_mode' | 'output_file'>;
8+
export type FormatterArgv = Pick<ParsedArgs, 'output_mode' | 'output_file' | 'unchanged'>;
89

910
export type FormatterConstructorParams = {
1011
oldApiEnv: ApiEnv;
@@ -19,7 +20,7 @@ export type PerHostFinishedStats = {
1920
}
2021

2122
export type FinishedStats = {
22-
new: PerHostFinishedStats,
23+
new?: PerHostFinishedStats,
2324
old: PerHostFinishedStats
2425
}
2526

@@ -40,14 +41,6 @@ export function makeResponseTimesHistogram(responseTimes: number[]): Record<stri
4041
}
4142

4243
export abstract class CompareFormatter {
43-
totalQueries: number;
44-
45-
abstract logChange(change: Change): void;
46-
47-
abstract queryRan(): void;
48-
49-
abstract finished(stats: FinishedStats): void;
50-
5144
oldApiEnv: ApiEnv;
5245

5346
newApiEnv: ApiEnv;
@@ -56,10 +49,53 @@ export abstract class CompareFormatter {
5649

5750
outputStream: fs.WriteStream;
5851

52+
showUnchanged: boolean;
53+
54+
totalQueries = 0;
55+
56+
numQueriesRun = 0;
57+
58+
/** Called when a query has actually changed */
59+
abstract logChange(change: Change): void;
60+
61+
/**
62+
* Called each time a query completes. Change is a bit of a misnomer.
63+
* This is called even when there was no diff. It's up to the formatter
64+
* what to do with it. Calls the formatter implementation of logChange if
65+
* there is a diff or --unchanged is specified
66+
*
67+
* @param {Change} change query + old response + new response to log
68+
*/
69+
queryCompleted(change: Change): void {
70+
this.numQueriesRun += 1;
71+
if (this.numQueriesRun % 10 === 0) {
72+
console.error(`IN PROGRESS. ${this.numQueriesRun}/${this.totalQueries} run`);
73+
}
74+
75+
if (!change.delta && this.showUnchanged) {
76+
return;
77+
}
78+
79+
this.logChange(change);
80+
}
81+
82+
/** Called when all queries are finished running */
83+
abstract finished(stats: FinishedStats): void;
84+
85+
/**
86+
* Helper to deal with output redirection
87+
*
88+
* @param {string} s string to output with newline
89+
*/
5990
writeln(s: string): void {
6091
this.write(`${s}\n`);
6192
}
6293

94+
/**
95+
* Helper to deal with output redirection
96+
*
97+
* @param {string} s string to output
98+
*/
6399
write(s: string): void {
64100
if (this.outputStream) {
65101
this.outputStream.write(s);
@@ -75,6 +111,7 @@ export abstract class CompareFormatter {
75111
this.newApiEnv = newApiEnv;
76112
this.totalQueries = totalQueries;
77113
this.startDate = new Date();
114+
this.showUnchanged = argv.unchanged;
78115

79116
const outputFilename = argv.output_file;
80117
if (outputFilename && outputFilename !== '-') {

lib/compare/formatters/json-formatter.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@ export default class JsonFormatter extends CompareFormatter {
2525

2626
changes: JsonChange[] = [];
2727

28-
logChange(change: Change): void {
28+
queryCompleted(change: Change): void {
29+
this.numQueriesRun += 1;
30+
if (this.numQueriesRun % 10 === 0) {
31+
console.error(`IN PROGRESS. ${this.numQueriesRun}/${this.totalQueries} run`);
32+
}
33+
34+
if (!change.delta && this.showUnchanged) {
35+
return;
36+
}
2937
this.numQueriesChanged += 1;
3038

3139
this.changes.push({
@@ -47,13 +55,6 @@ export default class JsonFormatter extends CompareFormatter {
4755
});
4856
}
4957

50-
queryRan(): void {
51-
this.numQueriesRun += 1;
52-
if (this.numQueriesRun % 10 === 0) {
53-
console.error(`IN PROGRESS. ${this.numQueriesRun}/${this.totalQueries} run`);
54-
}
55-
}
56-
5758
/**
5859
* Output the overall json object. Broken out here so html formatter can call it
5960
*

lib/compare/generate-baseline.ts

+26-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
/**
2+
* Generate baseline json to be used in future compare runs.
3+
*
4+
* This file is mostly a copy of compare.ts, which I don't love.
5+
* compare.ts is designed to work by comparing two servers, this
6+
* script only runs against one server and saves the output to a json file.
7+
*
8+
* Adding more clauses to compare.ts seemed ugly, but I will probably
9+
* attempt it at some point
10+
*/
111
/* eslint-disable camelcase */
2-
import * as queryString from 'query-string';
312
import * as Bluebird from 'bluebird';
413

514
import { ApiEnv, argvToApiEnv } from '../apiEnv';
@@ -29,19 +38,10 @@ async function runOneQuery({
2938
query: Query;
3039
argv: ParsedArgs;
3140
}): Promise<Change> {
32-
const extraParams = queryString.parse(argv.extra_params.join('&')) as Record<string, string>;
33-
delete extraParams.undefined;
34-
const params = { ...query.params, ...extraParams };
35-
const queryWithExtraParams = {
36-
endpoint: query.endpoint,
37-
method: query.method,
38-
params,
39-
};
40-
41-
const oldResponse = await runQuery(oldApiEnv, queryWithExtraParams, argv.timeout);
41+
const oldResponse = await runQuery(oldApiEnv, query, argv.timeout);
4242

4343
return {
44-
query: queryWithExtraParams,
44+
query,
4545
delta: undefined,
4646
oldResponse,
4747
newResponse: undefined,
@@ -68,6 +68,7 @@ async function runQueries({
6868
formatter: CompareFormatter
6969
}) {
7070
const oldResponseTimes: number[] = [];
71+
const oldStatusCodes: Record<number, number> = {};
7172

7273
await Bluebird.map(
7374
queries,
@@ -77,13 +78,23 @@ async function runQueries({
7778
query,
7879
argv,
7980
});
80-
formatter.queryRan();
81-
formatter.logChange(change);
81+
formatter.queryCompleted(change);
8282
oldResponseTimes.push((change.oldResponse as any).duration);
83+
84+
if (!oldStatusCodes[change.oldResponse.status]) {
85+
oldStatusCodes[change.oldResponse.status] = 0;
86+
}
87+
88+
oldStatusCodes[change.oldResponse.status] += 1;
8389
},
8490
{ concurrency: argv.concurrency },
8591
);
86-
formatter.finished({ oldResponseTimes, newResponseTimes: undefined });
92+
formatter.finished({
93+
old: {
94+
responseTimes: oldResponseTimes,
95+
statusCodes: oldStatusCodes,
96+
},
97+
});
8798
}
8899

89100
const argv = parseArgv([OLD_KEY]) as ParsedArgs;

0 commit comments

Comments
 (0)