node-avo-inspector 1.0.3-beta.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AvoDeduplicator.js +2 -2
- package/dist/AvoInspector.js +40 -30
- package/dist/AvoInspectorEnv.d.ts +2 -2
- package/dist/AvoNetworkCallsHandler.js +1 -1
- package/dist/AvoSchemaParser.d.ts +1 -0
- package/dist/AvoSchemaParser.js +16 -5
- package/dist/__tests__/TestDeduplicator.js +7 -4
- package/dist/__tests__/TestNetworkCallsHandlerPublic.js +24 -22
- package/dist/__tests__/TestParsing.js +23 -9
- package/dist/__tests__/constants.d.ts +6 -1
- package/dist/__tests__/constants.js +6 -1
- package/dist/utils.js +2 -2
- package/examples/node-inspector-js/yarn.lock +1 -1
- package/package.json +6 -6
- package/src/AvoInspector.ts +24 -26
- package/src/AvoSchemaParser.ts +24 -7
- package/src/__tests__/TestDeduplicator.ts +4 -0
- package/src/__tests__/TestNetworkCallsHandlerPublic.ts +30 -25
- package/src/__tests__/TestParsing.ts +32 -9
- package/src/__tests__/constants.ts +6 -1
package/dist/AvoDeduplicator.js
CHANGED
|
@@ -46,7 +46,7 @@ class AvoDeduplicator {
|
|
|
46
46
|
if (eventsStorage.hasOwnProperty(otherEventName)) {
|
|
47
47
|
if (otherEventName === eventName) {
|
|
48
48
|
const otherParams = eventsStorage[eventName];
|
|
49
|
-
if (otherParams && utils_1.deepEquals(params, otherParams)) {
|
|
49
|
+
if (otherParams && (0, utils_1.deepEquals)(params, otherParams)) {
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -72,7 +72,7 @@ class AvoDeduplicator {
|
|
|
72
72
|
for (const otherEventName in eventsStorage) {
|
|
73
73
|
if (eventsStorage.hasOwnProperty(otherEventName)) {
|
|
74
74
|
const otherParams = eventsStorage[otherEventName];
|
|
75
|
-
if (otherParams && utils_1.deepEquals(params, otherParams)) {
|
|
75
|
+
if (otherParams && (0, utils_1.deepEquals)(params, otherParams)) {
|
|
76
76
|
return true;
|
|
77
77
|
}
|
|
78
78
|
}
|
package/dist/AvoInspector.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.AvoInspector = void 0;
|
|
4
13
|
const AvoInspectorEnv_1 = require("./AvoInspectorEnv");
|
|
@@ -9,9 +18,15 @@ const utils_1 = require("./utils");
|
|
|
9
18
|
const AvoGuid_1 = require("./AvoGuid");
|
|
10
19
|
const libVersion = require("../package.json").version;
|
|
11
20
|
class AvoInspector {
|
|
21
|
+
static get shouldLog() {
|
|
22
|
+
return this._shouldLog;
|
|
23
|
+
}
|
|
24
|
+
static set shouldLog(enable) {
|
|
25
|
+
this._shouldLog = enable;
|
|
26
|
+
}
|
|
12
27
|
constructor(options) {
|
|
13
28
|
// the constructor does aggressive null/undefined checking because same code paths will be accessible from JS
|
|
14
|
-
if (utils_1.isValueEmpty(options.env)) {
|
|
29
|
+
if ((0, utils_1.isValueEmpty)(options.env)) {
|
|
15
30
|
this.environment = AvoInspectorEnv_1.AvoInspectorEnv.Dev;
|
|
16
31
|
console.warn("[Avo Inspector] No environment provided. Defaulting to dev.");
|
|
17
32
|
}
|
|
@@ -22,13 +37,13 @@ class AvoInspector {
|
|
|
22
37
|
else {
|
|
23
38
|
this.environment = options.env;
|
|
24
39
|
}
|
|
25
|
-
if (utils_1.isValueEmpty(options.apiKey)) {
|
|
40
|
+
if ((0, utils_1.isValueEmpty)(options.apiKey)) {
|
|
26
41
|
throw new Error("[Avo Inspector] No API key provided. Inspector can't operate without API key.");
|
|
27
42
|
}
|
|
28
43
|
else {
|
|
29
44
|
this.apiKey = options.apiKey;
|
|
30
45
|
}
|
|
31
|
-
if (utils_1.isValueEmpty(options.version)) {
|
|
46
|
+
if ((0, utils_1.isValueEmpty)(options.version)) {
|
|
32
47
|
throw new Error("[Avo Inspector] No version provided. Many features of Inspector rely on versioning. Please provide comparable string version, i.e. integer or semantic.");
|
|
33
48
|
}
|
|
34
49
|
else {
|
|
@@ -43,12 +58,6 @@ class AvoInspector {
|
|
|
43
58
|
this.avoNetworkCallsHandler = new AvoNetworkCallsHandler_1.AvoNetworkCallsHandler(this.apiKey, this.environment.toString(), options.appName || "", this.version, libVersion);
|
|
44
59
|
this.avoDeduplicator = new AvoDeduplicator_1.AvoDeduplicator();
|
|
45
60
|
}
|
|
46
|
-
static get shouldLog() {
|
|
47
|
-
return this._shouldLog;
|
|
48
|
-
}
|
|
49
|
-
static set shouldLog(enable) {
|
|
50
|
-
this._shouldLog = enable;
|
|
51
|
-
}
|
|
52
61
|
trackSchemaFromEvent(eventName, eventProperties) {
|
|
53
62
|
try {
|
|
54
63
|
if (this.avoDeduplicator.shouldRegisterEvent(eventName, eventProperties, false)) {
|
|
@@ -102,34 +111,35 @@ class AvoInspector {
|
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
trackSchemaInternal(eventName, eventSchema, eventId, eventHash) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
try {
|
|
116
|
+
const seesionId = AvoGuid_1.AvoGuid.newGuid();
|
|
117
|
+
yield this.avoNetworkCallsHandler
|
|
118
|
+
.callInspectorWithBatchBody([
|
|
119
|
+
this.avoNetworkCallsHandler.bodyForSessionStartedCall(seesionId),
|
|
120
|
+
]);
|
|
112
121
|
if (AvoInspector.shouldLog) {
|
|
113
122
|
console.log("Avo Inspector: session started sent successfully.");
|
|
114
123
|
}
|
|
115
|
-
|
|
116
|
-
this.avoNetworkCallsHandler.
|
|
117
|
-
|
|
124
|
+
try {
|
|
125
|
+
yield this.avoNetworkCallsHandler.callInspectorWithBatchBody([
|
|
126
|
+
this.avoNetworkCallsHandler.bodyForEventSchemaCall(seesionId, eventName, eventSchema, eventId, eventHash)
|
|
127
|
+
]);
|
|
118
128
|
if (AvoInspector.shouldLog) {
|
|
119
129
|
console.log("Avo Inspector: schema sent successfully.");
|
|
120
130
|
}
|
|
121
|
-
});
|
|
122
|
-
})
|
|
123
|
-
.catch((err) => {
|
|
124
|
-
if (AvoInspector.shouldLog) {
|
|
125
|
-
console.log("Avo Inspector: schema sending failed: " + err + ".");
|
|
126
131
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
catch (err) {
|
|
133
|
+
if (AvoInspector.shouldLog) {
|
|
134
|
+
console.log("Avo Inspector: schema sending failed: " + err + ".");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
console.error("Avo Inspector: something went wrong. Please report to support@avo.app.", e);
|
|
140
|
+
return Promise.reject();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
133
143
|
}
|
|
134
144
|
enableLogging(enable) {
|
|
135
145
|
AvoInspector._shouldLog = enable;
|
|
@@ -3,5 +3,5 @@ export declare const AvoInspectorEnv: {
|
|
|
3
3
|
readonly Dev: "dev";
|
|
4
4
|
readonly Staging: "staging";
|
|
5
5
|
};
|
|
6
|
-
export
|
|
7
|
-
export
|
|
6
|
+
export type AvoInspectorEnvType = typeof AvoInspectorEnv;
|
|
7
|
+
export type AvoInspectorEnvValueType = AvoInspectorEnvType[keyof AvoInspectorEnvType];
|
|
@@ -50,7 +50,7 @@ class AvoNetworkCallsHandler {
|
|
|
50
50
|
"Content-Length": Buffer.byteLength(data),
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
|
-
var req = https_1.request(options, (res) => {
|
|
53
|
+
var req = (0, https_1.request)(options, (res) => {
|
|
54
54
|
const chunks = [];
|
|
55
55
|
res.on("data", (data) => chunks.push(data));
|
|
56
56
|
res.on("end", () => {
|
package/dist/AvoSchemaParser.js
CHANGED
|
@@ -56,7 +56,7 @@ class AvoSchemaParser {
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
-
static
|
|
59
|
+
static getBasicPropType(propValue) {
|
|
60
60
|
let propType = typeof propValue;
|
|
61
61
|
if (propValue == null) {
|
|
62
62
|
return "null";
|
|
@@ -76,15 +76,26 @@ class AvoSchemaParser {
|
|
|
76
76
|
return "boolean";
|
|
77
77
|
}
|
|
78
78
|
else if (propType === "object") {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
return "object";
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return "unknown";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
static getPropValueType(propValue) {
|
|
86
|
+
if (isArray(propValue)) {
|
|
87
|
+
//we now know that propValue is an array. get first element in propValue array
|
|
88
|
+
let propElement = propValue[0];
|
|
89
|
+
if (propElement == null) {
|
|
90
|
+
return "list(string)"; // Default to list(string) if the list is empty.
|
|
81
91
|
}
|
|
82
92
|
else {
|
|
83
|
-
|
|
93
|
+
let propElementType = this.getBasicPropType(propElement);
|
|
94
|
+
return `list(${propElementType})`;
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
else {
|
|
87
|
-
return
|
|
98
|
+
return this.getBasicPropType(propValue);
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
}
|
|
@@ -13,6 +13,9 @@ const AvoDeduplicator_1 = require("../AvoDeduplicator");
|
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
14
|
const AvoInspector_1 = require("../AvoInspector");
|
|
15
15
|
const constants_1 = require("./constants");
|
|
16
|
+
jest
|
|
17
|
+
.useFakeTimers()
|
|
18
|
+
.setSystemTime(new Date('2020-01-01'));
|
|
16
19
|
describe("Deduplicator", () => {
|
|
17
20
|
const deduplicator = new AvoDeduplicator_1.AvoDeduplicator();
|
|
18
21
|
const testObject = {
|
|
@@ -28,10 +31,10 @@ describe("Deduplicator", () => {
|
|
|
28
31
|
2: [["str", true]],
|
|
29
32
|
3: { avo: [1.1, 2.2, 3.3] },
|
|
30
33
|
};
|
|
31
|
-
expect(utils_1.deepEquals(testObject, secondObject)).toBe(true);
|
|
32
|
-
expect(utils_1.deepEquals(testObject, Object.assign(Object.assign({}, secondObject), { "4": "4" }))).toBe(false);
|
|
33
|
-
expect(utils_1.deepEquals(testObject, Object.assign(Object.assign({}, secondObject), { 3: Object.assign(Object.assign({}, secondObject[3]), { avo: [1.1] }) }))).toBe(false);
|
|
34
|
-
expect(utils_1.deepEquals(testObject, Object.assign(Object.assign({}, secondObject), { 0: "other string" }))).toBe(false);
|
|
34
|
+
expect((0, utils_1.deepEquals)(testObject, secondObject)).toBe(true);
|
|
35
|
+
expect((0, utils_1.deepEquals)(testObject, Object.assign(Object.assign({}, secondObject), { "4": "4" }))).toBe(false);
|
|
36
|
+
expect((0, utils_1.deepEquals)(testObject, Object.assign(Object.assign({}, secondObject), { 3: Object.assign(Object.assign({}, secondObject[3]), { avo: [1.1] }) }))).toBe(false);
|
|
37
|
+
expect((0, utils_1.deepEquals)(testObject, Object.assign(Object.assign({}, secondObject), { 0: "other string" }))).toBe(false);
|
|
35
38
|
});
|
|
36
39
|
test(`Detects duplications when track in avo and then manually`, () => {
|
|
37
40
|
const shouldRegisterFromAvo = deduplicator.shouldRegisterEvent("Test", testObject, true);
|
|
@@ -14,17 +14,29 @@ const AvoNetworkCallsHandler_1 = require("../AvoNetworkCallsHandler");
|
|
|
14
14
|
const constants_1 = require("./constants");
|
|
15
15
|
describe("AvoNetworkCallsHandler", () => {
|
|
16
16
|
let inspectorCallSpy;
|
|
17
|
+
let consoleLogSpy;
|
|
17
18
|
let inspector;
|
|
18
19
|
const { apiKey, env, version, appName } = constants_1.defaultOptions;
|
|
20
|
+
const eventName = "event name";
|
|
21
|
+
const properties = {
|
|
22
|
+
prop0: "",
|
|
23
|
+
prop2: false,
|
|
24
|
+
prop3: 0,
|
|
25
|
+
prop4: 0.0,
|
|
26
|
+
};
|
|
27
|
+
const happyPathInspectorCall = () => {
|
|
28
|
+
inspector.avoNetworkCallsHandler["samplingRate"] = 0.1;
|
|
29
|
+
return Promise.resolve();
|
|
30
|
+
};
|
|
19
31
|
beforeAll(() => {
|
|
20
32
|
inspector = new AvoInspector_1.AvoInspector(constants_1.defaultOptions);
|
|
21
|
-
inspector.enableLogging(
|
|
33
|
+
inspector.enableLogging(true);
|
|
22
34
|
inspectorCallSpy = jest
|
|
23
35
|
.spyOn(AvoNetworkCallsHandler_1.AvoNetworkCallsHandler.prototype, "callInspectorWithBatchBody");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => { });
|
|
37
|
+
});
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
inspectorCallSpy.mockImplementation(happyPathInspectorCall);
|
|
28
40
|
});
|
|
29
41
|
afterEach(() => {
|
|
30
42
|
jest.clearAllMocks();
|
|
@@ -32,17 +44,9 @@ describe("AvoNetworkCallsHandler", () => {
|
|
|
32
44
|
inspector.avoDeduplicator._clearEvents();
|
|
33
45
|
});
|
|
34
46
|
test("handleTrackSchema is called on trackSchemaFromEvent", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
-
const eventName = "event name";
|
|
36
|
-
const properties = {
|
|
37
|
-
prop0: "",
|
|
38
|
-
prop2: false,
|
|
39
|
-
prop3: 0,
|
|
40
|
-
prop4: 0.0,
|
|
41
|
-
};
|
|
42
|
-
const schema = inspector.extractSchema(properties);
|
|
43
47
|
yield inspector.trackSchemaFromEvent(eventName, properties);
|
|
44
48
|
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(2);
|
|
45
|
-
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).
|
|
49
|
+
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
|
|
46
50
|
expect.objectContaining({
|
|
47
51
|
type: "sessionStarted",
|
|
48
52
|
apiKey: apiKey,
|
|
@@ -76,16 +80,8 @@ describe("AvoNetworkCallsHandler", () => {
|
|
|
76
80
|
]);
|
|
77
81
|
}));
|
|
78
82
|
test("handleTrackSchema is called on _avoFunctionTrackSchemaFromEvent", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
-
const eventName = "event name";
|
|
80
|
-
const properties = {
|
|
81
|
-
prop0: "",
|
|
82
|
-
prop2: false,
|
|
83
|
-
prop3: 0,
|
|
84
|
-
prop4: 0.0,
|
|
85
|
-
};
|
|
86
83
|
const eventId = "testId";
|
|
87
84
|
const eventHash = "testHash";
|
|
88
|
-
const schema = inspector.extractSchema(properties);
|
|
89
85
|
// @ts-ignore
|
|
90
86
|
yield inspector._avoFunctionTrackSchemaFromEvent(eventName, properties, eventId, eventHash);
|
|
91
87
|
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(2);
|
|
@@ -122,4 +118,10 @@ describe("AvoNetworkCallsHandler", () => {
|
|
|
122
118
|
}),
|
|
123
119
|
]);
|
|
124
120
|
}));
|
|
121
|
+
test("an error is logged if event schema call fails and AvoInspector.shouldLog is true", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
122
|
+
inspectorCallSpy.mockImplementationOnce(happyPathInspectorCall);
|
|
123
|
+
inspectorCallSpy.mockRejectedValueOnce('Network error');
|
|
124
|
+
yield inspector.trackSchemaFromEvent(eventName, properties);
|
|
125
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('Avo Inspector: schema sending failed: Network error.');
|
|
126
|
+
}));
|
|
125
127
|
});
|
|
@@ -52,7 +52,7 @@ describe("Schema Parsing", () => {
|
|
|
52
52
|
propertyType: constants_1.type.STRING,
|
|
53
53
|
},
|
|
54
54
|
]);
|
|
55
|
-
expect(res[7].propertyType).toBe(constants_1.type.
|
|
55
|
+
expect(res[7].propertyType).toBe(constants_1.type.STRINGLIST);
|
|
56
56
|
expect(res[7].children).toMatchObject([
|
|
57
57
|
constants_1.type.STRING,
|
|
58
58
|
[
|
|
@@ -77,13 +77,7 @@ describe("Schema Parsing", () => {
|
|
|
77
77
|
// When
|
|
78
78
|
const res = inspector.extractSchema(eventProperties);
|
|
79
79
|
// Then
|
|
80
|
-
expect(res[0].propertyType).toBe(constants_1.type.
|
|
81
|
-
expect(res[0].children).toMatchObject([
|
|
82
|
-
constants_1.type.STRING,
|
|
83
|
-
constants_1.type.BOOL,
|
|
84
|
-
constants_1.type.INT,
|
|
85
|
-
constants_1.type.FLOAT,
|
|
86
|
-
]);
|
|
80
|
+
expect(res[0].propertyType).toBe(constants_1.type.STRINGLIST);
|
|
87
81
|
});
|
|
88
82
|
test("Empty and falsy values are set correctly", () => {
|
|
89
83
|
// Given
|
|
@@ -108,7 +102,27 @@ describe("Schema Parsing", () => {
|
|
|
108
102
|
expect(res[5].propertyType).toBe(constants_1.type.NULL);
|
|
109
103
|
expect(res[6].propertyType).toBe(constants_1.type.OBJECT);
|
|
110
104
|
expect(res[6].children).toMatchObject([]);
|
|
111
|
-
expect(res[7].propertyType).toBe(constants_1.type.
|
|
105
|
+
expect(res[7].propertyType).toBe(constants_1.type.STRINGLIST);
|
|
112
106
|
expect(res[7].children).toMatchObject([]);
|
|
113
107
|
});
|
|
108
|
+
test("List of string returns list(string)", () => {
|
|
109
|
+
// Given
|
|
110
|
+
const eventProperties = {
|
|
111
|
+
prop0: ["a", "b", "c"],
|
|
112
|
+
};
|
|
113
|
+
// When
|
|
114
|
+
const res = inspector.extractSchema(eventProperties);
|
|
115
|
+
// Then
|
|
116
|
+
expect(res[0].propertyType).toBe(constants_1.type.STRINGLIST);
|
|
117
|
+
});
|
|
118
|
+
test("List of multiple types returns list(`firstType`)", () => {
|
|
119
|
+
// Given
|
|
120
|
+
const eventProperties = {
|
|
121
|
+
prop0: [1.2, "two", { "three": 3 }],
|
|
122
|
+
};
|
|
123
|
+
// When
|
|
124
|
+
const res = inspector.extractSchema(eventProperties);
|
|
125
|
+
// Then
|
|
126
|
+
expect(res[0].propertyType).toBe(constants_1.type.FLOATLIST);
|
|
127
|
+
});
|
|
114
128
|
});
|
|
@@ -26,12 +26,17 @@ declare const trackingEndpoint = "https://api.avo.app/inspector/v1/track";
|
|
|
26
26
|
declare const sessionTimeMs: number;
|
|
27
27
|
declare const type: {
|
|
28
28
|
STRING: string;
|
|
29
|
+
STRINGLIST: string;
|
|
29
30
|
INT: string;
|
|
31
|
+
INTLIST: string;
|
|
30
32
|
OBJECT: string;
|
|
33
|
+
OBJECTLIST: string;
|
|
31
34
|
FLOAT: string;
|
|
32
|
-
|
|
35
|
+
FLOATLIST: string;
|
|
33
36
|
BOOL: string;
|
|
37
|
+
BOOLLIST: string;
|
|
34
38
|
NULL: string;
|
|
35
39
|
UNKNOWN: string;
|
|
40
|
+
UNKNOWNLIST: string;
|
|
36
41
|
};
|
|
37
42
|
export { defaultOptions, error, mockedReturns, networkCallType, requestMsg, sessionTimeMs, type, trackingEndpoint, };
|
|
@@ -37,12 +37,17 @@ const sessionTimeMs = 5 * 60 * 1000;
|
|
|
37
37
|
exports.sessionTimeMs = sessionTimeMs;
|
|
38
38
|
const type = {
|
|
39
39
|
STRING: "string",
|
|
40
|
+
STRINGLIST: "list(string)",
|
|
40
41
|
INT: "int",
|
|
42
|
+
INTLIST: "list(int)",
|
|
41
43
|
OBJECT: "object",
|
|
44
|
+
OBJECTLIST: "list(object)",
|
|
42
45
|
FLOAT: "float",
|
|
43
|
-
|
|
46
|
+
FLOATLIST: "list(float)",
|
|
44
47
|
BOOL: "boolean",
|
|
48
|
+
BOOLLIST: "list(boolean)",
|
|
45
49
|
NULL: "null",
|
|
46
50
|
UNKNOWN: "unknown",
|
|
51
|
+
UNKNOWNLIST: "list(unknown)",
|
|
47
52
|
};
|
|
48
53
|
exports.type = type;
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.isValueEmpty = void 0;
|
|
4
|
+
exports.deepEquals = deepEquals;
|
|
4
5
|
const isValueEmpty = (value) => {
|
|
5
6
|
return value === null || value === undefined || value.trim().length == 0;
|
|
6
7
|
};
|
|
@@ -39,4 +40,3 @@ function deepEquals(x, y) {
|
|
|
39
40
|
}
|
|
40
41
|
return true;
|
|
41
42
|
}
|
|
42
|
-
exports.deepEquals = deepEquals;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-avo-inspector",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Avo Inspector for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": "https://github.com/avohq/node-avo-inspector",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
]
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/jest": "^
|
|
40
|
-
"@types/node": "^
|
|
41
|
-
"jest": "^
|
|
42
|
-
"ts-jest": "^
|
|
43
|
-
"typescript": "
|
|
39
|
+
"@types/jest": "^29.5.12",
|
|
40
|
+
"@types/node": "^22.3.0",
|
|
41
|
+
"jest": "^29.7.0",
|
|
42
|
+
"ts-jest": "^29.2.4",
|
|
43
|
+
"typescript": "*"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {}
|
|
46
46
|
}
|
package/src/AvoInspector.ts
CHANGED
|
@@ -182,7 +182,7 @@ export class AvoInspector {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
private trackSchemaInternal(
|
|
185
|
+
private async trackSchemaInternal(
|
|
186
186
|
eventName: string,
|
|
187
187
|
eventSchema: Array<{
|
|
188
188
|
propertyName: string;
|
|
@@ -194,33 +194,31 @@ export class AvoInspector {
|
|
|
194
194
|
): Promise<void> {
|
|
195
195
|
try {
|
|
196
196
|
const seesionId = AvoGuid.newGuid();
|
|
197
|
-
|
|
197
|
+
await this.avoNetworkCallsHandler
|
|
198
198
|
.callInspectorWithBatchBody([
|
|
199
199
|
this.avoNetworkCallsHandler.bodyForSessionStartedCall(seesionId),
|
|
200
|
-
])
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
])
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
});
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
if (AvoInspector.shouldLog) {
|
|
203
|
+
console.log("Avo Inspector: session started sent successfully.");
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
await this.avoNetworkCallsHandler.callInspectorWithBatchBody([
|
|
207
|
+
this.avoNetworkCallsHandler.bodyForEventSchemaCall(
|
|
208
|
+
seesionId,
|
|
209
|
+
eventName,
|
|
210
|
+
eventSchema,
|
|
211
|
+
eventId,
|
|
212
|
+
eventHash
|
|
213
|
+
)]);
|
|
214
|
+
if (AvoInspector.shouldLog) {
|
|
215
|
+
console.log("Avo Inspector: schema sent successfully.");
|
|
216
|
+
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
if (AvoInspector.shouldLog) {
|
|
219
|
+
console.log("Avo Inspector: schema sending failed: " + err + ".");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
224
222
|
} catch (e) {
|
|
225
223
|
console.error(
|
|
226
224
|
"Avo Inspector: something went wrong. Please report to support@avo.app.",
|
package/src/AvoSchemaParser.ts
CHANGED
|
@@ -71,7 +71,8 @@ export class AvoSchemaParser {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
|
|
75
|
+
private static getBasicPropType(propValue: any): string {
|
|
75
76
|
let propType = typeof propValue;
|
|
76
77
|
if (propValue == null) {
|
|
77
78
|
return "null";
|
|
@@ -86,13 +87,29 @@ export class AvoSchemaParser {
|
|
|
86
87
|
} else if (propType === "boolean") {
|
|
87
88
|
return "boolean";
|
|
88
89
|
} else if (propType === "object") {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
return "object"
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return "unknown";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private static getPropValueType(propValue: any): string {
|
|
98
|
+
if (isArray(propValue)){
|
|
99
|
+
|
|
100
|
+
//we now know that propValue is an array. get first element in propValue array
|
|
101
|
+
let propElement = propValue[0];
|
|
102
|
+
|
|
103
|
+
if (propElement == null) {
|
|
104
|
+
return "list(string)"; // Default to list(string) if the list is empty.
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
let propElementType = this.getBasicPropType(propElement);
|
|
108
|
+
return `list(${propElementType})`
|
|
93
109
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
return this.getBasicPropType(propValue);
|
|
96
113
|
}
|
|
97
114
|
}
|
|
98
115
|
}
|
|
@@ -3,6 +3,10 @@ import { deepEquals } from "../utils";
|
|
|
3
3
|
import { AvoInspector } from "../AvoInspector";
|
|
4
4
|
import { defaultOptions } from "./constants";
|
|
5
5
|
|
|
6
|
+
jest
|
|
7
|
+
.useFakeTimers()
|
|
8
|
+
.setSystemTime(new Date('2020-01-01'));
|
|
9
|
+
|
|
6
10
|
describe("Deduplicator", () => {
|
|
7
11
|
const deduplicator = new AvoDeduplicator();
|
|
8
12
|
|
|
@@ -5,24 +5,39 @@ import { defaultOptions } from "./constants";
|
|
|
5
5
|
|
|
6
6
|
describe("AvoNetworkCallsHandler", () => {
|
|
7
7
|
let inspectorCallSpy: jest.SpyInstance<any, unknown[]>;
|
|
8
|
+
let consoleLogSpy: jest.SpyInstance<any, unknown[]>;
|
|
8
9
|
|
|
9
10
|
let inspector: AvoInspector;
|
|
10
11
|
|
|
11
12
|
const { apiKey, env, version, appName } = defaultOptions;
|
|
12
13
|
|
|
14
|
+
const eventName = "event name";
|
|
15
|
+
const properties = {
|
|
16
|
+
prop0: "",
|
|
17
|
+
prop2: false,
|
|
18
|
+
prop3: 0,
|
|
19
|
+
prop4: 0.0,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const happyPathInspectorCall = () => {
|
|
23
|
+
inspector.avoNetworkCallsHandler["samplingRate"] = 0.1;
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
};
|
|
26
|
+
|
|
13
27
|
beforeAll(() => {
|
|
14
28
|
inspector = new AvoInspector(defaultOptions);
|
|
15
|
-
inspector.enableLogging(
|
|
29
|
+
inspector.enableLogging(true);
|
|
16
30
|
|
|
17
31
|
inspectorCallSpy = jest
|
|
18
32
|
.spyOn(AvoNetworkCallsHandler.prototype as any, "callInspectorWithBatchBody");
|
|
19
33
|
|
|
20
|
-
|
|
21
|
-
inspector.avoNetworkCallsHandler["samplingRate"] = 0.1;
|
|
22
|
-
return Promise.resolve();
|
|
23
|
-
});
|
|
34
|
+
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => {});
|
|
24
35
|
});
|
|
25
36
|
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
inspectorCallSpy.mockImplementation(happyPathInspectorCall);
|
|
39
|
+
})
|
|
40
|
+
|
|
26
41
|
afterEach(() => {
|
|
27
42
|
jest.clearAllMocks();
|
|
28
43
|
// @ts-ignore
|
|
@@ -30,20 +45,10 @@ describe("AvoNetworkCallsHandler", () => {
|
|
|
30
45
|
});
|
|
31
46
|
|
|
32
47
|
test("handleTrackSchema is called on trackSchemaFromEvent", async () => {
|
|
33
|
-
const eventName = "event name";
|
|
34
|
-
const properties = {
|
|
35
|
-
prop0: "",
|
|
36
|
-
prop2: false,
|
|
37
|
-
prop3: 0,
|
|
38
|
-
prop4: 0.0,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const schema = inspector.extractSchema(properties);
|
|
42
|
-
|
|
43
48
|
await inspector.trackSchemaFromEvent(eventName, properties);
|
|
44
49
|
|
|
45
50
|
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(2);
|
|
46
|
-
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).
|
|
51
|
+
expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
|
|
47
52
|
expect.objectContaining({
|
|
48
53
|
type: "sessionStarted",
|
|
49
54
|
apiKey: apiKey,
|
|
@@ -79,18 +84,9 @@ describe("AvoNetworkCallsHandler", () => {
|
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
test("handleTrackSchema is called on _avoFunctionTrackSchemaFromEvent", async () => {
|
|
82
|
-
const eventName = "event name";
|
|
83
|
-
const properties = {
|
|
84
|
-
prop0: "",
|
|
85
|
-
prop2: false,
|
|
86
|
-
prop3: 0,
|
|
87
|
-
prop4: 0.0,
|
|
88
|
-
};
|
|
89
87
|
const eventId = "testId";
|
|
90
88
|
const eventHash = "testHash";
|
|
91
89
|
|
|
92
|
-
const schema = inspector.extractSchema(properties);
|
|
93
|
-
|
|
94
90
|
// @ts-ignore
|
|
95
91
|
await inspector._avoFunctionTrackSchemaFromEvent(
|
|
96
92
|
eventName,
|
|
@@ -134,4 +130,13 @@ describe("AvoNetworkCallsHandler", () => {
|
|
|
134
130
|
}),
|
|
135
131
|
]);
|
|
136
132
|
});
|
|
133
|
+
|
|
134
|
+
test("an error is logged if event schema call fails and AvoInspector.shouldLog is true", async () => {
|
|
135
|
+
inspectorCallSpy.mockImplementationOnce(happyPathInspectorCall);
|
|
136
|
+
inspectorCallSpy.mockRejectedValueOnce('Network error');
|
|
137
|
+
|
|
138
|
+
await inspector.trackSchemaFromEvent(eventName, properties);
|
|
139
|
+
|
|
140
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('Avo Inspector: schema sending failed: Network error.');
|
|
141
|
+
});
|
|
137
142
|
});
|
|
@@ -60,7 +60,7 @@ describe("Schema Parsing", () => {
|
|
|
60
60
|
},
|
|
61
61
|
]);
|
|
62
62
|
|
|
63
|
-
expect(res[7].propertyType).toBe(type.
|
|
63
|
+
expect(res[7].propertyType).toBe(type.STRINGLIST);
|
|
64
64
|
expect(res[7].children).toMatchObject([
|
|
65
65
|
type.STRING,
|
|
66
66
|
[
|
|
@@ -88,13 +88,7 @@ describe("Schema Parsing", () => {
|
|
|
88
88
|
const res = inspector.extractSchema(eventProperties);
|
|
89
89
|
|
|
90
90
|
// Then
|
|
91
|
-
expect(res[0].propertyType).toBe(type.
|
|
92
|
-
expect(res[0].children).toMatchObject([
|
|
93
|
-
type.STRING,
|
|
94
|
-
type.BOOL,
|
|
95
|
-
type.INT,
|
|
96
|
-
type.FLOAT,
|
|
97
|
-
]);
|
|
91
|
+
expect(res[0].propertyType).toBe(type.STRINGLIST);
|
|
98
92
|
});
|
|
99
93
|
|
|
100
94
|
test("Empty and falsy values are set correctly", () => {
|
|
@@ -124,7 +118,36 @@ describe("Schema Parsing", () => {
|
|
|
124
118
|
expect(res[6].propertyType).toBe(type.OBJECT);
|
|
125
119
|
expect(res[6].children).toMatchObject([]);
|
|
126
120
|
|
|
127
|
-
expect(res[7].propertyType).toBe(type.
|
|
121
|
+
expect(res[7].propertyType).toBe(type.STRINGLIST);
|
|
128
122
|
expect(res[7].children).toMatchObject([]);
|
|
129
123
|
});
|
|
124
|
+
|
|
125
|
+
test("List of string returns list(string)", () => {
|
|
126
|
+
// Given
|
|
127
|
+
const eventProperties = {
|
|
128
|
+
prop0: ["a", "b", "c"],
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// When
|
|
132
|
+
const res = inspector.extractSchema(eventProperties);
|
|
133
|
+
|
|
134
|
+
// Then
|
|
135
|
+
expect(res[0].propertyType).toBe(type.STRINGLIST);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
test("List of multiple types returns list(`firstType`)", () => {
|
|
140
|
+
// Given
|
|
141
|
+
const eventProperties = {
|
|
142
|
+
prop0: [1.2, "two", {"three": 3}],
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// When
|
|
146
|
+
const res = inspector.extractSchema(eventProperties);
|
|
147
|
+
|
|
148
|
+
// Then
|
|
149
|
+
expect(res[0].propertyType).toBe(type.FLOATLIST);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
|
|
130
153
|
});
|
|
@@ -37,13 +37,18 @@ const sessionTimeMs = 5 * 60 * 1000;
|
|
|
37
37
|
|
|
38
38
|
const type = {
|
|
39
39
|
STRING: "string",
|
|
40
|
+
STRINGLIST: "list(string)",
|
|
40
41
|
INT: "int",
|
|
42
|
+
INTLIST: "list(int)",
|
|
41
43
|
OBJECT: "object",
|
|
44
|
+
OBJECTLIST: "list(object)",
|
|
42
45
|
FLOAT: "float",
|
|
43
|
-
|
|
46
|
+
FLOATLIST: "list(float)",
|
|
44
47
|
BOOL: "boolean",
|
|
48
|
+
BOOLLIST: "list(boolean)",
|
|
45
49
|
NULL: "null",
|
|
46
50
|
UNKNOWN: "unknown",
|
|
51
|
+
UNKNOWNLIST: "list(unknown)",
|
|
47
52
|
};
|
|
48
53
|
|
|
49
54
|
export {
|