Skip to content

Commit cb8b21c

Browse files
committed
feat: 🎸 Add sqlite support for session recordings (#2974)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17419
1 parent 47c1bea commit cb8b21c

File tree

4 files changed

+96
-7
lines changed

4 files changed

+96
-7
lines changed

‎addons/api/addon/handlers/sqlite-handler.js‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import { typeOf } from '@ember/utils';
1111
import { hashCode } from '../utils/hash-code';
1212
import { generateSQLExpressions } from '../utils/sqlite-query';
1313

14+
const isISODateString = (str) =>
15+
typeOf(str) === 'string' &&
16+
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z?$/.test(str);
17+
1418
export default class SqliteHandler {
1519
@service sqlite;
1620

@@ -138,7 +142,13 @@ export default class SqliteHandler {
138142
});
139143
console.timeLog(`SQLite fetch ${type}`, 'count');
140144

141-
const results = rows.map((item) => JSON.parse(item.data));
145+
const results = rows.map((item) =>
146+
JSON.parse(item.data, (key, value) =>
147+
// Reviver function to convert any ISO strings back to date objects
148+
// as we had to initially convert them to strings when stored in SQLite
149+
isISODateString(value) ? new Date(value) : value,
150+
),
151+
);
142152

143153
// If we are not pushing to the store, use the raw data with id property
144154
const records = pushToStore

‎addons/api/addon/services/sqlite.js‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,22 @@ export const modelMapping = {
8383
scope_id: 'scope.scope_id',
8484
created_time: 'created_time',
8585
},
86+
'session-recording': {
87+
id: 'id',
88+
type: 'type',
89+
state: 'state',
90+
start_time: 'start_time',
91+
end_time: 'end_time',
92+
duration: 'duration',
93+
scope_id: 'scope.scope_id',
94+
user_id: 'create_time_values.user.id',
95+
user_name: 'create_time_values.user.name',
96+
target_id: 'create_time_values.target.id',
97+
target_name: 'create_time_values.target.name',
98+
target_scope_id: 'create_time_values.target.scope.id',
99+
target_scope_name: 'create_time_values.target.scope.name',
100+
created_time: 'created_time',
101+
},
86102
};
87103

88104
// A list of tables that we support searching using FTS5 in SQLite.
@@ -96,6 +112,7 @@ export const searchTables = new Set([
96112
'scope',
97113
'auth-method',
98114
'host-catalog',
115+
'session-recording',
99116
]);
100117

101118
export default class SqliteDbService extends Service {

‎addons/api/addon/workers/utils/schema.js‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,57 @@ CREATE TRIGGER IF NOT EXISTS host_catalog_ad AFTER DELETE ON host_catalog BEGIN
337337
VALUES('delete', old.rowid, old.id, old.type, old.name, old.description, old.plugin_name, old.scope_id, old.created_time);
338338
END;`;
339339

340+
const createSessionRecordingTables = `
341+
CREATE TABLE IF NOT EXISTS session_recording (
342+
id TEXT NOT NULL PRIMARY KEY,
343+
type TEXT NOT NULL,
344+
state TEXT,
345+
start_time TEXT,
346+
end_time TEXT,
347+
duration TEXT,
348+
scope_id TEXT NOT NULL,
349+
user_id TEXT,
350+
user_name TEXT,
351+
target_id TEXT,
352+
target_name TEXT,
353+
target_scope_id TEXT,
354+
target_scope_name TEXT,
355+
created_time TEXT NOT NULL,
356+
data TEXT NOT NULL
357+
);
358+
CREATE INDEX IF NOT EXISTS idx_session_recording_created_time ON session_recording(created_time DESC);
359+
360+
CREATE VIRTUAL TABLE IF NOT EXISTS session_recording_fts USING fts5(
361+
id,
362+
type,
363+
state,
364+
start_time,
365+
end_time,
366+
duration,
367+
scope_id,
368+
user_id,
369+
user_name,
370+
target_id,
371+
target_name,
372+
target_scope_id,
373+
target_scope_name,
374+
created_time,
375+
content='',
376+
);
377+
378+
CREATE TRIGGER IF NOT EXISTS session_recording_ai AFTER INSERT ON session_recording BEGIN
379+
INSERT INTO session_recording_fts(
380+
id, type, state, start_time, end_time, duration, scope_id, user_id, user_name, target_id, target_name, target_scope_id, target_scope_name, created_time
381+
) VALUES (
382+
new.id, new.type, new.state, new.start_time, new.end_time, new.duration, new.scope_id, new.user_id, new.user_name, new.target_id, new.target_name, new.target_scope_id, new.target_scope_name, new.created_time
383+
);
384+
END;
385+
386+
CREATE TRIGGER IF NOT EXISTS session_recording_ad AFTER DELETE ON session_recording BEGIN
387+
INSERT INTO session_recording_fts(session_recording_fts, rowid, id, type, state, start_time, end_time, duration, scope_id, user_id, user_name, target_id, target_name, target_scope_id, target_scope_name, created_time)
388+
VALUES('delete', old.rowid, old.id, old.type, old.state, old.start_time, old.end_time, old.duration, old.scope_id, old.user_id, old.user_name, old.target_id, old.target_name, old.target_scope_id, old.target_scope_name, old.created_time);
389+
END;`;
390+
340391
export const CREATE_TABLES = (version) => `
341392
BEGIN;
342393
@@ -356,6 +407,17 @@ ${createCredentialStoreTables}
356407
${createScopeTables}
357408
${createAuthMethodTables}
358409
${createHostCatalogTables}
410+
${createSessionRecordingTables}
411+
412+
CREATE TABLE IF NOT EXISTS "group" (
413+
id TEXT NOT NULL PRIMARY KEY,
414+
name TEXT,
415+
description TEXT,
416+
scope_id TEXT NOT NULL,
417+
created_time TEXT NOT NULL,
418+
data TEXT NOT NULL
419+
);
420+
CREATE INDEX IF NOT EXISTS idx_group_scope_id_created_time ON "group"(scope_id, created_time DESC);
359421
360422
COMMIT;`;
361423

‎ui/admin/app/routes/scopes/scope/session-recordings/index.js‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,19 @@ export default class ScopesScopeSessionRecordingsIndexRoute extends Route {
9696
let doStorageBucketsExist = false;
9797
const filters = {
9898
created_time: [],
99-
'create_time_values.user.id': [],
100-
'create_time_values.target.scope.id': [],
101-
'create_time_values.target.id': [],
99+
user_id: [],
100+
target_scope_id: [],
101+
target_id: [],
102102
};
103103
if (time) filters.created_time.push({ gte: new Date(time) });
104104
users.forEach((user) => {
105-
filters['create_time_values.user.id'].push({ equals: user });
105+
filters['user_id'].push({ equals: user });
106106
});
107107
scopes.forEach((scope) => {
108-
filters['create_time_values.target.scope.id'].push({ equals: scope });
108+
filters['target_scope_id'].push({ equals: scope });
109109
});
110110
targets.forEach((target) => {
111-
filters['create_time_values.target.id'].push({ equals: target });
111+
filters['target_id'].push({ equals: target });
112112
});
113113

114114
const stateMap = {

0 commit comments

Comments
 (0)