-
Notifications
You must be signed in to change notification settings - Fork 208
/
LogEntryConverter.ts
309 lines (273 loc) · 12.6 KB
/
LogEntryConverter.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/*---------------------------------------------------------------------------------------------
* Copyright (c) 2019 Bentley Systems, Incorporated. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license terms.
*--------------------------------------------------------------------------------------------*/
import { Guid, GuidString, Logger } from "@bentley/bentleyjs-core";
import { UsageLogEntry, FeatureLogEntry, FeatureStartedLogEntry, FeatureEndedLogEntry, ProductVersion, UsageType } from "./UlasClient";
import { AuthorizedClientRequestContext } from "../AuthorizedClientRequestContext";
import { ClientsLoggerCategory } from "../ClientsLoggerCategory";
const loggerCategory: string = ClientsLoggerCategory.UlasClient;
/** Specifies the JSON format for a UsageLogEntry as expected by the ULAS REST API
* (see https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1)
* @internal
*/
export interface UsageLogEntryJson {
/** Ultimate ID, i.e. company ID in SAP */
ultID?: number;
/** The ID of the Principal that was granted access to the application */
pid?: GuidString;
/** The GUID of the IMS user accessing the product, maybe the same as the Principal. */
imsID?: GuidString;
/** The client’s machine name excluding domain information. */
hID: string;
/** The client’s login name excluding domain information */
uID?: string;
/** The GUID embedded in the policy file that allows us to track the entitlement history. */
polID: GuidString;
/** The ID of the securable. */
secID: string;
/** The product ID for which usage is being submitted. It is a 4-digit Product ID from the GPR. */
prdid: number;
/** A feature string further identifying the product for which available usage is being submitted. Not to be confused with feature IDs. */
fstr: string;
/** The version of the application producing the usage.
* Format: Pad all sections out to 4 digits padding is with zeros, e.g. 9.10.2.113 becomes 9001000020113.
*/
ver: number;
/** The GUID of the project that the usage should be associated with.
* If no project is selected, omit the field.
*/
projID?: GuidString;
/** The GUID that identifies a unique usage session, used to correlate data between feature usage and usage logs. */
corID?: GuidString;
/** The UTC time of the event. */
evTimeZ?: string;
/** The version of the schema which this log entry represents. */
lVer: number;
/** Identifies the source of the usage log entry: RealTime, Offline, Checkout */
lSrc: string;
/** Identifies the country where the client reporting the usage belongs to. */
country?: string;
/** The type of usage that occurred on the client. It is acting as a filter to eliminate records from log processing that
* should not count towards a customer’s peak processing. One of: Production, Trial, Beta, HomeUse, PreActivation
*/
uType: string;
}
/** @internal */
export interface FeatureLogEntryAttributeJson {
name: string;
value: string;
}
/** Specifies the JSON format for a FeatureLogEntry as expected by the ULAS REST API
* (see https://qa-connect-ulastm.bentley.com/Bentley.ULAS.SwaggerUI/SwaggerWebApp/?urls.primaryName=ULAS%20Posting%20Service%20v1)
* @internal
*/
export interface FeatureLogEntryJson extends UsageLogEntryJson {
/** Gets the ID of the feature used (from the Global Feature Registry) */
ftrID: GuidString;
/** The start date in UTC when feature usage has started (for duration feature log entries) */
sDateZ: string;
/** The end date in UTC when feature usage has started (for duration feature log entries) */
eDateZ: string;
/** Additional user-defined metadata for the feature usage */
uData: FeatureLogEntryAttributeJson[];
}
/** @internal */
export class LogEntryConverter {
// for now this is always 1
private static readonly _logEntryVersion: number = 1;
// this is a real-time client, i.e. it sends the requests right away without caching or aggregating.
private static readonly _logPostingSource: string = "RealTime";
// fStr argument is empty for now
private static readonly _featureString: string = "";
private static readonly _policyFileId: GuidString = Guid.createValue();
private static readonly _securableId: string = Guid.createValue();
/**
* Extracts the application version from the supplied request context
* @param requestContext The client request context
* @returns The application version for the request context
*/
private static getApplicationVersion(requestContext: AuthorizedClientRequestContext): ProductVersion {
const applicationVersion = requestContext.applicationVersion;
const defaultVersion = { major: 1, minor: 0 };
if (!applicationVersion) {
Logger.logWarning(loggerCategory, "ApplicationVersion was not specified. Set up IModelApp.applicationVersion for frontend applications, or IModelHost.applicationVersion for agents", () => ({ applicationVersion }));
return defaultVersion;
}
const versionSplit = applicationVersion.split(".");
const length = versionSplit.length;
if (length < 2) {
Logger.logWarning(loggerCategory, "ApplicationVersion is not valid", () => ({ applicationVersion }));
return defaultVersion;
}
const major = parseInt(versionSplit[0], 10);
if (typeof major === "undefined") {
Logger.logWarning(loggerCategory, "ApplicationVersion is not valid", () => ({ applicationVersion }));
return defaultVersion;
}
const minor = parseInt(versionSplit[1], 10);
if (typeof minor === "undefined") {
Logger.logWarning(loggerCategory, "ApplicationVersion is not valid", () => ({ applicationVersion }));
return { major, minor: 0 };
}
let sub1: number | undefined;
let sub2: number | undefined;
if (length > 2) {
sub1 = parseInt(versionSplit[2], 10);
if (length > 3 && !isNaN(sub1)) {
sub2 = parseInt(versionSplit[3], 10) || undefined;
}
}
return { major, minor, sub1, sub2 };
}
/**
* Extracts the application id from the supplied request context
* @param requestContext The client request context
* @returns The application id for the request context
*/
private static getApplicationId(requestContext: AuthorizedClientRequestContext): number {
const defaultId = 2686; // iModel.js
if (!requestContext.applicationId) {
Logger.logWarning(loggerCategory, "ApplicationId was not specified. Set up IModelApp.applicationId for frontend applications, or IModelHost.applicationId for agents");
return defaultId;
}
return parseInt(requestContext.applicationId, 10) || defaultId;
}
/**
* Extracts the session id from the supplied request context
* @param requestContext The client request context
* @returns The session id for the request context
*/
private static getSessionId(requestContext: AuthorizedClientRequestContext): GuidString {
if (!Guid.isGuid(requestContext.sessionId)) {
Logger.logWarning(loggerCategory, "Specified sessionId is not a valid Guid. Set up IModelApp.sessionId for frontend applications, or IModelHost.sessionId for agents");
return Guid.empty;
}
return requestContext.sessionId;
}
public static toUsageLogJson(requestContext: AuthorizedClientRequestContext, entry: UsageLogEntry): UsageLogEntryJson {
const productId: number = LogEntryConverter.getApplicationId(requestContext);
const productVersion: ProductVersion = LogEntryConverter.getApplicationVersion(requestContext);
const sessionId: GuidString = LogEntryConverter.getSessionId(requestContext);
const hID: string = LogEntryConverter.prepareMachineName(entry.hostName);
const ver: number = LogEntryConverter.toVersionNumber(productVersion);
const uType: string = LogEntryConverter.usageTypeToString(entry.usageType);
return {
hID,
polID: LogEntryConverter._policyFileId,
secID: LogEntryConverter._securableId,
prdid: productId,
fstr: LogEntryConverter._featureString,
ver,
projID: entry.contextId,
corID: sessionId,
lVer: LogEntryConverter._logEntryVersion,
lSrc: LogEntryConverter._logPostingSource,
uType,
};
}
public static toFeatureLogJson(requestContext: AuthorizedClientRequestContext, entries: FeatureLogEntry[]): FeatureLogEntryJson[] {
const json: FeatureLogEntryJson[] = [];
const productId: number = LogEntryConverter.getApplicationId(requestContext);
const productVersion: ProductVersion = LogEntryConverter.getApplicationVersion(requestContext);
const sessionId: GuidString = LogEntryConverter.getSessionId(requestContext);
const ver: number | undefined = LogEntryConverter.toVersionNumber(productVersion);
for (const entry of entries) {
const hID: string = LogEntryConverter.prepareMachineName(entry.hostName);
const evTimeZ: string = entry.timestamp;
let sDateZ: string;
let eDateZ: string;
let corID: GuidString | undefined;
const startEntry: FeatureStartedLogEntry = entry as FeatureStartedLogEntry;
const endEntry: FeatureEndedLogEntry = entry as FeatureEndedLogEntry;
const defaultDate: string = "0001-01-01T00:00:00Z";
if (!!startEntry.entryId) {
sDateZ = evTimeZ;
eDateZ = defaultDate;
corID = startEntry.entryId;
} else if (!!endEntry.startEntryId) {
sDateZ = defaultDate;
eDateZ = evTimeZ;
corID = endEntry.startEntryId;
} else {
sDateZ = evTimeZ;
eDateZ = evTimeZ;
corID = sessionId;
}
const uType: string = LogEntryConverter.usageTypeToString(entry.usageType);
const uData: FeatureLogEntryAttributeJson[] = [];
for (const att of entry.usageData) {
uData.push({ name: att.name, value: att.value.toString() });
}
const entryJson: FeatureLogEntryJson = {
hID,
polID: LogEntryConverter._policyFileId,
secID: LogEntryConverter._securableId,
prdid: productId,
fstr: LogEntryConverter._featureString,
ver,
projID: entry.contextId,
corID,
lVer: LogEntryConverter._logEntryVersion,
lSrc: LogEntryConverter._logPostingSource,
uType,
ftrID: entry.featureId,
sDateZ,
eDateZ,
uData,
};
json.push(entryJson);
}
return json;
}
private static toVersionNumber(version: ProductVersion): number {
// version must be encoded into a single number where each version digit is padded out to 4 digits
// and the version is always considered to have 4 digits.
// Ex: 3.99.4 -> 3.99.4.0 -> 3009900040000
let verNumber: number = !!version.sub2 ? version.sub2 : 0;
verNumber += 10000 * (!!version.sub1 ? version.sub1 : 0);
verNumber += Math.pow(10000, 2) * version.minor;
verNumber += Math.pow(10000, 3) * version.major;
return verNumber;
}
private static prepareMachineName(machineName: string): string {
if (!machineName || machineName.length === 0)
return "";
if (machineName === "::1" || machineName === "127.0.0.1")
return "localhost";
return machineName.toLowerCase();
}
/* private static prepareUserName(userName: string, machineName: string): string {
if (!userName || userName.length === 0)
return "";
let preparedUserName: string = userName;
const backslashPos: number = userName.indexOf("\\");
if (backslashPos >= 0)
preparedUserName = userName.substr(backslashPos + 1);
else {
const slashPos: number = userName.indexOf("/");
if (slashPos >= 0)
preparedUserName = userName.substr(slashPos + 1);
}
preparedUserName = preparedUserName.toLowerCase();
if (!!machineName && machineName.length > 0 && (preparedUserName.includes("administrator") || preparedUserName.includes("system")))
preparedUserName = `${machineName.toLowerCase()}\\${preparedUserName}`;
return preparedUserName;
} */
private static usageTypeToString(val: UsageType): string {
switch (val) {
case UsageType.Beta:
return "Beta";
case UsageType.HomeUse:
return "HomeUse";
case UsageType.PreActivation:
return "PreActivation";
case UsageType.Production:
return "Production";
case UsageType.Trial:
return "Trial";
default:
throw new Error("Unhandled UsageType enum value");
}
}
}