/
Utils.ts
159 lines (148 loc) · 5.62 KB
/
Utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Core
*/
import { parse as parseVersion } from "semver";
import { Element, IModelDb } from "@itwin/core-backend";
import { DbResult, Id64String } from "@itwin/core-bentley";
import {
combineDiagnosticsSeverities,
compareDiagnosticsSeverities,
Diagnostics,
DiagnosticsLogEntry,
DiagnosticsOptions,
InstanceKey,
} from "@itwin/presentation-common";
const presentation = require("@itwin/presentation-common/lib/cjs/assets/locales/en/Presentation.json"); // eslint-disable-line @typescript-eslint/no-var-requires
/** @internal */
export function getLocalizedStringEN(key: string) {
let result = presentation;
const [namespace, identifier] = key.split(":", 2);
if (namespace !== "Presentation") {
return key;
}
const keySteps = identifier.split(".");
for (const keyStep of keySteps) {
if (keyStep in result === false) {
return key;
}
result = result[keyStep];
}
return typeof result === "string" ? result : key;
}
/** @internal */
export function getElementKey(imodel: IModelDb, id: Id64String): InstanceKey | undefined {
let key: InstanceKey | undefined;
const query = `SELECT ECClassId FROM ${Element.classFullName} e WHERE ECInstanceId = ?`;
imodel.withPreparedStatement(query, (stmt) => {
try {
stmt.bindId(1, id);
if (stmt.step() === DbResult.BE_SQLITE_ROW) {
key = { className: stmt.getValue(0).getClassNameForClassId().replace(".", ":"), id };
}
} catch {}
});
return key;
}
/** @internal */
export function normalizeVersion(version?: string) {
if (version) {
const parsedVersion = parseVersion(version, true);
if (parsedVersion) {
return `${parsedVersion.major}.${parsedVersion.minor}.${parsedVersion.patch}`;
}
}
return "0.0.0";
}
/**
* A function that received request diagnostics and, optionally, request context.
* @beta
*/
export type BackendDiagnosticsHandler<TContext = any> = (logs: Diagnostics, requestContext?: TContext) => void;
/**
* Data structure for backend diagnostics options.
* @beta
*/
export interface BackendDiagnosticsOptions<TContext = any> extends DiagnosticsOptions {
/**
* An optional function to supply request context that'll be passed to [[handler]] when
* it's called after the request is fulfilled.
*/
requestContextSupplier?: () => TContext;
/**
* Request diagnostics handler function that is called after the request is fulfilled. The handler
* receives request diagnostics as the first argument and, optionally, request context as the
* second (see [[requestContextSupplier]]).
*/
handler: BackendDiagnosticsHandler<TContext>;
}
/**
* Data structure which contains backend diagnostics options.
* @public
*/
export interface BackendDiagnosticsAttribute {
/**
* Backend diagnostics options.
* @beta
*/
diagnostics?: BackendDiagnosticsOptions;
}
/** @internal */
export function combineDiagnosticsOptions(...options: Array<BackendDiagnosticsOptions | undefined>): DiagnosticsOptions | undefined {
const combinedOptions: DiagnosticsOptions = {};
options.forEach((d) => {
if (!d) {
return;
}
if (
d.perf === true ||
(typeof d.perf === "object" &&
(!combinedOptions.perf || (typeof combinedOptions.perf === "object" && d.perf.minimumDuration < combinedOptions.perf.minimumDuration)))
) {
combinedOptions.perf = d.perf;
}
const combinedDev = combineDiagnosticsSeverities(d.dev, combinedOptions.dev);
if (combinedDev) {
combinedOptions.dev = combinedDev;
}
const combinedEditor = combineDiagnosticsSeverities(d.editor, combinedOptions.editor);
if (combinedEditor) {
combinedOptions.editor = combinedEditor;
}
});
return combinedOptions.dev || combinedOptions.editor || combinedOptions.perf ? combinedOptions : undefined;
}
/** @internal */
export function reportDiagnostics<TContext>(diagnostics: Diagnostics, options: BackendDiagnosticsOptions<TContext>, context?: TContext) {
const stripped = diagnostics.logs ? stripDiagnostics(options, diagnostics.logs) : undefined;
stripped && options.handler({ logs: stripped }, context);
}
function stripDiagnostics<TEntry extends DiagnosticsLogEntry>(options: DiagnosticsOptions, diagnostics: TEntry[]) {
const stripped: TEntry[] = [];
diagnostics.forEach((entry) => {
if (DiagnosticsLogEntry.isScope(entry)) {
const scopeLogs = stripDiagnostics(options, entry.logs ?? []);
const strippedScope = { ...entry, logs: scopeLogs };
if (!strippedScope.logs) {
delete strippedScope.logs;
}
if (entry.duration !== undefined && (options.perf === true || (typeof options.perf === "object" && entry.duration >= options.perf.minimumDuration))) {
stripped.push(strippedScope);
} else if (scopeLogs) {
delete strippedScope.duration;
delete strippedScope.scopeCreateTimestamp;
stripped.push(strippedScope);
}
} else {
const matchesDevSeverity = entry.severity.dev && compareDiagnosticsSeverities(entry.severity.dev, options.dev) >= 0;
const matchesEditorSeverity = entry.severity.editor && compareDiagnosticsSeverities(entry.severity.editor, options.editor) >= 0;
if (matchesDevSeverity || matchesEditorSeverity) {
stripped.push({ ...entry });
}
}
});
return stripped.length > 0 ? stripped : undefined;
}