@codaco/analytics 1.0.0-alpha-1 → 1.0.1-alpha-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/.turbo/turbo-build.log +6 -6
- package/.turbo/turbo-lint.log +5 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +70 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/src/index.ts +86 -53
- package/.turbo/turbo-test.log +0 -31
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @codaco/analytics@1.0.
|
|
2
|
+
> @codaco/analytics@1.0.1-alpha-1 build /Users/buckhalt/Code/complexdatacollective/error-analytics-microservice/packages/analytics
|
|
3
3
|
> tsup src/index.ts --format esm --dts --clean --sourcemap
|
|
4
4
|
|
|
5
5
|
CLI Building entry: src/index.ts
|
|
@@ -8,9 +8,9 @@ CLI tsup v7.2.0
|
|
|
8
8
|
CLI Target: es2022
|
|
9
9
|
CLI Cleaning output folder
|
|
10
10
|
ESM Build start
|
|
11
|
-
ESM dist/index.mjs 2.
|
|
12
|
-
ESM dist/index.mjs.map
|
|
13
|
-
ESM ⚡️ Build success in
|
|
11
|
+
ESM dist/index.mjs 2.91 KB
|
|
12
|
+
ESM dist/index.mjs.map 6.65 KB
|
|
13
|
+
ESM ⚡️ Build success in 44ms
|
|
14
14
|
DTS Build start
|
|
15
|
-
DTS ⚡️ Build success in
|
|
16
|
-
DTS dist/index.d.mts 1.
|
|
15
|
+
DTS ⚡️ Build success in 985ms
|
|
16
|
+
DTS dist/index.d.mts 1.63 KB
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
> @codaco/analytics@1.0.1-alpha-1 lint /Users/buckhalt/Code/complexdatacollective/error-analytics-microservice/packages/analytics
|
|
3
|
+
> eslint .
|
|
4
|
+
|
|
5
|
+
Pages directory cannot be found at /Users/buckhalt/Code/complexdatacollective/error-analytics-microservice/packages/analytics/pages or /Users/buckhalt/Code/complexdatacollective/error-analytics-microservice/packages/analytics/src/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
|
package/dist/index.d.mts
CHANGED
|
@@ -5,10 +5,10 @@ type GeoLocation = {
|
|
|
5
5
|
countryCode: string;
|
|
6
6
|
};
|
|
7
7
|
type AnalyticsEventBase = {
|
|
8
|
-
type: "InterviewCompleted" | "InterviewStarted" | "ProtocolInstalled" | "AppSetup" | "Error"
|
|
8
|
+
type: "InterviewCompleted" | "InterviewStarted" | "ProtocolInstalled" | "AppSetup" | "Error";
|
|
9
9
|
};
|
|
10
10
|
type AnalyticsEvent = AnalyticsEventBase & {
|
|
11
|
-
type: "InterviewCompleted" | "InterviewStarted" | "ProtocolInstalled" | "AppSetup"
|
|
11
|
+
type: "InterviewCompleted" | "InterviewStarted" | "ProtocolInstalled" | "AppSetup";
|
|
12
12
|
metadata?: Record<string, unknown>;
|
|
13
13
|
};
|
|
14
14
|
type AnalyticsError = AnalyticsEventBase & {
|
|
@@ -38,6 +38,6 @@ type RouteHandlerConfiguration = {
|
|
|
38
38
|
declare const createRouteHandler: ({ maxMindAccountId, maxMindLicenseKey, platformUrl, getInstallationId, WebServiceClient, }: RouteHandlerConfiguration) => (request: NextRequest) => Promise<Response>;
|
|
39
39
|
declare const makeEventTracker: ({ endpoint }: {
|
|
40
40
|
endpoint: string;
|
|
41
|
-
}) => (event: AnalyticsEventOrError) => void
|
|
41
|
+
}) => (event: AnalyticsEventOrError) => Promise<void>;
|
|
42
42
|
|
|
43
43
|
export { AnalyticsError, AnalyticsEvent, AnalyticsEventBase, AnalyticsEventOrError, AnalyticsEventOrErrorWithTimestamp, DispatchableAnalyticsEvent, createRouteHandler, makeEventTracker };
|
package/dist/index.mjs
CHANGED
|
@@ -26,55 +26,86 @@ var createRouteHandler = ({
|
|
|
26
26
|
WebServiceClient
|
|
27
27
|
}) => {
|
|
28
28
|
return async (request) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
try {
|
|
30
|
+
const maxMindClient = new WebServiceClient(
|
|
31
|
+
maxMindAccountId,
|
|
32
|
+
maxMindLicenseKey,
|
|
33
|
+
{
|
|
34
|
+
host: "geolite.info"
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
const installationId = await getInstallationId();
|
|
38
|
+
const event = await request.json();
|
|
39
|
+
const ip = await fetch("https://api64.ipify.org").then(
|
|
40
|
+
(res) => res.text()
|
|
41
|
+
);
|
|
42
|
+
const { country } = await maxMindClient.country(ip);
|
|
43
|
+
const countryCode = country?.isoCode ?? "Unknown";
|
|
44
|
+
const dispatchableEvent = {
|
|
45
|
+
...event,
|
|
46
|
+
installationId,
|
|
47
|
+
geolocation: {
|
|
48
|
+
countryCode
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
console.log(dispatchableEvent);
|
|
52
|
+
const response = await fetch(`${platformUrl}/api/event`, {
|
|
53
|
+
keepalive: true,
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: {
|
|
56
|
+
"Content-Type": "application/json"
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(dispatchableEvent)
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Failed to forward event to microservice: ${response.statusText}`
|
|
63
|
+
);
|
|
34
64
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
body: JSON.stringify(dispatchableEvent)
|
|
56
|
-
});
|
|
57
|
-
return Response.json({
|
|
58
|
-
message: "This is the API route"
|
|
59
|
-
});
|
|
65
|
+
return new Response(
|
|
66
|
+
JSON.stringify({ message: "Event forwarded successfully" }),
|
|
67
|
+
{
|
|
68
|
+
status: 200,
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
const error = ensureError(e);
|
|
76
|
+
console.error("Error in route handler:", error);
|
|
77
|
+
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
|
|
78
|
+
status: 500,
|
|
79
|
+
headers: {
|
|
80
|
+
"Content-Type": "application/json"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
60
84
|
};
|
|
61
85
|
};
|
|
62
|
-
var makeEventTracker = ({ endpoint }) => (event) => {
|
|
86
|
+
var makeEventTracker = ({ endpoint }) => async (event) => {
|
|
63
87
|
const eventWithTimeStamp = {
|
|
64
88
|
...event,
|
|
65
89
|
timestamp: /* @__PURE__ */ new Date()
|
|
66
90
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(endpoint, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
keepalive: true,
|
|
95
|
+
body: JSON.stringify(eventWithTimeStamp),
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json"
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Failed to send analytics event: ${response.statusText}`
|
|
103
|
+
);
|
|
73
104
|
}
|
|
74
|
-
}
|
|
105
|
+
} catch (e) {
|
|
75
106
|
const error = ensureError(e);
|
|
76
107
|
console.error("Error sending analytics event:", error.message);
|
|
77
|
-
}
|
|
108
|
+
}
|
|
78
109
|
};
|
|
79
110
|
export {
|
|
80
111
|
createRouteHandler,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["// Helper function that ensures that a value is an Error\nexport function ensureError(value: unknown): Error {\n if (!value) return new Error(\"No value was thrown\");\n\n if (value instanceof Error) return value;\n\n // Test if value inherits from Error\n if (value.isPrototypeOf(Error)) return value as Error & typeof value;\n\n let stringified = \"[Unable to stringify the thrown value]\";\n try {\n stringified = JSON.stringify(value);\n } catch {}\n\n const error = new Error(\n `This value was thrown as is, not through an Error: ${stringified}`\n );\n return error;\n}\n","import type { NextRequest } from \"next/server\";\nimport { WebServiceClient } from \"@maxmind/geoip2-node\";\nimport { ensureError } from \"./utils\";\n\ntype GeoLocation = {\n countryCode: string;\n};\n\nexport type AnalyticsEventBase = {\n type:\n | \"InterviewCompleted\"\n | \"InterviewStarted\"\n | \"ProtocolInstalled\"\n | \"AppSetup\"\n | \"Error\"
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["// Helper function that ensures that a value is an Error\nexport function ensureError(value: unknown): Error {\n if (!value) return new Error(\"No value was thrown\");\n\n if (value instanceof Error) return value;\n\n // Test if value inherits from Error\n if (value.isPrototypeOf(Error)) return value as Error & typeof value;\n\n let stringified = \"[Unable to stringify the thrown value]\";\n try {\n stringified = JSON.stringify(value);\n } catch {}\n\n const error = new Error(\n `This value was thrown as is, not through an Error: ${stringified}`\n );\n return error;\n}\n","import type { NextRequest } from \"next/server\";\nimport { WebServiceClient } from \"@maxmind/geoip2-node\";\nimport { ensureError } from \"./utils\";\n\ntype GeoLocation = {\n countryCode: string;\n};\n\nexport type AnalyticsEventBase = {\n type:\n | \"InterviewCompleted\"\n | \"InterviewStarted\"\n | \"ProtocolInstalled\"\n | \"AppSetup\"\n | \"Error\";\n};\n\nexport type AnalyticsEvent = AnalyticsEventBase & {\n type:\n | \"InterviewCompleted\"\n | \"InterviewStarted\"\n | \"ProtocolInstalled\"\n | \"AppSetup\";\n metadata?: Record<string, unknown>;\n};\n\nexport type AnalyticsError = AnalyticsEventBase & {\n type: \"Error\";\n error: {\n message: string;\n details: string;\n stacktrace: string;\n path: string;\n };\n};\n\nexport type AnalyticsEventOrError = AnalyticsEvent | AnalyticsError;\n\nexport type AnalyticsEventOrErrorWithTimestamp = AnalyticsEventOrError & {\n timestamp: Date;\n};\n\nexport type DispatchableAnalyticsEvent = AnalyticsEventOrErrorWithTimestamp & {\n installationId: string;\n geolocation?: GeoLocation;\n};\n\ntype RouteHandlerConfiguration = {\n maxMindAccountId: string;\n maxMindLicenseKey: string;\n platformUrl?: string;\n getInstallationId: () => Promise<string>;\n WebServiceClient: typeof WebServiceClient;\n};\n\nexport const createRouteHandler = ({\n maxMindAccountId,\n maxMindLicenseKey,\n platformUrl = \"https://analytics.networkcanvas.com\",\n getInstallationId,\n WebServiceClient,\n}: RouteHandlerConfiguration) => {\n return async (request: NextRequest) => {\n try {\n const maxMindClient = new WebServiceClient(\n maxMindAccountId,\n maxMindLicenseKey,\n {\n host: \"geolite.info\",\n }\n );\n\n const installationId = await getInstallationId();\n\n const event =\n (await request.json()) as AnalyticsEventOrErrorWithTimestamp;\n\n const ip = await fetch(\"https://api64.ipify.org\").then((res) =>\n res.text()\n );\n\n const { country } = await maxMindClient.country(ip);\n const countryCode = country?.isoCode ?? \"Unknown\";\n\n const dispatchableEvent: DispatchableAnalyticsEvent = {\n ...event,\n installationId,\n geolocation: {\n countryCode,\n },\n };\n\n console.log(dispatchableEvent);\n\n // Forward to microservice\n const response = await fetch(`${platformUrl}/api/event`, {\n keepalive: true,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(dispatchableEvent),\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to forward event to microservice: ${response.statusText}`\n );\n }\n\n return new Response(\n JSON.stringify({ message: \"Event forwarded successfully\" }),\n {\n status: 200,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n }\n );\n } catch (e) {\n const error = ensureError(e);\n console.error(\"Error in route handler:\", error);\n\n // Return an appropriate error response\n return new Response(JSON.stringify({ error: \"Internal Server Error\" }), {\n status: 500,\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n }\n };\n};\n\nexport const makeEventTracker =\n ({ endpoint }: { endpoint: string }) =>\n async (event: AnalyticsEventOrError) => {\n const eventWithTimeStamp = {\n ...event,\n timestamp: new Date(),\n };\n\n try {\n const response = await fetch(endpoint, {\n method: \"POST\",\n keepalive: true,\n body: JSON.stringify(eventWithTimeStamp),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to send analytics event: ${response.statusText}`\n );\n }\n } catch (e) {\n const error = ensureError(e);\n\n console.error(\"Error sending analytics event:\", error.message);\n }\n };\n"],"mappings":";AACO,SAAS,YAAY,OAAuB;AACjD,MAAI,CAAC;AAAO,WAAO,IAAI,MAAM,qBAAqB;AAElD,MAAI,iBAAiB;AAAO,WAAO;AAGnC,MAAI,MAAM,cAAc,KAAK;AAAG,WAAO;AAEvC,MAAI,cAAc;AAClB,MAAI;AACF,kBAAc,KAAK,UAAU,KAAK;AAAA,EACpC,QAAQ;AAAA,EAAC;AAET,QAAM,QAAQ,IAAI;AAAA,IAChB,sDAAsD,WAAW;AAAA,EACnE;AACA,SAAO;AACT;;;ACqCO,IAAM,qBAAqB,CAAC;AAAA,EACjC;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AACF,MAAiC;AAC/B,SAAO,OAAO,YAAyB;AACrC,QAAI;AACF,YAAM,gBAAgB,IAAI;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,UACE,MAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,iBAAiB,MAAM,kBAAkB;AAE/C,YAAM,QACH,MAAM,QAAQ,KAAK;AAEtB,YAAM,KAAK,MAAM,MAAM,yBAAyB,EAAE;AAAA,QAAK,CAAC,QACtD,IAAI,KAAK;AAAA,MACX;AAEA,YAAM,EAAE,QAAQ,IAAI,MAAM,cAAc,QAAQ,EAAE;AAClD,YAAM,cAAc,SAAS,WAAW;AAExC,YAAM,oBAAgD;AAAA,QACpD,GAAG;AAAA,QACH;AAAA,QACA,aAAa;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,IAAI,iBAAiB;AAG7B,YAAM,WAAW,MAAM,MAAM,GAAG,WAAW,cAAc;AAAA,QACvD,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,iBAAiB;AAAA,MACxC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI;AAAA,UACR,4CAA4C,SAAS,UAAU;AAAA,QACjE;AAAA,MACF;AAEA,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,+BAA+B,CAAC;AAAA,QAC1D;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,GAAG;AACV,YAAM,QAAQ,YAAY,CAAC;AAC3B,cAAQ,MAAM,2BAA2B,KAAK;AAG9C,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,wBAAwB,CAAC,GAAG;AAAA,QACtE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,IAAM,mBACX,CAAC,EAAE,SAAS,MACZ,OAAO,UAAiC;AACtC,QAAM,qBAAqB;AAAA,IACzB,GAAG;AAAA,IACH,WAAW,oBAAI,KAAK;AAAA,EACtB;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,MAAM,KAAK,UAAU,kBAAkB;AAAA,MACvC,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI;AAAA,QACR,mCAAmC,SAAS,UAAU;AAAA,MACxD;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AACV,UAAM,QAAQ,YAAY,CAAC;AAE3B,YAAQ,MAAM,kCAAkC,MAAM,OAAO;AAAA,EAC/D;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codaco/analytics",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1-alpha-1",
|
|
4
4
|
"module": "./dist/index.mjs",
|
|
5
5
|
"types": "./dist/index.d.mts",
|
|
6
6
|
"author": "Complex Data Collective <developers@coda.co>",
|
|
7
7
|
"description": "Utilities for tracking analytics and error reporting in Fresco",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsup src/index.ts --format esm --dts --clean --sourcemap",
|
|
10
|
-
"lint": "eslint .",
|
|
11
|
-
"dev": "npm run build -- --watch"
|
|
12
|
-
},
|
|
13
8
|
"peerDependencies": {
|
|
14
9
|
"next": "13 || 14"
|
|
15
10
|
},
|
|
16
11
|
"devDependencies": {
|
|
17
|
-
"eslint-config-custom": "workspace:*",
|
|
18
|
-
"tsconfig": "workspace:*",
|
|
19
12
|
"tsup": "^7.2.0",
|
|
20
|
-
"typescript": "^5.3.2"
|
|
13
|
+
"typescript": "^5.3.2",
|
|
14
|
+
"eslint-config-custom": "0.0.0",
|
|
15
|
+
"tsconfig": "0.0.0"
|
|
21
16
|
},
|
|
22
17
|
"dependencies": {
|
|
23
18
|
"@maxmind/geoip2-node": "^5.0.0"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format esm --dts --clean --sourcemap",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"dev": "npm run build -- --watch"
|
|
24
24
|
}
|
|
25
|
-
}
|
|
25
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -12,8 +12,7 @@ export type AnalyticsEventBase = {
|
|
|
12
12
|
| "InterviewStarted"
|
|
13
13
|
| "ProtocolInstalled"
|
|
14
14
|
| "AppSetup"
|
|
15
|
-
| "Error"
|
|
16
|
-
| string;
|
|
15
|
+
| "Error";
|
|
17
16
|
};
|
|
18
17
|
|
|
19
18
|
export type AnalyticsEvent = AnalyticsEventBase & {
|
|
@@ -21,8 +20,7 @@ export type AnalyticsEvent = AnalyticsEventBase & {
|
|
|
21
20
|
| "InterviewCompleted"
|
|
22
21
|
| "InterviewStarted"
|
|
23
22
|
| "ProtocolInstalled"
|
|
24
|
-
| "AppSetup"
|
|
25
|
-
| string;
|
|
23
|
+
| "AppSetup";
|
|
26
24
|
metadata?: Record<string, unknown>;
|
|
27
25
|
};
|
|
28
26
|
|
|
@@ -63,68 +61,103 @@ export const createRouteHandler = ({
|
|
|
63
61
|
WebServiceClient,
|
|
64
62
|
}: RouteHandlerConfiguration) => {
|
|
65
63
|
return async (request: NextRequest) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
try {
|
|
65
|
+
const maxMindClient = new WebServiceClient(
|
|
66
|
+
maxMindAccountId,
|
|
67
|
+
maxMindLicenseKey,
|
|
68
|
+
{
|
|
69
|
+
host: "geolite.info",
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const installationId = await getInstallationId();
|
|
74
|
+
|
|
75
|
+
const event =
|
|
76
|
+
(await request.json()) as AnalyticsEventOrErrorWithTimestamp;
|
|
77
|
+
|
|
78
|
+
const ip = await fetch("https://api64.ipify.org").then((res) =>
|
|
79
|
+
res.text()
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const { country } = await maxMindClient.country(ip);
|
|
83
|
+
const countryCode = country?.isoCode ?? "Unknown";
|
|
84
|
+
|
|
85
|
+
const dispatchableEvent: DispatchableAnalyticsEvent = {
|
|
86
|
+
...event,
|
|
87
|
+
installationId,
|
|
88
|
+
geolocation: {
|
|
89
|
+
countryCode,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
console.log(dispatchableEvent);
|
|
94
|
+
|
|
95
|
+
// Forward to microservice
|
|
96
|
+
const response = await fetch(`${platformUrl}/api/event`, {
|
|
97
|
+
keepalive: true,
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: {
|
|
100
|
+
"Content-Type": "application/json",
|
|
101
|
+
},
|
|
102
|
+
body: JSON.stringify(dispatchableEvent),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Failed to forward event to microservice: ${response.statusText}`
|
|
108
|
+
);
|
|
71
109
|
}
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const installationId = await getInstallationId();
|
|
75
|
-
|
|
76
|
-
const event = (await request.json()) as AnalyticsEventOrErrorWithTimestamp;
|
|
77
|
-
|
|
78
|
-
const ip = await fetch("https://api64.ipify.org").then((res) => res.text());
|
|
79
|
-
|
|
80
|
-
const { country } = await maxMindClient.country(ip);
|
|
81
|
-
const countryCode = country?.isoCode ?? "Unknown";
|
|
82
110
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
message: "This is the API route",
|
|
105
|
-
});
|
|
111
|
+
return new Response(
|
|
112
|
+
JSON.stringify({ message: "Event forwarded successfully" }),
|
|
113
|
+
{
|
|
114
|
+
status: 200,
|
|
115
|
+
headers: {
|
|
116
|
+
"Content-Type": "application/json",
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
const error = ensureError(e);
|
|
122
|
+
console.error("Error in route handler:", error);
|
|
123
|
+
|
|
124
|
+
// Return an appropriate error response
|
|
125
|
+
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
|
|
126
|
+
status: 500,
|
|
127
|
+
headers: {
|
|
128
|
+
"Content-Type": "application/json",
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
106
132
|
};
|
|
107
133
|
};
|
|
108
134
|
|
|
109
135
|
export const makeEventTracker =
|
|
110
136
|
({ endpoint }: { endpoint: string }) =>
|
|
111
|
-
(event: AnalyticsEventOrError) => {
|
|
137
|
+
async (event: AnalyticsEventOrError) => {
|
|
112
138
|
const eventWithTimeStamp = {
|
|
113
139
|
...event,
|
|
114
140
|
timestamp: new Date(),
|
|
115
141
|
};
|
|
116
142
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
143
|
+
try {
|
|
144
|
+
const response = await fetch(endpoint, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
keepalive: true,
|
|
147
|
+
body: JSON.stringify(eventWithTimeStamp),
|
|
148
|
+
headers: {
|
|
149
|
+
"Content-Type": "application/json",
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Failed to send analytics event: ${response.statusText}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
} catch (e) {
|
|
125
159
|
const error = ensureError(e);
|
|
126
160
|
|
|
127
|
-
// eslint-disable-next-line no-console
|
|
128
161
|
console.error("Error sending analytics event:", error.message);
|
|
129
|
-
}
|
|
162
|
+
}
|
|
130
163
|
};
|
package/.turbo/turbo-test.log
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @codaco/analytics@0.6.0-alpha test /Users/jmh629/Projects/error-analytics-microservice/packages/analytics
|
|
3
|
-
> NODE_OPTIONS=--experimental-vm-modules jest "--watch"
|
|
4
|
-
|
|
5
|
-
(node:55883) ExperimentalWarning: VM Modules is an experimental feature and might change at any time
|
|
6
|
-
(Use `node --trace-warnings ...` to show where the warning was created)
|
|
7
|
-
FAIL src/index.test.mjs
|
|
8
|
-
Analytics client package
|
|
9
|
-
✕ should be able to create a new client (1 ms)
|
|
10
|
-
|
|
11
|
-
● Analytics client package › should be able to create a new client
|
|
12
|
-
|
|
13
|
-
TypeError: Cannot read properties of undefined (reading 'platformUrl')
|
|
14
|
-
|
|
15
|
-
50 | private platformUrl?: string = "https://analytics.networkcanvas.dev";
|
|
16
|
-
51 | private installationId: string | null = null;
|
|
17
|
-
> 52 |
|
|
18
|
-
| ^
|
|
19
|
-
53 | private dispatchQueue: QueueObject<AnalyticsEventOrError>;
|
|
20
|
-
54 |
|
|
21
|
-
55 | private enabled: boolean = true;
|
|
22
|
-
|
|
23
|
-
at new s (src/index.ts:52:11)
|
|
24
|
-
at Object.<anonymous> (src/index.test.mjs:6:20)
|
|
25
|
-
|
|
26
|
-
Test Suites: 1 failed, 1 total
|
|
27
|
-
Tests: 1 failed, 1 total
|
|
28
|
-
Snapshots: 0 total
|
|
29
|
-
Time: 0.215 s, estimated 1 s
|
|
30
|
-
Ran all test suites related to changed files.
|
|
31
|
-
|