Skip to content

Commit faa269d

Browse files
author
Michael Mrowetz
committed
#155 refactor to remove rawRessource
1 parent 612e940 commit faa269d

File tree

12 files changed

+124
-105
lines changed

12 files changed

+124
-105
lines changed

src/ts/helpers/heuristics.ts

-12
This file was deleted.

src/ts/helpers/misc.ts

+11
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,14 @@ export function resourceUrlFormatter(url: string, maxLength: number): string {
6464
export function roundNumber(num: number, decimals: number = 2) {
6565
return Math.round(num * Math.pow(10, decimals)) / Math.pow(10, decimals);
6666
}
67+
68+
/**
69+
*
70+
* Checks if `status` code is `>= lowerBound` and `<= upperBound`
71+
* @param {number} entry
72+
* @param {number} lowerBound - inclusive lower bound
73+
* @param {number} upperBound - inclusive upper bound
74+
*/
75+
export function isInStatusCodeRange(status: number, lowerBound: number, upperBound: number) {
76+
return status >= lowerBound && status <= upperBound;
77+
}

src/ts/helpers/parse.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ const htmlCharMap = {
125125
const htmlChars = new RegExp(Object.keys(htmlCharMap).join("|"), "g");
126126

127127
/**
128-
* Escapes unsafe characters is a string to render safely in HTML
128+
* Escapes unsafe characters in a string to render safely in HTML
129129
* @param {string} unsafe - string to be rendered in HTML
130130
*/
131-
export function escapeHtml(unsafe: string | number | boolean = "") {
131+
export function escapeHtml(unsafe: string | number | boolean = ""): string {
132132
if (typeof unsafe !== "string") {
133133
if (typeof unsafe["toString"] === "function") {
134134
unsafe = unsafe.toString();

src/ts/transformers/har-heuristics.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44

55
import { hasHeader } from "../helpers/har";
6-
import { isInStatusCodeRange } from "../helpers/heuristics";
76
import * as misc from "../helpers/misc";
87
import { Entry } from "../typing/har";
98
import { WaterfallEntryIndicator } from "../typing/waterfall";
@@ -44,7 +43,7 @@ function hasCacheIssue(entry: Entry) {
4443
if (entry.request.method.toLowerCase() !== "get") {
4544
return false;
4645
}
47-
if (entry.response.status === 204 || !isInStatusCodeRange(entry, 200, 299)) {
46+
if (entry.response.status === 204 || !misc.isInStatusCodeRange(entry.response.status, 200, 299)) {
4847
return false;
4948
}
5049

@@ -126,7 +125,7 @@ export function collectIndicators(entry: Entry, docIsTLS: boolean, requestType:
126125
}
127126

128127
if (!entry.response.content.mimeType &&
129-
isInStatusCodeRange(entry, 200, 299) &&
128+
misc.isInStatusCodeRange(entry.response.status, 200, 299) &&
130129
entry.response.status !== 204) {
131130
output.push({
132131
description: "Response doesn't contain a 'Content-Type' header.",

src/ts/transformers/har.ts

+56-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import { roundNumber } from "../helpers/misc";
1+
import { isInStatusCodeRange, roundNumber } from "../helpers/misc";
22
import { toInt } from "../helpers/parse";
33
import { Entry, Har, PageTimings } from "../typing/har";
44
import {
5+
Icon,
56
Mark,
6-
RequestType,
77
TimingType,
88
WaterfallData,
99
WaterfallDocs,
1010
WaterfallEntry,
1111
WaterfallEntryIndicator,
1212
WaterfallEntryTab,
1313
WaterfallEntryTiming,
14+
WaterfallResponseDetails,
1415
} from "../typing/waterfall";
16+
import { makeIcon } from "../waterfall/row/svg-indicators";
1517
import { collectIndicators, documentIsSecure } from "./har-heuristics";
1618
import { makeTabs } from "./har-tabs";
1719
import { mimeToRequestType } from "./helpers";
@@ -20,9 +22,7 @@ function createWaterfallEntry(url: string,
2022
start: number,
2123
end: number,
2224
segments: WaterfallEntryTiming[] = [],
23-
rawResource: Entry,
24-
requestType: RequestType,
25-
indicators: WaterfallEntryIndicator[],
25+
responseDetails: WaterfallResponseDetails,
2626
tabs: WaterfallEntryTab[]): WaterfallEntry {
2727
const total = (typeof start !== "number" || typeof end !== "number") ? undefined : (end - start);
2828
return {
@@ -31,9 +31,7 @@ function createWaterfallEntry(url: string,
3131
start,
3232
end,
3333
segments,
34-
rawResource,
35-
requestType,
36-
indicators,
34+
responseDetails,
3735
tabs,
3836
};
3937
}
@@ -77,13 +75,12 @@ function toWaterFallEntry(entry: Entry, index: number, startRelative: number, is
7775
const endRelative = toInt(entry._all_end) || (startRelative + entry.time);
7876
const requestType = mimeToRequestType(entry.response.content.mimeType);
7977
const indicators = collectIndicators(entry, isTLS, requestType);
78+
const responseDetails = createResponseDetails(entry, indicators);
8079
return createWaterfallEntry(entry.request.url,
8180
startRelative,
8281
endRelative,
8382
buildDetailTimingBlocks(startRelative, entry),
84-
entry,
85-
requestType,
86-
indicators,
83+
responseDetails,
8784
makeTabs(entry, (index + 1), requestType, startRelative, endRelative, indicators),
8885
);
8986
}
@@ -199,3 +196,51 @@ function getTimePair(key: string, harEntry: Entry, collect: WaterfallEntryTiming
199196
"start": start,
200197
};
201198
}
199+
200+
function createResponseDetails(entry: Entry, indicators: WaterfallEntryIndicator[]): WaterfallResponseDetails {
201+
const requestType = mimeToRequestType(entry.response.content.mimeType);
202+
203+
return {
204+
icon: getMimeTypeIcon(entry, requestType),
205+
rowClass: getRowCssClasses(entry),
206+
indicators,
207+
requestType,
208+
statusCode: entry.response.status,
209+
};
210+
}
211+
212+
/**
213+
* Scan the request for errors or potential issues and highlight them
214+
* @param {Entry} entry
215+
* @returns {Icon}
216+
*/
217+
function getMimeTypeIcon(entry: Entry, requestType): Icon {
218+
const status = entry.response.status;
219+
// highlight redirects
220+
if (!!entry.response.redirectURL) {
221+
const url = encodeURI(entry.response.redirectURL.split("?")[0] || "");
222+
return makeIcon("err3xx", `${status} response status: Redirect to ${url}...`);
223+
} else if (isInStatusCodeRange(status, 400, 499)) {
224+
return makeIcon("err4xx", `${status} response status: ${entry.response.statusText}`);
225+
} else if (isInStatusCodeRange(status, 500, 599)) {
226+
return makeIcon("err5xx", `${status} response status: ${entry.response.statusText}`);
227+
} else if (status === 204) {
228+
return makeIcon("plain", "No content");
229+
} else {
230+
return makeIcon(requestType, requestType);
231+
}
232+
}
233+
234+
function getRowCssClasses(entry: Entry): string {
235+
const classes = ["row-item"];
236+
if (isInStatusCodeRange(entry.response.status, 500, 599)) {
237+
classes.push("status5xx");
238+
} else if (isInStatusCodeRange(entry.response.status, 400, 499)) {
239+
classes.push("status4xx");
240+
} else if (entry.response.status !== 304 &&
241+
isInStatusCodeRange(entry.response.status, 300, 399)) {
242+
// 304 == Not Modified, so not an issue
243+
classes.push("status3xx");
244+
}
245+
return classes.join(" ");
246+
}

src/ts/typing/waterfall.ts

+28-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import {Entry} from "./har";
2-
31
export type TimingType = "blocked" | "dns" | "connect" | "send" | "wait" | "receive" | "ssl";
42
export type RequestType = "other" | "image" | "video" | "audio" | "font" | "svg" | "html" |
53
"plain" | "css" | "javascript" | "flash";
@@ -28,12 +26,8 @@ export interface WaterfallEntry {
2826
end: number;
2927
/** time segments (dns, tls/ssl, connect...) */
3028
segments: WaterfallEntryTiming[];
31-
/** raw e.g. HAR entry */
32-
rawResource: Entry;
33-
/** media type category */
34-
requestType: RequestType;
35-
/** Warnings, Errors and Info indicators */
36-
indicators: WaterfallEntryIndicator[];
29+
/** Resource-type icon */
30+
responseDetails: WaterfallResponseDetails;
3731
/** Tabs to render in the details-overlay view */
3832
tabs: WaterfallEntryTab[];
3933
}
@@ -79,6 +73,19 @@ export interface WaterfallEntryTiming {
7973
end: number;
8074
}
8175

76+
export interface WaterfallResponseDetails {
77+
/** HTTP response status code */
78+
statusCode: number;
79+
/** Response Type Icon (e.g. Mime type) */
80+
icon: Icon;
81+
/** Warnings, Errors and Info indicators */
82+
indicators: WaterfallEntryIndicator[];
83+
/** CSS class to use for row */
84+
rowClass?: string;
85+
/** media type category */
86+
requestType: RequestType;
87+
}
88+
8289
export interface WaterfallData {
8390
title: string;
8491
durationMs: number;
@@ -92,3 +99,16 @@ export interface WaterfallData {
9299
export interface WaterfallDocs {
93100
pages: WaterfallData[];
94101
}
102+
103+
/**
104+
* Interface for `Icon` metadata
105+
* (e.g. warning and response type icons)
106+
*/
107+
export interface Icon {
108+
/** Icon types (as in `helpers/icons.ts`) */
109+
type: string;
110+
/** title for icon */
111+
title: string;
112+
/** width of icon in px */
113+
width: number;
114+
}

src/ts/waterfall/details-overlay/html-details-body.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function createDetailsBody(requestID: number, detailsHeight: number, entr
3131

3232
body.innerHTML = `
3333
<div class="wrapper">
34-
<header class="type-${entry.requestType}">
34+
<header class="type-${entry.responseDetails.requestType}">
3535
<h3><strong>#${requestID}</strong> <a href="${entry.url}">${entry.url}</a></h3>
3636
<nav class="tab-nav">
3737
<ul>

src/ts/waterfall/details-overlay/svg-details-overlay.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function createHolder(y: number, detailsHeight: number) {
4646
}
4747

4848
export function createRowInfoOverlay(overlay: OpenOverlay, y: number, detailsHeight: number): SVGGElement {
49-
const requestID = overlay.entry.rawResource._number || overlay.index + 1;
49+
const requestID = overlay.index + 1;
5050
let wrapper = svg.newG("outer-info-overlay-holder");
5151
let holder = createHolder(y, detailsHeight);
5252

src/ts/waterfall/row/svg-indicators.ts

+13-40
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,33 @@
1-
/**
2-
* Creation of sub-components used in a resource request row
3-
*/
4-
5-
import { isInStatusCodeRange } from "../../helpers/heuristics";
6-
import { WaterfallEntry } from "../../typing/waterfall";
1+
import {
2+
Icon,
3+
WaterfallEntry,
4+
} from "../../typing/waterfall";
75

86
/**
9-
* Interface for `Icon` metadata
7+
* Convinience helper to create a new `Icon`
8+
*
9+
* _Width of icons is fixed_
1010
*/
11-
export interface Icon {
12-
type: string;
13-
title: string;
14-
width: number;
15-
}
16-
17-
// helper to avoid typing out all key of the helper object
18-
function makeIcon(type: string, title: string): Icon {
11+
export function makeIcon(type: string, title: string): Icon {
1912
return { "type": type, "title": title, "width": 20 };
2013
}
2114

22-
/**
23-
* Scan the request for errors or potential issues and highlight them
24-
* @param {WaterfallEntry} entry
25-
* @returns {Icon}
26-
*/
27-
export function getMimeTypeIcon(entry: WaterfallEntry): Icon {
28-
const harEntry = entry.rawResource;
29-
// highlight redirects
30-
if (!!harEntry.response.redirectURL) {
31-
const url = encodeURI(harEntry.response.redirectURL.split("?")[0] || "");
32-
return makeIcon("err3xx", `${harEntry.response.status} response status: Redirect to ${url}...`);
33-
} else if (isInStatusCodeRange(harEntry, 400, 499)) {
34-
return makeIcon("err4xx", `${harEntry.response.status} response status: ${harEntry.response.statusText}`);
35-
} else if (isInStatusCodeRange(harEntry, 500, 599)) {
36-
return makeIcon("err5xx", `${harEntry.response.status} response status: ${harEntry.response.statusText}`);
37-
} else if (harEntry.response.status === 204) {
38-
return makeIcon("plain", "No content");
39-
} else {
40-
return makeIcon(entry.requestType, entry.requestType);
41-
}
42-
}
4315
/**
4416
* Gets the Indicators in Icon format
4517
* @param {WaterfallEntry} entry
4618
* @returns {Icon[]}
4719
*/
4820
export function getIndicatorIcons(entry: WaterfallEntry): Icon[] {
49-
if (entry.indicators.length === 0) {
21+
const indicators = entry.responseDetails.indicators;
22+
if (indicators.length === 0) {
5023
return [];
5124
}
5225

5326
let combinedTitle = [];
5427
let icon = "";
55-
const errors = entry.indicators.filter((i) => i.type === "error");
56-
const warnings = entry.indicators.filter((i) => i.type === "warning");
57-
const info = entry.indicators.filter((i) => i.type !== "error" && i.type !== "warning");
28+
const errors = indicators.filter((i) => i.type === "error");
29+
const warnings = indicators.filter((i) => i.type === "warning");
30+
const info = indicators.filter((i) => i.type !== "error" && i.type !== "warning");
5831

5932
if (errors.length > 0) {
6033
combinedTitle.push(`Error${errors.length > 1 ? "s" : ""}:\n${errors.map((e) => e.title).join("\n")}`);

src/ts/waterfall/row/svg-row.ts

+6-22
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { isInStatusCodeRange } from "../../helpers/heuristics";
21
import * as icons from "../../helpers/icons";
32
import * as misc from "../../helpers/misc";
43
import * as svg from "../../helpers/svg";
54
import { Context } from "../../typing/context";
6-
import {Entry} from "../../typing/har";
75
import { RectData } from "../../typing/rect-data";
8-
import {WaterfallEntry} from "../../typing/waterfall";
9-
import * as indicators from "./svg-indicators";
6+
import { WaterfallEntry } from "../../typing/waterfall";
7+
import { getIndicatorIcons } from "./svg-indicators";
108
import * as rowSubComponents from "./svg-row-subcomponents";
119

1210
// initial clip path
@@ -16,20 +14,6 @@ clipPathElProto.appendChild(svg.newRect({
1614
"width": "100%",
1715
}));
1816

19-
function getRowCssClasses(harEntry: Entry): string {
20-
const classes = ["row-item"];
21-
if (isInStatusCodeRange(harEntry, 500, 599)) {
22-
classes.push("status5xx");
23-
} else if (isInStatusCodeRange(harEntry, 400, 499)) {
24-
classes.push("status4xx");
25-
} else if (harEntry.response.status !== 304 &&
26-
isInStatusCodeRange(harEntry, 300, 399)) {
27-
// 304 == Not Modified, so not an issue
28-
classes.push("status3xx");
29-
}
30-
return classes.join(" ");
31-
}
32-
3317
const ROW_LEFT_MARGIN = 3;
3418

3519
// Create row for a single request
@@ -41,7 +25,7 @@ export function createRow(context: Context, index: number,
4125
const y = rectData.y;
4226
const rowHeight = rectData.height;
4327
const leftColumnWith = context.options.leftColumnWith;
44-
let rowItem = svg.newG(getRowCssClasses(entry.rawResource));
28+
let rowItem = svg.newG(entry.responseDetails.rowClass);
4529
let leftFixedHolder = svg.newSvg("left-fixed-holder", {
4630
"width": `${leftColumnWith}%`,
4731
"x": "0",
@@ -59,14 +43,14 @@ export function createRow(context: Context, index: number,
5943
let x = ROW_LEFT_MARGIN + maxIconsWidth;
6044

6145
if (context.options.showMimeTypeIcon) {
62-
const icon = indicators.getMimeTypeIcon(entry);
46+
const icon = entry.responseDetails.icon;
6347
x -= icon.width;
6448
rowName.appendChild(icons[icon.type](x, y + 3, icon.title));
6549
}
6650

6751
if (context.options.showIndicatorIcons) {
68-
// Create and add warnings for potential issues
69-
indicators.getIndicatorIcons(entry).forEach((icon: indicators.Icon) => {
52+
// Create and add warnings for potentia;l issues
53+
getIndicatorIcons(entry).forEach((icon) => {
7054
x -= icon.width;
7155
rowName.appendChild(icons[icon.type](x, y + 3, icon.title));
7256
});

0 commit comments

Comments
 (0)