Skip to content

Commit 2208a3c

Browse files
authored
Record usage events where permission granted (#227)
* Record usage events where permission granted Where the user has possibly granted permission for us to record usage data, we will now make a HTTPS request to an RD server, containing only the client's uuid (generated by either us or the prototype kit depending on permissions) and the name of the event. If the recording fails, then we will not interupt the usage of the prototype at all, instead just recording the failure in the user's terminal. * Clarify comment
1 parent 639fe6e commit 2208a3c

2 files changed

Lines changed: 86 additions & 22 deletions

File tree

lib/importer/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ exports.Initialise = (config, router, prototypeKit) => {
4040
const usageRecorder = new usage.UsageCollection(plugin_config)
4141

4242
// Record initialisation of the plugin
43-
usageRecorder.recordEvent("Plugin initialised")
43+
usageRecorder.recordEvent("initialised")
4444

4545
//--------------------------------------------------------------------
4646
// To be able to add a local views folder, we need to update the
@@ -346,7 +346,7 @@ exports.Initialise = (config, router, prototypeKit) => {
346346
(request, response) => {
347347

348348
// Record initialisation of the plugin
349-
usageRecorder.recordEvent("Plugin configured")
349+
usageRecorder.recordEvent("configured")
350350

351351

352352
const pc = {

lib/importer/src/usage.js

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
const fs = require('node:fs');
1313
const path = require('node:path');
14-
14+
const httplib = require('https');
15+
const { URL } = require('url');
1516

1617
exports.UsageCollection = class {
1718
constructor() {
@@ -21,18 +22,97 @@ exports.UsageCollection = class {
2122
}
2223

2324
setPermission(perm) {
24-
updateDUDKPermission(perm)
25+
this.updateDUDKPermission(perm)
2526
this.enabled = perm
2627
}
2728

29+
updateDUDKPermission(dudkPermission) {
30+
const projectDir = path.resolve(projectDirectory() || process.cwd());
31+
const usage_config = path.join(projectDir, "usage-data-config.json")
32+
33+
// When running in production mode we don't allow the configuration page
34+
// to make any changes and so if there is no usage file we will return
35+
// immediately.
36+
if (!fs.existsSync(usage_config)) {
37+
return
38+
}
39+
40+
const data = fs.readFileSync(usage_config);
41+
const obj = JSON.parse(data)
42+
obj.dudkCollectUsageData = dudkPermission
43+
44+
// If no clientid because the user said no to the prototype kit,
45+
// then we will create and save one.
46+
if (!('clientId' in obj)) {
47+
obj.clientId = crypto.randomUUID();
48+
}
49+
50+
51+
fs.writeFileSync(usage_config, JSON.stringify(obj))
52+
}
53+
54+
55+
2856
recordEvent(eventName) {
2957
if (!this.enabled) return false;
3058

31-
//TODO: Actually record the usage against eventName
32-
console.log("EVENT: " + eventName)
59+
// Make the request in a block that will allow us to return
60+
// immediately whilst the event is recorded
61+
(async () => {
62+
const response = await this.create_event_request(eventName, this.clientId);
63+
if (response.error) {
64+
console.log("Failed to record event");
65+
}
66+
})();
67+
3368

3469
return eventName != ""
3570
}
71+
72+
// Create a promise that will make our HTTP request for us, ensuring
73+
// that it will swallow any errors so that we don't interupt the operation
74+
// of the prototype.
75+
async create_event_request(eventName, clientID) {
76+
// Creates a path that acts as our event record. For now this is
77+
// just a simple path with no extra payload.
78+
const url = new URL(`https://union.register-dynamics.co.uk/dudk-metrics/${clientID}/${eventName}`);
79+
80+
return new Promise((resolve) => {
81+
try {
82+
83+
const options = {
84+
method: "GET",
85+
hostname: url.hostname,
86+
path: url.pathname + url.search,
87+
port: 443,
88+
};
89+
90+
const req = httplib.request(options, (res) => {
91+
res.on('end', () => {
92+
resolve({
93+
statusCode: res.statusCode,
94+
headers: res.headers,
95+
});
96+
});
97+
});
98+
99+
req.on('error', (err) => {
100+
resolve({
101+
error: true,
102+
message: err.message,
103+
});
104+
});
105+
106+
req.end();
107+
} catch (err) {
108+
resolve({
109+
error: true,
110+
message: err.message,
111+
});
112+
}
113+
});
114+
}
115+
36116
}
37117

38118
// KIT_PROJECT_DIR contains the app folder, and so if it exists in prod or dev
@@ -44,8 +124,6 @@ const projectDirectory = () => {
44124
}
45125

46126
const prototypeKitUsageCollectionSettings = () => {
47-
48-
49127
const projectDir = path.resolve(projectDirectory() || process.cwd());
50128
const usage_config = path.join(projectDir, "usage-data-config.json")
51129
if (!fs.existsSync(usage_config)) {
@@ -56,18 +134,4 @@ const prototypeKitUsageCollectionSettings = () => {
56134
return JSON.parse(data)
57135
}
58136

59-
const updateDUDKPermission = (dudkPermission) => {
60-
const projectDir = path.resolve(projectDirectory() || process.cwd());
61-
const usage_config = path.join(projectDir, "usage-data-config.json")
62-
63-
// We are running in production mode, usage tracking now enabled here.
64-
if (!fs.existsSync(usage_config)) {
65-
return
66-
}
67-
68-
const data = fs.readFileSync(usage_config);
69-
const obj = JSON.parse(data)
70-
obj.dudkCollectUsageData = dudkPermission
71137

72-
fs.writeFileSync(usage_config, JSON.stringify(obj))
73-
}

0 commit comments

Comments
 (0)