From 966ec2a17c3c30a029ee4e7333372a56c31fc8ba Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 27 Apr 2022 16:34:00 +0800 Subject: [PATCH 1/8] add workshop button --- dbux-code/package.json | 10 ++++++- dbux-code/src/preActivate.js | 18 ++++++++++++- dbux-code/src/preActivateView/ActivateNode.js | 2 +- dbux-code/src/preActivateView/WorkshopNode.js | 20 ++++++++++++++ .../preActivateNodeProvider.js | 5 ++-- dbux-code/src/workshop/Workshop.js | 27 +++++++++++++++++++ 6 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 dbux-code/src/preActivateView/WorkshopNode.js create mode 100644 dbux-code/src/workshop/Workshop.js diff --git a/dbux-code/package.json b/dbux-code/package.json index 97b087aa0..c289917e9 100644 --- a/dbux-code/package.json +++ b/dbux-code/package.json @@ -87,7 +87,11 @@ "commands": [ { "command": "dbux.doActivate", - "title": "Dbux: Start Dbux" + "title": "Dbux: Start Dbux (no workshop)" + }, + { + "command": "dbux.doWorkshopActivate", + "title": "Dbux: Start Workshop Session" }, { "command": "dbux.diagnostics", @@ -1063,6 +1067,10 @@ "command": "dbux.doActivate", "when": "!dbux.context.activated" }, + { + "command": "dbux.doWorkshopActivate", + "when": "!dbux.context.activated" + }, { "command": "dbux.backendLogin", "when": "dbux.context.activated" diff --git a/dbux-code/src/preActivate.js b/dbux-code/src/preActivate.js index 408f9be90..32dd0f305 100644 --- a/dbux-code/src/preActivate.js +++ b/dbux-code/src/preActivate.js @@ -1,4 +1,4 @@ -import { workspace, commands } from 'vscode'; +import { workspace, commands, window } from 'vscode'; import { newLogger } from '@dbux/common/src/log/logger'; import { initMemento, get as mementoGet, set as mementoSet } from './memento'; import { initInstallId } from './installId'; @@ -9,6 +9,8 @@ import { initPreActivateView } from './preActivateView/preActivateNodeProvider'; import { registerCommand } from './commands/commandUtil'; import initLang from './lang'; import { getCurrentResearch } from './research/Research'; +import { activateWorkshopSession, isValidCode } from './workshop/Workshop'; +import { showInformationMessage } from './codeUtil/codeModals'; /** @typedef {import('./dialogs/dialogController').DialogController} DialogController */ @@ -107,6 +109,20 @@ async function ensureActivate(context) { */ function initPreActivateCommand(context) { registerCommand(context, 'dbux.doActivate', async () => ensureActivate(context)); + + registerCommand(context, 'dbux.doWorkshopActivate', async () => { + const code = await window.showInputBox({ + ignoreFocusOut: true, + placeHolder: 'Enter Workshop Code' + }); + if (isValidCode(code)) { + activateWorkshopSession(code); + await ensureActivate(context); + } + else { + await showInformationMessage(`Workshop code ${code} is invalid`); + } + }); } function registerErrorHandler() { diff --git a/dbux-code/src/preActivateView/ActivateNode.js b/dbux-code/src/preActivateView/ActivateNode.js index 4c1daf664..5ad35bcdd 100644 --- a/dbux-code/src/preActivateView/ActivateNode.js +++ b/dbux-code/src/preActivateView/ActivateNode.js @@ -1,6 +1,6 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; -export class ActivateNode extends TreeItem { +export default class ActivateNode extends TreeItem { constructor() { super('Start Dbux', TreeItemCollapsibleState.None); diff --git a/dbux-code/src/preActivateView/WorkshopNode.js b/dbux-code/src/preActivateView/WorkshopNode.js new file mode 100644 index 000000000..361ab0370 --- /dev/null +++ b/dbux-code/src/preActivateView/WorkshopNode.js @@ -0,0 +1,20 @@ +import { TreeItem, TreeItemCollapsibleState } from 'vscode'; + +export default class WorkshopNode extends TreeItem { + constructor() { + super('Start Dbux', TreeItemCollapsibleState.None); + + this.command = { + command: 'dbux.doWorkshopActivate' + }; + } + + makeIconPath() { + return 'play.svg'; + } + + // singleton + static get instance() { + return WorkshopNode._instance = (WorkshopNode._instance || new WorkshopNode()); + } +} \ No newline at end of file diff --git a/dbux-code/src/preActivateView/preActivateNodeProvider.js b/dbux-code/src/preActivateView/preActivateNodeProvider.js index 065d7e454..def58b34f 100644 --- a/dbux-code/src/preActivateView/preActivateNodeProvider.js +++ b/dbux-code/src/preActivateView/preActivateNodeProvider.js @@ -1,5 +1,6 @@ import BaseTreeViewNodeProvider from '../codeUtil/treeView/BaseTreeViewNodeProvider'; -import { ActivateNode } from './ActivateNode'; +import ActivateNode from './ActivateNode'; +import WorkshopNode from './WorkshopNode'; class PreActivateNodeProvider extends BaseTreeViewNodeProvider { /** @@ -10,7 +11,7 @@ class PreActivateNodeProvider extends BaseTreeViewNodeProvider { } buildRoots() { - return [ActivateNode.instance]; + return [ActivateNode.instance, WorkshopNode.instance]; } } diff --git a/dbux-code/src/workshop/Workshop.js b/dbux-code/src/workshop/Workshop.js new file mode 100644 index 000000000..4e7802b4b --- /dev/null +++ b/dbux-code/src/workshop/Workshop.js @@ -0,0 +1,27 @@ +const ValidCodes = new Set([]); +let currentCode = ''; + +/** + * @param {string} code + * @returns {boolean} + */ +export function isValidCode(code) { + return ValidCodes.has(code); +} + +export function activateWorkshopSession(code) { + if (isValidCode(code)) { + currentCode = code; + } + else { + throw new Error('Invalid workshop code'); + } +} + +export function isWorkshopSessionActive() { + return !!currentCode; +} + +export function getCurrentWorkshopCode() { + return currentCode; +} From d743a2a1bbb4d80599a0e94511a0590bcff38c81 Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 27 Apr 2022 17:59:08 +0800 Subject: [PATCH 2/8] WIP: add `PathwaysDataBuffer` --- dbux-code/src/activate.js | 5 +- dbux-code/src/dialogs/survey1.js | 4 +- dbux-code/src/preActivate.js | 12 +-- dbux-code/src/preActivateView/WorkshopNode.js | 2 +- dbux-code/src/workshop/PathwaysDataBuffer.js | 80 +++++++++++++++++++ dbux-code/src/workshop/SafetyStorage.js | 48 +++++++++++ dbux-code/src/workshop/Workshop.js | 27 ------- dbux-code/src/workshop/index.js | 61 ++++++++++++++ dbux-projects/src/ProjectsManager.js | 4 +- dbux-projects/src/firestore/upload.js | 24 +++++- 10 files changed, 225 insertions(+), 42 deletions(-) create mode 100644 dbux-code/src/workshop/PathwaysDataBuffer.js create mode 100644 dbux-code/src/workshop/SafetyStorage.js delete mode 100644 dbux-code/src/workshop/Workshop.js create mode 100644 dbux-code/src/workshop/index.js diff --git a/dbux-code/src/activate.js b/dbux-code/src/activate.js index 58b2eeeb4..5b1418188 100644 --- a/dbux-code/src/activate.js +++ b/dbux-code/src/activate.js @@ -21,6 +21,7 @@ import { initDialogController } from './dialogs/dialogController'; import DialogNodeKind from './dialogs/DialogNodeKind'; import { showInformationMessage } from './codeUtil/codeModals'; import { translate } from './lang'; +import { initWorkshopSession } from './workshop'; // import { initPlugins } from './PluginMgr'; // eslint-disable-next-line no-unused-vars @@ -34,7 +35,9 @@ export default async function activate(context) { log(`Starting Dbux v${process.env.DBUX_VERSION} (mode=${process.env.NODE_ENV}${dbuxRoot})...`); // make sure, projectManager is available - createProjectManager(context); + const projectsManager = createProjectManager(context); + + await initWorkshopSession(projectsManager); // install dependencies (and show progress bar) right away await installDbuxDependencies(); diff --git a/dbux-code/src/dialogs/survey1.js b/dbux-code/src/dialogs/survey1.js index 0e59db85a..e1314bcb9 100644 --- a/dbux-code/src/dialogs/survey1.js +++ b/dbux-code/src/dialogs/survey1.js @@ -1,7 +1,7 @@ /* eslint-disable max-len */ import { env, Uri, window } from 'vscode'; import ExerciseStatus from '@dbux/projects/src/dataLib/ExerciseStatus'; -import { upload } from '@dbux/projects/src/firestore/upload'; +import { uploadSurvey } from '@dbux/projects/src/firestore/upload'; import { newLogger } from '@dbux/common/src/log/logger'; import { showHelp } from '../help'; import DialogNodeKind from './DialogNodeKind'; @@ -458,7 +458,7 @@ ${data.email || ''}`; log('survey result', data); // store to backend - await upload('survey1', data); + await uploadSurvey(data); // const backend = await getProjectManager().getAndInitBackend(); // return backend.containers.survey1.storeSurveyResult(data); }, diff --git a/dbux-code/src/preActivate.js b/dbux-code/src/preActivate.js index 32dd0f305..1862c4af1 100644 --- a/dbux-code/src/preActivate.js +++ b/dbux-code/src/preActivate.js @@ -9,7 +9,7 @@ import { initPreActivateView } from './preActivateView/preActivateNodeProvider'; import { registerCommand } from './commands/commandUtil'; import initLang from './lang'; import { getCurrentResearch } from './research/Research'; -import { activateWorkshopSession, isValidCode } from './workshop/Workshop'; +import { setupWorkshopSession, isValidCode } from './workshop'; import { showInformationMessage } from './codeUtil/codeModals'; /** @typedef {import('./dialogs/dialogController').DialogController} DialogController */ @@ -70,8 +70,10 @@ export default async function preActivate(context) { commands.executeCommand('setContext', 'dbux.context.researchEnabled', !!process.env.RESEARCH_ENABLED); // the following should ensures `doActivate` will be called at least once - autoStart = (process.env.NODE_ENV === 'development') || - workspace.getConfiguration('dbux').get('autoStart'); + // autoStart = (process.env.NODE_ENV === 'development') || + // workspace.getConfiguration('dbux').get('autoStart'); + // TODO-M: recover this + autoStart = workspace.getConfiguration('dbux').get('autoStart'); if (autoStart) { await ensureActivate(context); } @@ -116,11 +118,11 @@ function initPreActivateCommand(context) { placeHolder: 'Enter Workshop Code' }); if (isValidCode(code)) { - activateWorkshopSession(code); + setupWorkshopSession(code); await ensureActivate(context); } else { - await showInformationMessage(`Workshop code ${code} is invalid`); + await showInformationMessage(`Workshop code "${code}" is invalid.`, { modal: true }); } }); } diff --git a/dbux-code/src/preActivateView/WorkshopNode.js b/dbux-code/src/preActivateView/WorkshopNode.js index 361ab0370..28abc4380 100644 --- a/dbux-code/src/preActivateView/WorkshopNode.js +++ b/dbux-code/src/preActivateView/WorkshopNode.js @@ -2,7 +2,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; export default class WorkshopNode extends TreeItem { constructor() { - super('Start Dbux', TreeItemCollapsibleState.None); + super('Start Workshop Session', TreeItemCollapsibleState.None); this.command = { command: 'dbux.doWorkshopActivate' diff --git a/dbux-code/src/workshop/PathwaysDataBuffer.js b/dbux-code/src/workshop/PathwaysDataBuffer.js new file mode 100644 index 000000000..3753b4b72 --- /dev/null +++ b/dbux-code/src/workshop/PathwaysDataBuffer.js @@ -0,0 +1,80 @@ + +import { newLogger } from '@dbux/common/src/log/logger'; +import NestedError from '@dbux/common/src/NestedError'; +import { uploadPathways } from '@dbux/projects/src/firestore/upload'; +import SafetyStorage from './SafetyStorage'; + +// eslint-disable-next-line no-unused-vars +const { log, debug, warn, error: logError } = newLogger('PathwaysDataBuffer'); + +const DefaultBufferSize = 50; +const DefaultBufferFlushTime = 2 * 60 * 1000; // 2 minutes + +export default class PathwaysDataBuffer { + constructor(sessionId, collectionName) { + const storageKeyName = `dbux.pathwaysDataBuffer.${collectionName}`; + this.buffer = new SafetyStorage(storageKeyName); + this.sessionId = sessionId; + this.collectionName = collectionName; + this._previousFlushTime = Date.now(); + } + + /** + * @return {Array} + */ + safeGetBuffer() { + return this.buffer.get() || []; + } + + async add(entries) { + await this.buffer.acquireLock(); + + try { + let buffer = this.safeGetBuffer(); + buffer.push(...entries); + await this.buffer.set(buffer); + } + finally { + this.buffer.releaseLock(); + } + } + + async maybeFlush() { + if (!this._flushing && (this.safeGetBuffer().length >= DefaultBufferSize || Date.now() - this._previousFlushTime >= DefaultBufferFlushTime)) { + await this.forceFlush(); + } + } + + async forceFlush() { + this._flushing = true; + await this._flush(); + } + + async _flush() { + this._flushing = true; + await this.buffer.acquireLock(); + + let buffer; + try { + buffer = this.safeGetBuffer(); + await this.buffer.set([]); + } + finally { + this.buffer.releaseLock(); + } + + try { + await this.addDoc(buffer); + } + catch (err) { + this._flushing = false; + throw new NestedError(`Failed when flushing`, err); + } + + this._previousFlushTime = Date.now(); + } + + async addDoc(entries) { + return await uploadPathways(this.sessionId, this.collectionName, entries); + } +} \ No newline at end of file diff --git a/dbux-code/src/workshop/SafetyStorage.js b/dbux-code/src/workshop/SafetyStorage.js new file mode 100644 index 000000000..83446990d --- /dev/null +++ b/dbux-code/src/workshop/SafetyStorage.js @@ -0,0 +1,48 @@ +import os from 'os'; +import path from 'path'; +import lockfile from 'lockfile'; +import { newLogger } from '@dbux/common/src/log/logger'; +import { get, set } from '../memento'; + +// eslint-disable-next-line no-unused-vars +const { log, debug, warn, error: logError } = newLogger('SavetyStorage'); + +function getLockfileName(name) { + const lockfilePath = path.join(os.tmpdir(), `dbux-lockfile.${name}`); + return lockfilePath; +} + +async function acquireLock(name) { + return new Promise((resolve, reject) => { + lockfile.lock(getLockfileName(name), { wait: 10 ** 9 }, (err) => { + if (err) { + reject(err); + } + else { + resolve(); + } + }); + }); +} + +export default class SafetyStorage { + constructor(name) { + this.name = name; + } + + async acquireLock() { + return acquireLock(this.name); + } + + releaseLock() { + lockfile.unlockSync(getLockfileName(this.name)); + } + + get() { + return get(this.name); + } + + async set(value) { + await set(this.name, value); + } +} diff --git a/dbux-code/src/workshop/Workshop.js b/dbux-code/src/workshop/Workshop.js deleted file mode 100644 index 4e7802b4b..000000000 --- a/dbux-code/src/workshop/Workshop.js +++ /dev/null @@ -1,27 +0,0 @@ -const ValidCodes = new Set([]); -let currentCode = ''; - -/** - * @param {string} code - * @returns {boolean} - */ -export function isValidCode(code) { - return ValidCodes.has(code); -} - -export function activateWorkshopSession(code) { - if (isValidCode(code)) { - currentCode = code; - } - else { - throw new Error('Invalid workshop code'); - } -} - -export function isWorkshopSessionActive() { - return !!currentCode; -} - -export function getCurrentWorkshopCode() { - return currentCode; -} diff --git a/dbux-code/src/workshop/index.js b/dbux-code/src/workshop/index.js new file mode 100644 index 000000000..1249cfd16 --- /dev/null +++ b/dbux-code/src/workshop/index.js @@ -0,0 +1,61 @@ +/** @typedef {import('@dbux/projects/src/ProjectsManager').default} ProjectsManager */ + +const ValidCodes = new Set([]); +let currentCode = ''; + +if (process.env.NODE_ENV === 'development') { + ValidCodes.add('1234'); +} + +/** + * @param {string} code + * @returns {boolean} + */ +export function isValidCode(code) { + return ValidCodes.has(code); +} + +export function isWorkshopSessionActive() { + return !!currentCode; +} + +export function setupWorkshopSession(code) { + if (isValidCode(code)) { + currentCode = code; + } + else { + throw new Error('Invalid workshop code'); + } +} + +/** ########################################################################### + * pdp listener and log writing + * #########################################################################*/ + +/** + * @param {ProjectsManager} projectsManager + * @returns + */ +export async function initWorkshopSession(projectsManager) { + if (isWorkshopSessionActive()) { + addHook(); + projectsManager.onPracticeSessionStateChanged(addHook); + } +} + +let sessionId, prevListener; + +function addHook(session) { + if (sessionId !== session?.sessionId) { + // stop listening on previous events + prevListener?.(); + + prevListener = session?.pdp.onAnyData(addData); + } +} + +function addData(allData) { + for (const collectionName of Object.keys(allData)) { + // TODO-M: push data into buffer + } +} \ No newline at end of file diff --git a/dbux-projects/src/ProjectsManager.js b/dbux-projects/src/ProjectsManager.js index aea57a859..123e1e229 100644 --- a/dbux-projects/src/ProjectsManager.js +++ b/dbux-projects/src/ProjectsManager.js @@ -518,8 +518,8 @@ export default class ProjectsManager { await this.practiceSession.init(); - isNew && emitPracticeSessionEvent('started', this.practiceSession); this._notifyPracticeSessionStateChanged(); + isNew && emitPracticeSessionEvent('started', this.practiceSession); await this.saveSession(); } @@ -601,7 +601,7 @@ export default class ProjectsManager { } _notifyPracticeSessionStateChanged() { - this._emitter.emit('practiceSessionStateChanged'); + this._emitter.emit('practiceSessionStateChanged', this.practiceSession); } // ######################################## diff --git a/dbux-projects/src/firestore/upload.js b/dbux-projects/src/firestore/upload.js index a7912fefa..6f6df2997 100644 --- a/dbux-projects/src/firestore/upload.js +++ b/dbux-projects/src/firestore/upload.js @@ -4,12 +4,28 @@ import NestedError from '@dbux/common/src/NestedError'; const API_KEY = 'AIzaSyC-d0HDLJ8Gd9UZ175z7dg6J98ZrOIK0Mc'; -function getUrl(collectionId) { - return `https://firestore.googleapis.com/v1/projects/learn-learn-b8e5a/databases/(default)/documents/${collectionId}?key=${API_KEY}`; +function makeUrl(collectionId, documentId = null) { + const url = new URL(`https://firestore.googleapis.com`); + url.pathname = `/v1/projects/learn-learn-b8e5a/databases/(default)/documents/${collectionId}`; + url.searchParams.set('key', API_KEY); + if (documentId) { + url.searchParams.set('documentId', documentId); + } + return url.toString(); } -export async function upload(collectionId, data) { - const url = getUrl(collectionId); +export async function uploadSurvey(data) { + return await upload(makeUrl('survey1'), data); +} + +export async function uploadPathways(sessionId, collectionName, entries) { + const collectionId = `sessions/${sessionId}/${collectionName}`; + const documentId = null; + const url = makeUrl(collectionId, documentId); + return await upload(url, entries); +} + +export async function upload(url, data) { const serializedData = serialize(data); const dataString = JSON.stringify({ fields: serializedData }); From 28494fd889e82efccc36cca6c5c1ed06ac6645a0 Mon Sep 17 00:00:00 2001 From: MiccWan Date: Mon, 2 May 2022 16:00:15 +0800 Subject: [PATCH 3/8] upload Pathways data in workshop session --- dbux-code/src/preActivate.js | 10 +-- dbux-code/src/workshop/PathwaysDataBuffer.js | 65 +++++++++------ .../src/workshop/PathwaysDataContainer.js | 82 +++++++++++++++++++ dbux-code/src/workshop/SafetyStorage.js | 2 +- dbux-code/src/workshop/index.js | 40 +++++---- dbux-projects/src/userEvents/index.js | 4 +- 6 files changed, 156 insertions(+), 47 deletions(-) create mode 100644 dbux-code/src/workshop/PathwaysDataContainer.js diff --git a/dbux-code/src/preActivate.js b/dbux-code/src/preActivate.js index 1862c4af1..e6dc20ccb 100644 --- a/dbux-code/src/preActivate.js +++ b/dbux-code/src/preActivate.js @@ -1,5 +1,6 @@ import { workspace, commands, window } from 'vscode'; import { newLogger } from '@dbux/common/src/log/logger'; +import EmptyObject from '@dbux/common/src/util/EmptyObject'; import { initMemento, get as mementoGet, set as mementoSet } from './memento'; import { initInstallId } from './installId'; import { initLogging } from './logging'; @@ -70,10 +71,9 @@ export default async function preActivate(context) { commands.executeCommand('setContext', 'dbux.context.researchEnabled', !!process.env.RESEARCH_ENABLED); // the following should ensures `doActivate` will be called at least once - // autoStart = (process.env.NODE_ENV === 'development') || - // workspace.getConfiguration('dbux').get('autoStart'); - // TODO-M: recover this - autoStart = workspace.getConfiguration('dbux').get('autoStart'); + autoStart = (process.env.NODE_ENV === 'development') || + workspace.getConfiguration('dbux').get('autoStart'); + // autoStart = workspace.getConfiguration('dbux').get('autoStart'); if (autoStart) { await ensureActivate(context); } @@ -122,7 +122,7 @@ function initPreActivateCommand(context) { await ensureActivate(context); } else { - await showInformationMessage(`Workshop code "${code}" is invalid.`, { modal: true }); + await showInformationMessage(`Workshop code "${code}" is invalid.`, EmptyObject, { modal: true }); } }); } diff --git a/dbux-code/src/workshop/PathwaysDataBuffer.js b/dbux-code/src/workshop/PathwaysDataBuffer.js index 3753b4b72..9d9ac7b62 100644 --- a/dbux-code/src/workshop/PathwaysDataBuffer.js +++ b/dbux-code/src/workshop/PathwaysDataBuffer.js @@ -7,9 +7,16 @@ import SafetyStorage from './SafetyStorage'; // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('PathwaysDataBuffer'); -const DefaultBufferSize = 50; +// const Verbose = true; +const Verbose = false; + +const DefaultBufferSize = 10; const DefaultBufferFlushTime = 2 * 60 * 1000; // 2 minutes +// const DefaultBufferFlushTime = 1 * 1000; // 1 second +/** + * @template T + */ export default class PathwaysDataBuffer { constructor(sessionId, collectionName) { const storageKeyName = `dbux.pathwaysDataBuffer.${collectionName}`; @@ -26,11 +33,14 @@ export default class PathwaysDataBuffer { return this.buffer.get() || []; } + /** + * @param {T[]} entries + */ async add(entries) { await this.buffer.acquireLock(); try { - let buffer = this.safeGetBuffer(); + const buffer = this.safeGetBuffer(); buffer.push(...entries); await this.buffer.set(buffer); } @@ -41,40 +51,47 @@ export default class PathwaysDataBuffer { async maybeFlush() { if (!this._flushing && (this.safeGetBuffer().length >= DefaultBufferSize || Date.now() - this._previousFlushTime >= DefaultBufferFlushTime)) { - await this.forceFlush(); + await this.flush(); } } - async forceFlush() { - this._flushing = true; - await this._flush(); - } - - async _flush() { + async flush() { + Verbose && log(`Flushing collection ${this.collectionName}`); this._flushing = true; - await this.buffer.acquireLock(); - let buffer; try { - buffer = this.safeGetBuffer(); - await this.buffer.set([]); - } - finally { - this.buffer.releaseLock(); - } + await this.buffer.acquireLock(); - try { - await this.addDoc(buffer); + let buffer; + try { + buffer = this.safeGetBuffer(); + await this.buffer.set([]); + } + finally { + this.buffer.releaseLock(); + } + + try { + await this.addDoc(buffer); + } + catch (err) { + throw new NestedError(`Failed when flushing`, err); + } + + this._previousFlushTime = Date.now(); } - catch (err) { + finally { this._flushing = false; - throw new NestedError(`Failed when flushing`, err); } - - this._previousFlushTime = Date.now(); } async addDoc(entries) { - return await uploadPathways(this.sessionId, this.collectionName, entries); + if (entries.length) { + Verbose && log(`Uploading ${entries.length} "${this.collectionName}" of session "${this.sessionId}"`); + return await uploadPathways(this.sessionId, this.collectionName, entries); + } + else { + return null; + } } } \ No newline at end of file diff --git a/dbux-code/src/workshop/PathwaysDataContainer.js b/dbux-code/src/workshop/PathwaysDataContainer.js new file mode 100644 index 000000000..b7b74b19e --- /dev/null +++ b/dbux-code/src/workshop/PathwaysDataContainer.js @@ -0,0 +1,82 @@ +import { newLogger } from '@dbux/common/src/log/logger'; +import PathwaysDataBuffer from './PathwaysDataBuffer'; + +// eslint-disable-next-line no-unused-vars +const { log, debug, warn, error: logError } = newLogger('PathwaysDataContainer'); + +// const Verbose = true; +const Verbose = false; + +export class PathwaysDataContainer { + /** + * @type {Object} + */ + buffers; + + constructor() { + this.prevListener = null; + this.reset(); + } + + reset(sessionId = null) { + Verbose && log(`Reset with sessionId ${sessionId}`); + this.buffers = {}; + if (sessionId) { + this.buffers = { + applications: new PathwaysDataBuffer(sessionId, 'applications'), + testRuns: new PathwaysDataBuffer(sessionId, 'testRuns'), + steps: new PathwaysDataBuffer(sessionId, 'steps'), + actionGroups: new PathwaysDataBuffer(sessionId, 'actionGroups'), + userActions: new PathwaysDataBuffer(sessionId, 'userActions'), + }; + } + } + + onSessionChanged = async (session) => { + if (this.sessionId !== session?.sessionId) { + this.sessionId = session?.sessionId; + + // stop listening on previous events + this.prevListener?.(); + + // flush all current data + const flushOldPromise = this.flushAll(); + + // make new buffers + this.reset(this.sessionId); + + // listen on new events + this.prevListener = session?.pdp.onAnyData((allData) => { + const serializedData = session?.pdp.serializeJson(Object.entries(allData)); + this.addAllData(serializedData); + }); + + await flushOldPromise; + } + } + + addAllData = ({ collections }) => { + for (const collectionName of Object.keys(collections)) { + this.buffers[collectionName].add(collections[collectionName]); + } + } + + async maybeFlushAll() { + return await Promise.all(Object.values(this.buffers).map((buffer) => buffer.maybeFlush())); + } + + async flushAll() { + return await Promise.all(Object.values(this.buffers).map((buffer) => buffer.flush())); + } +} + +/** + * @type {PathwaysDataContainer} + */ +let container; + +export function initPathwaysDataContainer() { + container = new PathwaysDataContainer(); + + return container; +} diff --git a/dbux-code/src/workshop/SafetyStorage.js b/dbux-code/src/workshop/SafetyStorage.js index 83446990d..841e856a6 100644 --- a/dbux-code/src/workshop/SafetyStorage.js +++ b/dbux-code/src/workshop/SafetyStorage.js @@ -5,7 +5,7 @@ import { newLogger } from '@dbux/common/src/log/logger'; import { get, set } from '../memento'; // eslint-disable-next-line no-unused-vars -const { log, debug, warn, error: logError } = newLogger('SavetyStorage'); +const { log, debug, warn, error: logError } = newLogger('SafetyStorage'); function getLockfileName(name) { const lockfilePath = path.join(os.tmpdir(), `dbux-lockfile.${name}`); diff --git a/dbux-code/src/workshop/index.js b/dbux-code/src/workshop/index.js index 1249cfd16..54ee110b2 100644 --- a/dbux-code/src/workshop/index.js +++ b/dbux-code/src/workshop/index.js @@ -1,5 +1,16 @@ /** @typedef {import('@dbux/projects/src/ProjectsManager').default} ProjectsManager */ +import NestedError from '@dbux/common/src/NestedError'; +import { newLogger } from '@dbux/common/src/log/logger'; +import sleep from '@dbux/common/src/util/sleep'; +import { initPathwaysDataContainer } from './PathwaysDataContainer'; + +// eslint-disable-next-line no-unused-vars +const { log, debug, warn, error: logError } = newLogger('WorkshopSession'); + +// const Verbose = true; +const Verbose = false; + const ValidCodes = new Set([]); let currentCode = ''; @@ -38,24 +49,23 @@ export function setupWorkshopSession(code) { */ export async function initWorkshopSession(projectsManager) { if (isWorkshopSessionActive()) { - addHook(); - projectsManager.onPracticeSessionStateChanged(addHook); + const pathwaysDataContainer = initPathwaysDataContainer(); + projectsManager.onPracticeSessionStateChanged(pathwaysDataContainer.onSessionChanged); + scheduleUpload(pathwaysDataContainer); } } -let sessionId, prevListener; +const UploadLoopInterval = 2 * 60 * 1000; +// const UploadLoopInterval = 10 * 1000; -function addHook(session) { - if (sessionId !== session?.sessionId) { - // stop listening on previous events - prevListener?.(); - - prevListener = session?.pdp.onAnyData(addData); +async function scheduleUpload(pathwaysDataContainer) { + while (isWorkshopSessionActive()) { + await sleep(UploadLoopInterval); + try { + await pathwaysDataContainer.maybeFlushAll(); + } + catch (err) { + throw new NestedError(`Failed in PathwaysDataContainer upload loop`, err); + } } } - -function addData(allData) { - for (const collectionName of Object.keys(allData)) { - // TODO-M: push data into buffer - } -} \ No newline at end of file diff --git a/dbux-projects/src/userEvents/index.js b/dbux-projects/src/userEvents/index.js index 74a189e73..a9c984668 100644 --- a/dbux-projects/src/userEvents/index.js +++ b/dbux-projects/src/userEvents/index.js @@ -8,8 +8,8 @@ import { newLogger } from '@dbux/common/src/log/logger'; // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('UserEvents'); -const Verbose = true; -// const Verbose = false; +// const Verbose = true; +const Verbose = false; // ########################################################################### // event registry From bcefa6a3089e7160e035aa211963e5a0c3609337 Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 4 May 2022 15:19:49 +0800 Subject: [PATCH 4/8] classify UserActions into 3 types * fix `UserAction` helper functions * rename `UserEvent` to `UserAction` --- dbux-code/package.json | 16 +- dbux-code/src/codeDeco/index.js | 2 +- .../treeView/BaseTreeViewNodeProvider.js | 8 +- .../codeUtil/treeView/TraceContainerNode.js | 2 +- .../src/commands/applicationsViewCommands.js | 9 +- .../src/commands/dataFlowViewCommands.js | 10 +- .../commands/globalAnalysisViewCommands.js | 4 +- dbux-code/src/commands/projectCommands.js | 2 +- dbux-code/src/commands/runCommands.js | 2 +- dbux-code/src/commands/userCommands.js | 8 +- .../nodes/GlobalModulesNode.js | 2 +- dbux-code/src/help.js | 2 +- dbux-code/src/logging.js | 2 +- dbux-code/src/net/SocketServer.js | 2 +- dbux-code/src/practice/codeEvents.js | 35 +-- .../src/projectViews/ExerciseIntroduction.js | 2 +- .../projectViews/practiceView/ExerciseNode.js | 10 +- .../practiceView/ProjectNodeProvider.js | 2 +- dbux-code/src/projectViews/projectControl.js | 4 +- .../projectViews/projectViewsController.js | 2 +- .../projectViews/sessionView/ActionNodes.js | 2 +- dbux-code/src/search/searchController.js | 6 +- dbux-code/src/toolbar.js | 2 +- .../traceDetailsView/nodes/AsyncTDNodes.js | 8 +- .../nodes/ExecutionsTDNodes.js | 4 +- .../traceDetailsView/nodes/NavigationNode.js | 5 +- .../src/traceDetailsView/nodes/ObjectNode.js | 4 +- .../traceDetailsView/nodes/ValueTDRefNode.js | 2 +- .../nodes/ValueTDSimpleNode.js | 2 +- .../traceDetailsController.js | 5 +- dbux-code/src/userActions.js | 249 +++++++++++++++++ dbux-code/src/userEvents.js | 253 ------------------ dbux-code/src/webViews/graphWebView.js | 2 +- dbux-code/src/webViews/pathwaysWebView.js | 4 +- .../src/workshop/PathwaysDataContainer.js | 5 + dbux-data/src/pathways/UserAction.js | 32 ++- .../src/graph/asyncGraph/AsyncGraph.js | 8 +- .../src/graph/controllers/GraphNode.js | 8 +- .../src/graph/syncGraph/ContextNode.js | 4 +- dbux-projects/src/ProjectsManager.js | 14 +- .../backend/containers/UserEventContainer.js | 6 +- dbux-projects/src/checkSystem.js | 3 +- .../src/dataLib/ExerciseDataProvider.js | 2 +- .../src/dataLib/PathwaysDataProvider.js | 2 +- dbux-projects/src/dataLib/pathwaysDataUtil.js | 14 +- .../src/practiceSession/PathwaysSession.js | 2 +- .../src/practiceSession/PracticeSession.js | 2 +- .../src/{userEvents => userActions}/index.js | 48 ++-- .../content/_partials/_dbux-code-commands.mdx | 2 +- docs_site/data/commands.json | 2 +- 50 files changed, 407 insertions(+), 421 deletions(-) create mode 100644 dbux-code/src/userActions.js delete mode 100644 dbux-code/src/userEvents.js rename dbux-projects/src/{userEvents => userActions}/index.js (61%) diff --git a/dbux-code/package.json b/dbux-code/package.json index c289917e9..7e3a1ad71 100644 --- a/dbux-code/package.json +++ b/dbux-code/package.json @@ -482,7 +482,7 @@ } }, { - "command": "dbuxDataFlowView.setSearchMode.ByAccessId", + "command": "dbuxDataFlowView.nextSearchMode.ByAccessId", "title": "Search by: accessId", "icon": { "light": "resources/light/variable.svg", @@ -490,7 +490,7 @@ } }, { - "command": "dbuxDataFlowView.setSearchMode.ByValueId", + "command": "dbuxDataFlowView.nextSearchMode.ByValueId", "title": "Search by: valueId", "icon": { "light": "resources/light/brackets.svg", @@ -642,7 +642,7 @@ } }, { - "command": "dbux.deleteUserEvents", + "command": "dbux.deleteUserActions", "title": "Dbux Dev: Delete all user events" }, { @@ -972,12 +972,12 @@ "group": "navigation@20" }, { - "command": "dbuxDataFlowView.setSearchMode.ByAccessId", + "command": "dbuxDataFlowView.nextSearchMode.ByAccessId", "when": "view == dbuxDataFlowView && dbuxDataFlowView.context.searchModeName == ByAccessId", "group": "navigation@1" }, { - "command": "dbuxDataFlowView.setSearchMode.ByValueId", + "command": "dbuxDataFlowView.nextSearchMode.ByValueId", "when": "view == dbuxDataFlowView && dbuxDataFlowView.context.searchModeName == ByValueId", "group": "navigation@1" }, @@ -1248,7 +1248,7 @@ "when": "dbux.context.activated" }, { - "command": "dbux.deleteUserEvents", + "command": "dbux.deleteUserActions", "when": "dbux.context.activated && dbux.context.nodeEnv == development" }, { @@ -1328,11 +1328,11 @@ "when": "false" }, { - "command": "dbuxDataFlowView.setSearchMode.ByAccessId", + "command": "dbuxDataFlowView.nextSearchMode.ByAccessId", "when": "false" }, { - "command": "dbuxDataFlowView.setSearchMode.ByValueId", + "command": "dbuxDataFlowView.nextSearchMode.ByValueId", "when": "false" }, { diff --git a/dbux-code/src/codeDeco/index.js b/dbux-code/src/codeDeco/index.js index b0c4d0a8f..4397d7f0c 100644 --- a/dbux-code/src/codeDeco/index.js +++ b/dbux-code/src/codeDeco/index.js @@ -10,7 +10,7 @@ import { renderTraceDecorations, clearTraceDecorations } from './traceDecorator' import { initTraceDecorators } from './traceDecoConfig'; import { initEditedWarning } from './editedWarning'; import { set as mementoSet, get as mementoGet } from '../memento'; -import { emitShowHideDecorationAction } from '../userEvents'; +import { emitShowHideDecorationAction } from '../userActions'; // import DataProvider from '@dbux/data/src/DataProvider'; // import StaticContextType from '@dbux/common/src/types/constants/StaticContextType'; diff --git a/dbux-code/src/codeUtil/treeView/BaseTreeViewNodeProvider.js b/dbux-code/src/codeUtil/treeView/BaseTreeViewNodeProvider.js index a783cb012..82b6ab32a 100644 --- a/dbux-code/src/codeUtil/treeView/BaseTreeViewNodeProvider.js +++ b/dbux-code/src/codeUtil/treeView/BaseTreeViewNodeProvider.js @@ -6,7 +6,7 @@ import NestedError from '@dbux/common/src/NestedError'; import { throttle } from '@dbux/common/src/util/scheduling'; import { getThemeResourcePath } from '../codePath'; import { registerCommand } from '../../commands/commandUtil'; -import { emitTreeViewAction, emitTreeViewCollapseChangeAction } from '../../userEvents'; +import { emitTreeViewAction, emitTreeViewCollapseChangeAction } from '../../userActions'; import BaseTreeViewNode from './BaseTreeViewNode'; /** @typedef { import("./BaseTreeViewNode").default } BaseTreeViewNode */ @@ -184,7 +184,6 @@ export default class BaseTreeViewNodeProvider { // record user action const { treeViewName } = this; - const action = ''; // not a button click const nodeId = node.id; const args = { description: node.description, @@ -195,7 +194,7 @@ export default class BaseTreeViewNodeProvider { // trigger event handlers evtHandler.call(this, node); this.handleNodeCollapsibleStateChanged(node); - emitTreeViewCollapseChangeAction(treeViewName, action, nodeId, node.label, node.collapseChangeUserActionType, args); + emitTreeViewCollapseChangeAction(treeViewName, nodeId, node.label, node.collapseChangeUserActionType, args); } handleExpanded(node) { @@ -225,7 +224,6 @@ export default class BaseTreeViewNodeProvider { */ handleClick = async (node) => { const { treeViewName } = this; - const action = ''; // not a button click const nodeId = node.id; const args = { description: node.description, @@ -237,7 +235,7 @@ export default class BaseTreeViewNodeProvider { await node.handleClick?.(); const { clickUserActionType } = node; if (clickUserActionType !== false) { - emitTreeViewAction(treeViewName, action, nodeId, node.label, clickUserActionType, args); + emitTreeViewAction(treeViewName, nodeId, node.label, clickUserActionType, args); } } catch (err) { diff --git a/dbux-code/src/codeUtil/treeView/TraceContainerNode.js b/dbux-code/src/codeUtil/treeView/TraceContainerNode.js index 670afe53d..db8be8d98 100644 --- a/dbux-code/src/codeUtil/treeView/TraceContainerNode.js +++ b/dbux-code/src/codeUtil/treeView/TraceContainerNode.js @@ -2,7 +2,7 @@ import { TreeItemCollapsibleState } from 'vscode'; import allApplications from '@dbux/data/src/applications/allApplications'; import { makeContextLabel, makeTraceLabel } from '@dbux/data/src/helpers/makeLabels'; import traceSelection from '@dbux/data/src/traceSelection'; -import { emitTDExecutionGroupModeChangedAction } from '../../userEvents'; +import { emitTDExecutionGroupModeChangedAction } from '../../userActions'; import BaseTreeViewNode from './BaseTreeViewNode'; import TraceNode from './TraceNode'; diff --git a/dbux-code/src/commands/applicationsViewCommands.js b/dbux-code/src/commands/applicationsViewCommands.js index ce6d413d1..b5bcd5344 100644 --- a/dbux-code/src/commands/applicationsViewCommands.js +++ b/dbux-code/src/commands/applicationsViewCommands.js @@ -2,7 +2,7 @@ import { newLogger } from '@dbux/common/src/log/logger'; import { registerCommand } from './commandUtil'; import { showTextDocument } from '../codeUtil/codeNav'; import { initRuntimeServer, stopRuntimeServer } from '../net/SocketServer'; -import { emitShowApplicationEntryFileAction } from '../userEvents'; +import { emitShowApplicationEntryFileAction } from '../userActions'; // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('Commands'); @@ -21,9 +21,10 @@ export function initApplicationsViewCommands(context) { registerCommand(context, 'dbuxApplicationsView.showEntryPoint', async (node) => { - const filePath = node.application.entryPointPath; - await showTextDocument(filePath); - emitShowApplicationEntryFileAction(filePath); + const { application } = node; + const { entryPointPath } = application; + await showTextDocument(entryPointPath); + emitShowApplicationEntryFileAction(application, entryPointPath); } ); } \ No newline at end of file diff --git a/dbux-code/src/commands/dataFlowViewCommands.js b/dbux-code/src/commands/dataFlowViewCommands.js index 3a7229864..de6063e90 100644 --- a/dbux-code/src/commands/dataFlowViewCommands.js +++ b/dbux-code/src/commands/dataFlowViewCommands.js @@ -1,4 +1,4 @@ -import { emitDataFlowViewFilterModeChangedAction, emitDataFlowViewSearchModeChangedAction } from '../userEvents'; +import { emitDataFlowViewFilterModeChangedAction, emitDataFlowViewSearchModeChangedAction } from '../userActions'; import DataFlowFilterModeType from '../dataFlowView/DataFlowFilterModeType'; import DataFlowSearchModeType from '../dataFlowView/DataFlowSearchModeType'; import { registerCommand } from './commandUtil'; @@ -10,18 +10,18 @@ import { registerCommand } from './commandUtil'; */ export function initDataFlowViewCommands(context, dataFlowViewController) { registerCommand(context, - 'dbuxDataFlowView.setSearchMode.ByAccessId', + 'dbuxDataFlowView.nextSearchMode.ByAccessId', (/* node */) => { dataFlowViewController.setSearchMode(DataFlowSearchModeType.nextValue(DataFlowSearchModeType.ByAccessId)); - emitDataFlowViewSearchModeChangedAction(DataFlowSearchModeType.ByAccessId); + emitDataFlowViewSearchModeChangedAction(dataFlowViewController.searchMode); } ); registerCommand(context, - 'dbuxDataFlowView.setSearchMode.ByValueId', + 'dbuxDataFlowView.nextSearchMode.ByValueId', (/* node */) => { dataFlowViewController.setSearchMode(DataFlowSearchModeType.nextValue(DataFlowSearchModeType.ByValueId)); - emitDataFlowViewSearchModeChangedAction(DataFlowSearchModeType.ByValueId); + emitDataFlowViewSearchModeChangedAction(dataFlowViewController.searchMode); } ); diff --git a/dbux-code/src/commands/globalAnalysisViewCommands.js b/dbux-code/src/commands/globalAnalysisViewCommands.js index f4b71d7df..b80d50078 100644 --- a/dbux-code/src/commands/globalAnalysisViewCommands.js +++ b/dbux-code/src/commands/globalAnalysisViewCommands.js @@ -1,7 +1,7 @@ import { nextMode } from '../globalAnalysisView/nodes/GlobalModulesNode'; import { showInformationMessage } from '../codeUtil/codeModals'; import searchController from '../search/searchController'; -import { emitShowErrorAction } from '../userEvents'; +import { emitTraceUserAction } from '../userActions'; import { registerCommand } from './commandUtil'; /** @typedef {import('../globalAnalysisView/GlobalAnalysisViewController').default} GlobalAnalysisViewController */ @@ -15,7 +15,7 @@ export function initGlobalAnalysisViewCommands(context, globalAnalysisViewContro async () => { const selectedNode = await globalAnalysisViewController.showError(); if (selectedNode) { - emitShowErrorAction(selectedNode.trace); + emitTraceUserAction(selectedNode.trace); } } ); diff --git a/dbux-code/src/commands/projectCommands.js b/dbux-code/src/commands/projectCommands.js index 60711ef17..eb38a2c25 100644 --- a/dbux-code/src/commands/projectCommands.js +++ b/dbux-code/src/commands/projectCommands.js @@ -7,7 +7,7 @@ import { registerCommand } from './commandUtil'; import { chooseFile, showInformationMessage, showWarningMessage } from '../codeUtil/codeModals'; import { getCurrentResearch } from '../research/Research'; import { translate } from '../lang'; -import { emitAnnotateTraceAction, emitStopRunnerAction } from '../userEvents'; +import { emitAnnotateTraceAction, emitStopRunnerAction } from '../userActions'; import { addProjectFolderToWorkspace } from '../codeUtil/workspaceUtil'; /** @typedef {import('../projectViews/projectViewsController').ProjectViewController} ProjectViewController */ diff --git a/dbux-code/src/commands/runCommands.js b/dbux-code/src/commands/runCommands.js index 79c701454..bf9a59d44 100644 --- a/dbux-code/src/commands/runCommands.js +++ b/dbux-code/src/commands/runCommands.js @@ -10,7 +10,7 @@ import { initRuntimeServer } from '../net/SocketServer'; import { installDbuxDependencies } from '../codeUtil/installUtil'; import { initProjectView } from '../projectViews/projectViewsController'; import { getNodePath } from '../codeUtil/codePath'; -import { emitRunFileAction } from '../userEvents'; +import { emitRunFileAction } from '../userActions'; // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('DBUX run file'); diff --git a/dbux-code/src/commands/userCommands.js b/dbux-code/src/commands/userCommands.js index 165b27072..f03c946f9 100644 --- a/dbux-code/src/commands/userCommands.js +++ b/dbux-code/src/commands/userCommands.js @@ -30,7 +30,7 @@ import { translate } from '../lang'; import { getDefaultExportDirectory, getLogsDirectory } from '../codeUtil/codePath'; import { runTaskWithProgressBar } from '../codeUtil/runTaskWithProgressBar'; import searchController from '../search/searchController'; -import { emitSelectTraceAction, emitShowOutputChannelAction } from '../userEvents'; +import { emitTraceUserAction, emitShowOutputChannelAction } from '../userActions'; import { runFile } from './runCommands'; // eslint-disable-next-line no-unused-vars @@ -218,7 +218,7 @@ export function initUserCommands(extensionContext) { } else { traceSelection.selectTrace(trace); - emitSelectTraceAction(trace, UserActionType.SelectTraceById, { userInput }); + emitTraceUserAction(UserActionType.SelectTraceById, trace, { userInput }); } } @@ -249,11 +249,11 @@ export function initUserCommands(extensionContext) { return backend.containers.survey1.storeSurveyResult(data); }); - registerCommand(extensionContext, 'dbux.deleteUserEvents', async () => { + registerCommand(extensionContext, 'dbux.deleteUserActions', async () => { if (process.env.NODE_ENV === 'production') { throw new Error('This command is currently disabled in Production mode.'); } - await getProjectManager().deleteUserEvents(); + await getProjectManager().deleteUserActions(); }); // ########################################################################### diff --git a/dbux-code/src/globalAnalysisView/nodes/GlobalModulesNode.js b/dbux-code/src/globalAnalysisView/nodes/GlobalModulesNode.js index 9e2c9dc75..64d6268ec 100644 --- a/dbux-code/src/globalAnalysisView/nodes/GlobalModulesNode.js +++ b/dbux-code/src/globalAnalysisView/nodes/GlobalModulesNode.js @@ -6,7 +6,7 @@ import traceSelection from '@dbux/data/src/traceSelection'; import { pathRelative } from '@dbux/common-node/src/util/pathUtil'; import BaseTreeViewNode from '../../codeUtil/treeView/BaseTreeViewNode'; import TraceNode from '../../codeUtil/treeView/TraceNode'; -import { emitGlobalPackageSortModeChangedAction } from '../../userEvents'; +import { emitGlobalPackageSortModeChangedAction } from '../../userActions'; import PackageNodeSortMode from './PackageNodeSortMode'; diff --git a/dbux-code/src/help.js b/dbux-code/src/help.js index 0d90043a0..523071e09 100644 --- a/dbux-code/src/help.js +++ b/dbux-code/src/help.js @@ -1,7 +1,7 @@ import { Uri, env } from 'vscode'; import { showInformationMessage } from './codeUtil/codeModals'; import { translate } from './lang'; -import { emitShowHelpAction } from './userEvents'; +import { emitShowHelpAction } from './userActions'; let dialogController; diff --git a/dbux-code/src/logging.js b/dbux-code/src/logging.js index 046d642f3..cf2c88084 100644 --- a/dbux-code/src/logging.js +++ b/dbux-code/src/logging.js @@ -4,7 +4,7 @@ import { showOutputChannel } from './projectViews/projectViewsController'; import { showInformationMessage, showErrorMessage } from './codeUtil/codeModals'; import { showHelp } from './help'; import { translate } from './lang'; -import { emitShowHideErrorLogNotificationAction } from './userEvents'; +import { emitShowHideErrorLogNotificationAction } from './userActions'; let isShowingAllError = true; diff --git a/dbux-code/src/net/SocketServer.js b/dbux-code/src/net/SocketServer.js index 1a015484a..4bdd496b3 100644 --- a/dbux-code/src/net/SocketServer.js +++ b/dbux-code/src/net/SocketServer.js @@ -1,6 +1,6 @@ import NanoEvents from 'nanoevents'; import { newLogger } from '@dbux/common/src/log/logger'; -import { emitRuntimeServerStatusChangedAction } from '../userEvents'; +import { emitRuntimeServerStatusChangedAction } from '../userActions'; import RuntimeClient from './RuntimeClient'; import { makeListenSocket } from './serverUtil'; diff --git a/dbux-code/src/practice/codeEvents.js b/dbux-code/src/practice/codeEvents.js index a614d277f..1453daa08 100644 --- a/dbux-code/src/practice/codeEvents.js +++ b/dbux-code/src/practice/codeEvents.js @@ -1,10 +1,10 @@ import { commands, SymbolKind, window } from 'vscode'; -import allApplications from '@dbux/data/src/applications/allApplications'; +// import allApplications from '@dbux/data/src/applications/allApplications'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; import { newLogger } from '@dbux/common/src/log/logger'; -import { emitEditorAction } from '../userEvents'; -import { getOrCreateTracesAtCursor } from '../codeUtil/TracesAtCursor'; +import { emitEditorAction } from '../userActions'; +// import { getOrCreateTracesAtCursor } from '../codeUtil/TracesAtCursor'; import { codeRangeToBabelLoc } from '../helpers/codeLocHelpers'; /** @typedef {import('@dbux/projects/src/ProjectsManager').default} ProjectsManager */ @@ -21,7 +21,6 @@ const defaultNewEventLineThreshold = 8; * @param {ProjectsManager} manager */ export function initCodeEvents(manager, context) { - const traceAtCursor = getOrCreateTracesAtCursor(context); let _previousSelectionData, _previousVisibleRangeData; window.onDidChangeTextEditorSelection(async (e) => { @@ -37,7 +36,6 @@ export function initCodeEvents(manager, context) { return; } - // TODO?: take only first selection only. Do we need all selections? Can there be no selections? const firstSelection = e.selections[0]; let data = { file: e.textEditor.document.uri.fsPath, @@ -48,8 +46,8 @@ export function initCodeEvents(manager, context) { if (isNewData(_previousSelectionData, data)) { Verbose && debug('is new'); _previousSelectionData = data; - data = { ...data, ...await getExtraEditorEventInfo(e.textEditor) }; - emitEditorAction(UserActionType.EditorSelectionChanged, data); + const extraEditorEventInfo = await getExtraEditorEventInfo(e.textEditor); + emitEditorAction(UserActionType.EditorSelectionChanged, data, extraEditorEventInfo); } }); @@ -64,7 +62,7 @@ export function initCodeEvents(manager, context) { // TODO?: take only first range only. Do we need all range? Can there be no range? const firstRange = e.visibleRanges[0]; - let data = { + const data = { file: e.textEditor.document.uri.fsPath, range: firstRange ? codeRangeToBabelLoc(firstRange) : null }; @@ -73,8 +71,8 @@ export function initCodeEvents(manager, context) { if (isNewData(_previousVisibleRangeData, data)) { Verbose && debug('is new'); _previousVisibleRangeData = data; - data = { ...data, ...await getExtraEditorEventInfo(e.textEditor) }; - emitEditorAction(UserActionType.EditorVisibleRangeChanged, data); + const extraEditorEventInfo = await getExtraEditorEventInfo(e.textEditor); + emitEditorAction(UserActionType.EditorVisibleRangeChanged, data, extraEditorEventInfo); } }); @@ -83,26 +81,11 @@ export function initCodeEvents(manager, context) { // ########################################################################### async function getExtraEditorEventInfo(editor) { - const trace = traceAtCursor.getMostInner(); - let staticTrace = null; - let staticContext = null; - let applicationId = null; - if (trace) { - const { staticTraceId } = trace; - ({ applicationId } = trace); - const dp = allApplications.getById(applicationId).dataProvider; - staticTrace = dp.collections.staticTraces.getById(staticTraceId); - staticContext = dp.collections.staticContexts.getById(staticTrace.staticContextId); - } + // const trace = traceAtCursor.getMostInner(); const symbol = await getSymbolAt(editor.document.uri, editor.selections[0]?.start); - const { sessionId } = manager.practiceSession; return { - applicationId, - staticContext, - staticTrace, symbol: convertVSCodeSymbol(symbol), - sessionId }; } } diff --git a/dbux-code/src/projectViews/ExerciseIntroduction.js b/dbux-code/src/projectViews/ExerciseIntroduction.js index 2018f484e..743acc3dc 100644 --- a/dbux-code/src/projectViews/ExerciseIntroduction.js +++ b/dbux-code/src/projectViews/ExerciseIntroduction.js @@ -5,7 +5,7 @@ import isString from 'lodash/isString'; import isEmpty from 'lodash/isEmpty'; import WebviewWrapper from '../codeUtil/WebviewWrapper'; import { getThemeResourcePathUri } from '../codeUtil/codePath'; -import { emitShowExerciseIntroductionViewAction } from '../userEvents'; +import { emitShowExerciseIntroductionViewAction } from '../userActions'; /** * @typedef {import('@dbux/projects/src/projectLib/Exercise').default} Exercise diff --git a/dbux-code/src/projectViews/practiceView/ExerciseNode.js b/dbux-code/src/projectViews/practiceView/ExerciseNode.js index 23ff22dee..158f01936 100644 --- a/dbux-code/src/projectViews/practiceView/ExerciseNode.js +++ b/dbux-code/src/projectViews/practiceView/ExerciseNode.js @@ -3,7 +3,7 @@ import ExerciseStatus from '@dbux/projects/src/dataLib/ExerciseStatus'; import RunStatus from '@dbux/projects/src/projectLib/RunStatus'; import BaseTreeViewNode from '../../codeUtil/treeView/BaseTreeViewNode'; import { showInformationMessage } from '../../codeUtil/codeModals'; -import { emitOpenWebsiteAction } from '../../userEvents'; +import { emitOpenWebsiteAction } from '../../userActions'; import cleanUp from './cleanUp'; /** @typedef {import('@dbux/projects/src/projectLib/Exercise').default} Exercise */ @@ -80,11 +80,15 @@ export default class ExerciseNode extends BaseTreeViewNode { } + /** + * NOTE: Currently this will never happened, since the `Show Website` button in `PracticeView` is hidden while project is activated. + * Use `Run` button in `SessionView` instead to open website. + */ async showWebsite() { - const url = this.exercise.website; + const { website: { url }, id } = this.exercise; if (url) { const success = await env.openExternal(Uri.parse(url)); - emitOpenWebsiteAction(url); + emitOpenWebsiteAction(id, url); return success; } diff --git a/dbux-code/src/projectViews/practiceView/ProjectNodeProvider.js b/dbux-code/src/projectViews/practiceView/ProjectNodeProvider.js index 7d79e0b68..49793114f 100644 --- a/dbux-code/src/projectViews/practiceView/ProjectNodeProvider.js +++ b/dbux-code/src/projectViews/practiceView/ProjectNodeProvider.js @@ -1,6 +1,6 @@ import BaseTreeViewNodeProvider from '../../codeUtil/treeView/BaseTreeViewNodeProvider'; import EmptyTreeViewNode from '../../codeUtil/treeView/EmptyNode'; -import { emitProjectViewListModeChanged } from '../../userEvents'; +import { emitProjectViewListModeChanged } from '../../userActions'; import ProjectNode from './ProjectNode'; import ChapterNode from './ChapterNode'; diff --git a/dbux-code/src/projectViews/projectControl.js b/dbux-code/src/projectViews/projectControl.js index d5e7f4aea..98534f4b1 100644 --- a/dbux-code/src/projectViews/projectControl.js +++ b/dbux-code/src/projectViews/projectControl.js @@ -13,7 +13,7 @@ import TerminalWrapper from '../terminal/TerminalWrapper'; import { set as storageSet, get as storageGet } from '../memento'; import { interactiveGithubLogin } from '../net/GithubAuth'; import WebviewWrapper from '../codeUtil/WebviewWrapper'; -import { initUserEvent } from '../userEvents'; +import { initUserAction } from '../userActions'; import { showHelp } from '../help'; import { initRuntimeServer } from '../net/SocketServer'; import { getCurrentResearch } from '../research/Research'; @@ -155,7 +155,7 @@ export function createProjectManager(extensionContext) { projectManager = initDbuxProjects(cfg, externals); initDbuxManager(projectManager); - initUserEvent(projectManager); + initUserAction(projectManager); return projectManager; } \ No newline at end of file diff --git a/dbux-code/src/projectViews/projectViewsController.js b/dbux-code/src/projectViews/projectViewsController.js index 8d01d25ed..b27be7377 100644 --- a/dbux-code/src/projectViews/projectViewsController.js +++ b/dbux-code/src/projectViews/projectViewsController.js @@ -15,7 +15,7 @@ import { initCodeEvents } from '../practice/codeEvents'; import { translate } from '../lang'; import { getLogsDirectory } from '../codeUtil/codePath'; import { addProjectFolderToWorkspace, getDefaultWorkspaceFilePath, isProjectFolderInWorkspace, maybeCreateWorkspaceFile } from '../codeUtil/workspaceUtil'; -import { emitShowHideProjectViewsAction } from '../userEvents'; +import { emitShowHideProjectViewsAction } from '../userActions'; /** @typedef {import('./practiceView/ExerciseNode').ExerciseNode} ExerciseNode */ /** @typedef {import('@dbux/projects/src/projectLib/Exercise').default} Exercise */ diff --git a/dbux-code/src/projectViews/sessionView/ActionNodes.js b/dbux-code/src/projectViews/sessionView/ActionNodes.js index 90cb2c996..f511eceac 100644 --- a/dbux-code/src/projectViews/sessionView/ActionNodes.js +++ b/dbux-code/src/projectViews/sessionView/ActionNodes.js @@ -2,7 +2,7 @@ import traceSelection from '@dbux/data/src/traceSelection'; import PracticeSessionState from '@dbux/projects/src/practiceSession/PracticeSessionState'; import BaseTreeViewNode from '../../codeUtil/treeView/BaseTreeViewNode'; import { showInformationMessage, showWarningMessage } from '../../codeUtil/codeModals'; -import { emitTagTraceAction } from '../../userEvents'; +import { emitTagTraceAction } from '../../userActions'; import { getCursorLocation } from '../../codeUtil/codeNav'; import { codeLineToBabelLine } from '../../helpers/codeLocHelpers'; import { isProjectFolderInWorkspace } from '../../codeUtil/workspaceUtil'; diff --git a/dbux-code/src/search/searchController.js b/dbux-code/src/search/searchController.js index d4d875d80..c5a9ddabb 100644 --- a/dbux-code/src/search/searchController.js +++ b/dbux-code/src/search/searchController.js @@ -3,7 +3,7 @@ import allApplications from '@dbux/data/src/applications/allApplications'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; import SearchMode from '@dbux/graph-common/src/shared/SearchMode'; import NanoEvents from 'nanoevents'; -import { emitUserAction } from '../userEvents'; +import { emitBasicUserAction } from '../userActions'; import SearchTools from './SearchTools'; const SearchUserActionTypeByMode = { @@ -34,7 +34,7 @@ class SearchController { if (this.mode !== mode) { this.mode = mode; this._notifySearchModeChanged(); - emitUserAction(UserActionType.SearchModeChanged, mode); + emitBasicUserAction(UserActionType.SearchModeChanged, mode); } } @@ -45,7 +45,7 @@ class SearchController { this.contexts = EmptyArray; } else { - emitUserAction(SearchUserActionTypeByMode[this.mode], { searchTerm }); + emitBasicUserAction(SearchUserActionTypeByMode[this.mode], { searchTerm }); this.matches = SearchTools[this.mode].search(searchTerm); this.contexts = SearchTools[this.mode].getContexts(this.matches); } diff --git a/dbux-code/src/toolbar.js b/dbux-code/src/toolbar.js index cf4997491..c4d5f43a1 100644 --- a/dbux-code/src/toolbar.js +++ b/dbux-code/src/toolbar.js @@ -1,5 +1,5 @@ import { commands } from 'vscode'; -import { emitShowHideNavBarButtonsAction } from './userEvents'; +import { emitShowHideNavBarButtonsAction } from './userActions'; let showAllNavButton; diff --git a/dbux-code/src/traceDetailsView/nodes/AsyncTDNodes.js b/dbux-code/src/traceDetailsView/nodes/AsyncTDNodes.js index 6f865bd31..af1a4aeaa 100644 --- a/dbux-code/src/traceDetailsView/nodes/AsyncTDNodes.js +++ b/dbux-code/src/traceDetailsView/nodes/AsyncTDNodes.js @@ -8,10 +8,10 @@ import PromiseLink from '@dbux/common/src/types/PromiseLink'; import PromiseLinkType from '@dbux/common/src/types/constants/PromiseLinkType'; import EmptyObject from '@dbux/common/src/util/EmptyObject'; import { showInformationMessage } from '../../codeUtil/codeModals'; -import makeTreeItem, { makeTreeItems } from '../../helpers/makeTreeItem'; +import makeTreeItem from '../../helpers/makeTreeItem'; import TraceDetailNode from './TraceDetailNode'; import { makeArrayLengthLabel } from '../../helpers/treeViewUtil'; -import { emitSelectTraceAction } from '../../userEvents'; +import { emitTraceUserAction } from '../../userActions'; /** @typedef {import('@dbux/common/src/types/Trace').default} Trace */ @@ -110,7 +110,7 @@ class RootEdgesTDNode extends TraceDetailNode { const trace = dp.util.getTraceOfAsyncNode(forkParent.asyncNodeId); if (trace) { traceSelection.selectTrace(trace); - emitSelectTraceAction(trace, UserActionType.TDAsyncGoToForkParent); + emitTraceUserAction(UserActionType.TDAsyncGoToForkParent, trace); return; } } @@ -123,7 +123,7 @@ class RootEdgesTDNode extends TraceDetailNode { const schedulerTrace = this.dp.collections.traces.getById(schedulerTraceId); if (schedulerTrace) { traceSelection.selectTrace(schedulerTrace); - emitSelectTraceAction(schedulerTrace, UserActionType.TDAsyncGoToScheduler); + emitTraceUserAction(UserActionType.TDAsyncGoToScheduler, schedulerTrace); return; } } diff --git a/dbux-code/src/traceDetailsView/nodes/ExecutionsTDNodes.js b/dbux-code/src/traceDetailsView/nodes/ExecutionsTDNodes.js index cc7caa80f..a1cf679f6 100644 --- a/dbux-code/src/traceDetailsView/nodes/ExecutionsTDNodes.js +++ b/dbux-code/src/traceDetailsView/nodes/ExecutionsTDNodes.js @@ -2,7 +2,7 @@ import { makeTraceValueLabel } from '@dbux/data/src/helpers/makeLabels'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; import allApplications from '@dbux/data/src/applications/allApplications'; import EmptyArray from '@dbux/common/src/util/EmptyArray'; -import { emitSelectTraceAction } from '../../userEvents'; +import { emitTraceUserAction } from '../../userActions'; import TraceNode from '../../codeUtil/treeView/TraceNode'; import TraceContainerNode, { UngroupedNode } from '../../codeUtil/treeView/TraceContainerNode'; @@ -23,7 +23,7 @@ class ExecutionNode extends TraceNode { handleClick() { super.handleClick(); - emitSelectTraceAction(this.trace, UserActionType.TDExecutionsTraceUse); + emitTraceUserAction(UserActionType.TDExecutionsTraceUse, this.trace); } } diff --git a/dbux-code/src/traceDetailsView/nodes/NavigationNode.js b/dbux-code/src/traceDetailsView/nodes/NavigationNode.js index 69697a12b..24ef1f6f2 100644 --- a/dbux-code/src/traceDetailsView/nodes/NavigationNode.js +++ b/dbux-code/src/traceDetailsView/nodes/NavigationNode.js @@ -1,9 +1,8 @@ -import { window } from 'vscode'; import tracePlayback from '@dbux/data/src/playback/tracePlayback'; import traceSelection from '@dbux/data/src/traceSelection'; import { newLogger } from '@dbux/common/src/log/logger'; import BaseTreeViewNode from '../../codeUtil/treeView/BaseTreeViewNode'; -import { emitNavigationAction } from '../../userEvents'; +import { emitNavigationAction } from '../../userActions'; import { showInformationMessage } from '../../codeUtil/codeModals'; // eslint-disable-next-line no-unused-vars @@ -85,7 +84,7 @@ export default class NavigationNode extends BaseTreeViewNode { const trace = this.findTargetTrace(methodName); if (trace) { traceSelection.selectTrace(trace); - emitNavigationAction(methodName, `navigation.${methodName}`, trace); + emitNavigationAction(trace, methodName); } else { showInformationMessage(`Can't find "${methodName}" of current trace.`); diff --git a/dbux-code/src/traceDetailsView/nodes/ObjectNode.js b/dbux-code/src/traceDetailsView/nodes/ObjectNode.js index 46e9afcb3..0d2ab8001 100644 --- a/dbux-code/src/traceDetailsView/nodes/ObjectNode.js +++ b/dbux-code/src/traceDetailsView/nodes/ObjectNode.js @@ -1,7 +1,7 @@ import allApplications from '@dbux/data/src/applications/allApplications'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; import traceSelection from '@dbux/data/src/traceSelection'; -import { emitSelectTraceAction } from '../../userEvents'; +import { emitTraceUserAction } from '../../userActions'; import TraceNode from '../../codeUtil/treeView/TraceNode'; export default class ObjectNode extends TraceNode { @@ -18,6 +18,6 @@ export default class ObjectNode extends TraceNode { handleClick() { super.handleClick(); - emitSelectTraceAction(this.trace, UserActionType.TDTrackObjectTraceUse); + emitTraceUserAction(UserActionType.TDTrackObjectTraceUse, this.trace); } } \ No newline at end of file diff --git a/dbux-code/src/traceDetailsView/nodes/ValueTDRefNode.js b/dbux-code/src/traceDetailsView/nodes/ValueTDRefNode.js index 7b5901e26..a85769d36 100644 --- a/dbux-code/src/traceDetailsView/nodes/ValueTDRefNode.js +++ b/dbux-code/src/traceDetailsView/nodes/ValueTDRefNode.js @@ -1,7 +1,7 @@ import { ValuePruneState } from '@dbux/common/src/types/constants/ValueTypeCategory'; import EmptyArray from '@dbux/common/src/util/EmptyArray'; import allApplications from '@dbux/data/src/applications/allApplications'; -import { emitValueRenderAction } from '../../userEvents'; +import { emitValueRenderAction } from '../../userActions'; // import { makeTreeItemNoChildren } from '../../helpers/makeTreeItem'; import EmptyTreeViewNode from '../../codeUtil/treeView/EmptyNode'; import { valueRender } from '../valueRender'; diff --git a/dbux-code/src/traceDetailsView/nodes/ValueTDSimpleNode.js b/dbux-code/src/traceDetailsView/nodes/ValueTDSimpleNode.js index 870d0f7c6..c6e051ce9 100644 --- a/dbux-code/src/traceDetailsView/nodes/ValueTDSimpleNode.js +++ b/dbux-code/src/traceDetailsView/nodes/ValueTDSimpleNode.js @@ -2,7 +2,7 @@ import isString from 'lodash/isString'; import EmptyObject from '@dbux/common/src/util/EmptyObject'; import { renderValueSimple } from '@dbux/common/src/util/stringUtil'; import allApplications from '@dbux/data/src/applications/allApplications'; -import { emitValueRenderAction } from '../../userEvents'; +import { emitValueRenderAction } from '../../userActions'; import { valueRender } from '../valueRender'; import ValueNode, { ValueLabel } from './ValueNode'; diff --git a/dbux-code/src/traceDetailsView/traceDetailsController.js b/dbux-code/src/traceDetailsView/traceDetailsController.js index 0e7f15490..abad59b71 100644 --- a/dbux-code/src/traceDetailsView/traceDetailsController.js +++ b/dbux-code/src/traceDetailsView/traceDetailsController.js @@ -5,7 +5,8 @@ import NestedError from '@dbux/common/src/NestedError'; import { throttle } from '@dbux/common/src/util/scheduling'; import allApplications from '@dbux/data/src/applications/allApplications'; import traceSelection from '@dbux/data/src/traceSelection'; -import { emitSelectTraceAction } from '../userEvents'; +import UserActionType from '@dbux/data/src/pathways/UserActionType'; +import { emitTraceUserAction } from '../userActions'; import { getRelatedAppIds } from '../codeDeco/editedWarning'; import { showWarningMessage } from '../codeUtil/codeModals'; import TraceDetailsNodeProvider from './TraceDetailsNodeProvider'; @@ -104,7 +105,7 @@ class TraceDetailsController { } if (trace) { traceSelection.selectTrace(trace, 'selectTraceAtCursor'); - emitSelectTraceAction(trace); + emitTraceUserAction(UserActionType.selectTrace, trace); } } diff --git a/dbux-code/src/userActions.js b/dbux-code/src/userActions.js new file mode 100644 index 000000000..3c507b066 --- /dev/null +++ b/dbux-code/src/userActions.js @@ -0,0 +1,249 @@ +import path from 'path'; +import { pathRelative } from '@dbux/common-node/src/util/pathUtil'; +import { newLogger } from '@dbux/common/src/log/logger'; +import EmptyObject from '@dbux/common/src/util/EmptyObject'; +import allApplications from '@dbux/data/src/applications/allApplications'; +import UserActionType from '@dbux/data/src/pathways/UserActionType'; +import DataFlowFilterModeType from './dataFlowView/DataFlowFilterModeType'; +import DataFlowSearchModeType from './dataFlowView/DataFlowSearchModeType'; +import PackageNodeSortMode from './globalAnalysisView/nodes/PackageNodeSortMode'; + +/** @typedef {import('@dbux/common/src/types/Loc').default} Loc */ +/** @typedef {import('@dbux/common/src/types/Trace').default} Trace */ +/** @typedef {import('@dbux/projects/src/ProjectsManager').ProjectsManager} */ + +// eslint-disable-next-line no-unused-vars +const { log, debug, warn, error: logError } = newLogger('UserActions'); + +// ########################################################################### +// register `ProjectsManager` +// ########################################################################### + +/** + * @type {ProjectsManager} + */ +let manager; + +export function initUserAction(_manager) { + manager = _manager; +} + +// ########################################################################### +// basic events registry +// ########################################################################### + +/** + * The most basic `UserActions`, which contains no location nor trace info + */ +export function emitBasicUserAction(actionType, data) { + emitUserAction(actionType, data); +} + +/** + * `UserActions` that related to some 'file' & 'location' + */ +export function emitLocUserAction(actionType, file, range, moreProp) { + emitBasicUserAction(actionType, { file, range, ...moreProp }); +} + +/** + * `UserActions` that related to some trace, we use the information to find the related location + */ +export function emitTraceUserAction(actionType, trace, moreProp = EmptyObject) { + const { file, range, ...moreTraceInformation } = makeTraceLocationInformation(trace); + emitLocUserAction(actionType, file, range, { + ...moreTraceInformation, + ...moreProp + }); +} + +/** ########################################################################### + * helpers + * #########################################################################*/ + +/** ######################################## + * DataFlowView + * ######################################*/ + +export function emitDataFlowViewSearchModeChangedAction(mode) { + const modeName = DataFlowSearchModeType.nameFromForce(mode); + emitBasicUserAction(UserActionType.DataFlowViewSearchModeChanged, { mode, modeName }); +} + +export function emitDataFlowViewFilterModeChangedAction(mode) { + const modeName = DataFlowFilterModeType.nameFromForce(mode); + emitBasicUserAction(UserActionType.DataFlowViewFilterModeChanged, { mode, modeName }); +} + +/** ######################################## + * GlobalAnalysisView + * ######################################*/ + +export function emitGlobalPackageSortModeChangedAction(mode) { + const modeName = PackageNodeSortMode.nameFromForce(mode); + emitBasicUserAction(UserActionType.GlobalPackageSortModeChanged, { mode, modeName }); +} + +/** ######################################## + * TDView + * ######################################*/ + +export function emitTDExecutionGroupModeChangedAction(modeLabel) { + emitBasicUserAction(UserActionType.TDExecutionsGroupModeChanged, { modeLabel }); +} + +export function emitValueRenderAction(value, nodeId) { + emitBasicUserAction(UserActionType.TDValueRender, { value, nodeId }); +} + +/** ######################################## + * dbux-project + * ######################################*/ + +export function emitStopRunnerAction() { + emitBasicUserAction(UserActionType.StopProjectRunner); +} + +export function emitShowHideProjectViewsAction(isShowing) { + emitBasicUserAction(UserActionType.ProjectViewsVisibilityChanged, { isShowing }); +} + +export function emitTagTraceAction(trace) { + emitTraceUserAction(UserActionType.TagTrace, trace); +} + +export function emitAnnotateTraceAction(actionType, trace, annotation) { + emitTraceUserAction(actionType, trace, { annotation }); +} + +export function emitProjectViewListModeChanged(byChapterMode) { + const modeName = byChapterMode ? 'byChapter' : 'byProject'; + emitBasicUserAction(UserActionType.ProjectViewListModeChanged, { byChapterMode, modeName }); +} + +export function emitOpenWebsiteAction(exerciseId, url) { + emitBasicUserAction(UserActionType.OpenWebsite, { exerciseId, url }); +} + +export function emitShowExerciseIntroductionViewAction(exercise) { + emitBasicUserAction(UserActionType.ShowExerciseIntroductionView, { exerciseId: exercise.id }); +} + +/** ######################################## + * misc + * ######################################*/ + +export function emitRunFileAction(filePath, debugMode) { + const fileName = path.basename(filePath); + emitBasicUserAction(UserActionType.RunFile, { fileName, debugMode }); +} + +export function emitRuntimeServerStatusChangedAction(isOn) { + emitBasicUserAction(UserActionType.RuntimeServerStatusChanged, { isOn }); +} + +export function emitShowHelpAction() { + emitBasicUserAction(UserActionType.ShowHelp); +} + +export function emitShowOutputChannelAction() { + emitBasicUserAction(UserActionType.ShowOutputChannel); +} + +export function emitShowHideDecorationAction(isShowing) { + emitBasicUserAction(UserActionType.DecorationVisibilityChanged, { isShowing }); +} + +export function emitShowHideNavBarButtonsAction(isShowing) { + emitBasicUserAction(UserActionType.NavBarButtonsVisibilityChanged, { isShowing }); +} + +export function emitShowHideErrorLogNotificationAction(isShowing) { + emitBasicUserAction(UserActionType.ErrorLogNotificationVisibilityChanged, { isShowing }); +} + +/** + * NOTE: For editor events e.g. `EditorSelectionChanged` + */ +export function emitEditorAction(actionType, { file, range }, moreProp) { + emitLocUserAction(actionType, file, range, moreProp); +} + +export function emitNavigationAction(trace, navigationMethodName) { + const actionTypeName = `Navigation${navigationMethodName}`; + const actionType = UserActionType.valueFromForce(actionTypeName); + + emitTraceUserAction(actionType, trace); +} + +export function emitShowApplicationEntryFileAction(app, entryPointPath) { + const filePath = pathRelative(app.getAppCommonAncestorPath(), entryPointPath); + emitBasicUserAction(UserActionType.ShowApplicationEntryFile, { filePath }); +} + +export function emitTreeViewAction(treeViewName, nodeId, nodeLabel, userActionType, args) { + emitBasicUserAction(userActionType || UserActionType.TreeViewOther, { + treeViewName, + nodeId, + nodeLabel, + args + }); +} + +export function emitTreeViewCollapseChangeAction(treeViewName, nodeId, nodeLabel, userActionType, args) { + emitBasicUserAction(userActionType || UserActionType.TreeViewCollapseChangeOther, { + treeViewName, + nodeId, + nodeLabel, + args + }); +} + +export function emitCallGraphAction(actionType, data) { + emitBasicUserAction(actionType, data); +} + +export function emitCallGraphTraceAction(actionType, trace, moreProp) { + emitTraceUserAction(actionType, trace, moreProp); +} + +export function emitPathwaysAction(actionType, data) { + emitBasicUserAction(actionType, data); +} + +// ########################################################################### +// Util +// ########################################################################### + +/** + * + * @param {Trace} trace + * @returns {{file: string, range: Loc, applicationId: number, traceId: number}} + */ +function makeTraceLocationInformation(trace) { + const { applicationId, traceId } = trace; + + const app = allApplications.getById(applicationId); + const dp = app.dataProvider; + const file = pathRelative(app.entryPointPath, dp.util.getTraceFilePath(traceId)); + const range = dp.util.getTraceLoc(traceId); + return { + file, + range, + applicationId, + traceId + }; +} + +// ########################################################################### +// emitter +// ########################################################################### + +/** + * NOTE: Basic UserAction emitter, should not be used without registration + * @param {string} name + * @param {Object} [data] NOTE: data *must* always be completely serializable, simple data. + */ +function emitUserAction(name, data) { + manager?.emitUserAction(name, data); +} diff --git a/dbux-code/src/userEvents.js b/dbux-code/src/userEvents.js deleted file mode 100644 index ffdf59e83..000000000 --- a/dbux-code/src/userEvents.js +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @file Here we export `ProjectsManager.emitUserEvent` such that you can emit events everywhere in dbux-code - */ - -import { pathRelative } from '@dbux/common-node/src/util/pathUtil'; -import { newLogger } from '@dbux/common/src/log/logger'; -import allApplications from '@dbux/data/src/applications/allApplications'; -import UserActionType from '@dbux/data/src/pathways/UserActionType'; -import PackageNodeSortMode from './globalAnalysisView/nodes/PackageNodeSortMode'; - -// eslint-disable-next-line no-unused-vars -const { log, debug, warn, error: logError } = newLogger('UserEvents'); - -// ########################################################################### -// register `ProjectsManager` -// ########################################################################### - -let manager; - -export function initUserEvent(_manager) { - manager = _manager; -} - -// ########################################################################### -// events registry -// ########################################################################### - -/** - * NOTE: Register every possible UserActions here, so we can manage them all together - */ -export function emitUserAction(actionType, data) { - emitUserEvent(actionType, data); -} - -/** ######################################## - * trace selection - * ######################################*/ - -export function emitShowErrorAction(errorTrace) { - emitUserEvent(UserActionType.ShowError, { errorTrace }); -} - -export function emitSelectTraceAction(trace, actionType = UserActionType.SelectTrace, moreProp) { - emitUserEvent(actionType, { - trace, - applicationUUID: getTraceApplicationUUID(trace), - locationInfo: getExtraTraceLocationImformation(trace), - ...moreProp - }); -} - -/** ######################################## - * DataFlowView - * ######################################*/ - -export function emitDataFlowViewSearchModeChangedAction(searchMode) { - emitUserEvent(UserActionType.DataFlowViewSearchModeChanged, { searchMode }); -} - -export function emitDataFlowViewFilterModeChangedAction(filterMode) { - emitUserEvent(UserActionType.DataFlowViewFilterModeChanged, { filterMode }); -} - -/** ######################################## - * GlobalAnalysisView - * ######################################*/ - -export function emitGlobalPackageSortModeChangedAction(mode) { - const modeName = PackageNodeSortMode.nameFromForce(mode); - emitUserEvent(UserActionType.GlobalPackageSortModeChanged, { mode, modeName }); -} - -/** ######################################## - * TDView - * ######################################*/ - -export function emitTDExecutionGroupModeChangedAction(modeLabel) { - emitUserEvent(UserActionType.TDExecutionsGroupModeChanged, { modeLabel }); -} - -export function emitValueRenderAction(value, nodeId) { - emitUserEvent(UserActionType.TDValueRender, { value, nodeId }); -} - -/** ######################################## - * dbux-project - * ######################################*/ - -export function emitStopRunnerAction() { - emitUserEvent(UserActionType.StopProjectRunner); -} - -export function emitShowHideProjectViewsAction(isShowing) { - emitUserEvent(UserActionType.ProjectViewsVisibilityChanged, { isShowing }); -} - -export function emitTagTraceAction(trace, actionType) { - emitUserEvent(actionType || UserActionType.TagTrace, { - trace, - locationInfo: getExtraTraceLocationImformation(trace) - }); -} - -export function emitAnnotateTraceAction(type, trace, s) { - emitUserEvent(type, { - annotation: s, - trace, - locationInfo: getExtraTraceLocationImformation(trace) - }); -} - -export function emitProjectViewListModeChanged(isByChapter) { - emitUserEvent(UserActionType.ProjectViewListModeChanged, { modeName: isByChapter ? 'byChapter' : 'byProject' }); -} - -export function emitOpenWebsiteAction(url) { - emitUserEvent(UserActionType.OpenWebsite, { url }); -} - -export function emitShowExerciseIntroductionViewAction(exercise) { - emitUserEvent(UserActionType.ShowExerciseIntroductionView, { exerciseId: exercise.id }); -} - -/** ######################################## - * misc - * ######################################*/ - -export function emitRunFileAction(filePath, debugMode) { - emitUserEvent(UserActionType.RunFile, { filePath, debugMode }); -} - -export function emitRuntimeServerStatusChangedAction(isOn) { - emitUserEvent(UserActionType.RuntimeServerStatusChanged, { isOn }); -} - -export function emitShowHelpAction() { - emitUserEvent(UserActionType.ShowHelp); -} - -export function emitShowOutputChannelAction() { - emitUserEvent(UserActionType.ShowOutputChannel); -} - -export function emitShowHideDecorationAction(isShowing) { - emitUserEvent(UserActionType.DecorationVisibilityChanged, { isShowing }); -} - -export function emitShowHideNavBarButtonsAction(isShowing) { - emitUserEvent(UserActionType.NavBarButtonsVisibilityChanged, { isShowing }); -} - -export function emitShowHideErrorLogNotificationAction(isShowing) { - emitUserEvent(UserActionType.ErrorLogNotificationVisibilityChanged, { isShowing }); -} - -/** - * NOTE: For editor events e.g. `EditorSelectionChanged` - */ -export function emitEditorAction(type, data) { - emitUserEvent(type, data); -} - -export function emitNavigationAction(actionName, selectMethod, trace) { - const actionTypeName = `Navigation${actionName}`; - const actionType = UserActionType.valueFromForce(actionTypeName); - - emitUserEvent(actionType, { - selectMethod, - trace, - applicationUUID: getTraceApplicationUUID(trace), - locationInfo: getExtraTraceLocationImformation(trace) - }); -} - -export function emitShowApplicationEntryFileAction(filePath) { - emitUserEvent(UserActionType.ShowApplicationEntryFile, { filePath }); -} - -export function emitTreeViewAction(treeViewName, action, nodeId, nodeLabel, userActionType, args) { - emitUserEvent(userActionType || UserActionType.TreeViewOther, { - treeViewName, - action, - nodeId, - nodeLabel, - args - }); -} - -export function emitTreeViewCollapseChangeAction(treeViewName, action, nodeId, nodeLabel, userActionType, args) { - emitUserEvent(userActionType || UserActionType.TreeViewCollapseChangeOther, { - treeViewName, - action, - nodeId, - nodeLabel, - args - }); -} - -export function emitCallGraphAction(evtType, data) { - emitUserEvent(evtType, data); -} - -export function emitCallGraphTraceAction(trace, evtType, moreProp) { - emitSelectTraceAction(trace, evtType, moreProp); -} - -export function emitPathwaysAction(evtType, data) { - emitUserEvent(evtType, data); -} - -// ########################################################################### -// Util -// ########################################################################### - -function getExtraTraceLocationImformation(trace) { - const { applicationId, traceId, staticTraceId, staticTraceIndex } = trace; - const app = allApplications.getById(applicationId); - const dp = app.dataProvider; - - const staticTrace = dp.collections.staticTraces.getById(staticTraceId); - const staticContext = dp.collections.staticContexts.getById(staticTrace.staticContextId); - const filePath = pathRelative(app.entryPointPath, dp.util.getTraceFilePath(traceId)); - return { - filePath, - staticTrace, - staticContext, - staticTraceIndex - }; -} - -function getTraceApplicationUUID(trace) { - return allApplications.getById(trace.applicationId).uuid; -} - -// ########################################################################### -// emitter -// ########################################################################### - -// export function onUserEvent(cb) { -// if (!manager) { -// throw new Error('trying to listen on userEvent before ProjectsManager is registered'); -// } -// return manager.onUserEvent(cb); -// } - -/** - * NOTE: Basic UserAction emitter, should not be used without registration - * @param {string} name - * @param {Object} [data] NOTE: data *must* always be completely serializable, simple data. - */ -function emitUserEvent(name, data) { - manager?.emitUserEvent(name, data); -} diff --git a/dbux-code/src/webViews/graphWebView.js b/dbux-code/src/webViews/graphWebView.js index f8442e942..b2f0f5646 100644 --- a/dbux-code/src/webViews/graphWebView.js +++ b/dbux-code/src/webViews/graphWebView.js @@ -5,7 +5,7 @@ import UserActionType from '@dbux/data/src/pathways/UserActionType'; import GraphHost from '@dbux/graph-host/src/GraphHost'; import { getThemeResourcePathUri } from '../codeUtil/codePath'; import { showQuickPick } from '../codeUtil/codeModals'; -import { emitCallGraphAction, emitCallGraphTraceAction } from '../userEvents'; +import { emitCallGraphAction, emitCallGraphTraceAction } from '../userActions'; import searchController from '../search/searchController'; import { getGlobalAnalysisViewController } from '../globalAnalysisView/GlobalAnalysisViewController'; import { get as getMemento, set as setMemento } from '../memento'; diff --git a/dbux-code/src/webViews/pathwaysWebView.js b/dbux-code/src/webViews/pathwaysWebView.js index 256302394..c3d704656 100644 --- a/dbux-code/src/webViews/pathwaysWebView.js +++ b/dbux-code/src/webViews/pathwaysWebView.js @@ -1,13 +1,13 @@ import { ViewColumn } from 'vscode'; +import UserActionType from '@dbux/data/src/pathways/UserActionType'; import PathwaysHost from '@dbux/graph-host/src/PathwaysHost'; import { goToTrace, goToCodeLoc } from '../codeUtil/codeNav'; import { getProjectManager } from '../projectViews/projectControl'; -import { emitPathwaysAction } from '../userEvents'; +import { emitPathwaysAction } from '../userActions'; import RichWebView from './RichWebView'; import { decorateVisitedTraces, stopDecorating } from './pathwaysDecorations'; -import UserActionType from '@dbux/data/src/pathways/UserActionType'; const defaultColumn = ViewColumn.Two; diff --git a/dbux-code/src/workshop/PathwaysDataContainer.js b/dbux-code/src/workshop/PathwaysDataContainer.js index b7b74b19e..d8f550ac4 100644 --- a/dbux-code/src/workshop/PathwaysDataContainer.js +++ b/dbux-code/src/workshop/PathwaysDataContainer.js @@ -1,4 +1,5 @@ import { newLogger } from '@dbux/common/src/log/logger'; +import { uploadPathways } from '@dbux/projects/src/firestore/upload'; import PathwaysDataBuffer from './PathwaysDataBuffer'; // eslint-disable-next-line no-unused-vars @@ -51,6 +52,10 @@ export class PathwaysDataContainer { this.addAllData(serializedData); }); + if (this.sessionId) { + await uploadPathways(this.sessionId, 'info', session.serialize()); + } + await flushOldPromise; } } diff --git a/dbux-data/src/pathways/UserAction.js b/dbux-data/src/pathways/UserAction.js index f3696e264..d85d8aeb5 100644 --- a/dbux-data/src/pathways/UserAction.js +++ b/dbux-data/src/pathways/UserAction.js @@ -1,19 +1,11 @@ +/** @typedef {import('@dbux/common/src/types/Loc').default} Loc */ + export default class UserAction { /** * @type {number} see: UserActionType.js */ type; - // /** - // * @type {string} uuid of PracticeSession - // */ - // sessionId; - - // /** - // * @type {string} - // */ - // exerciseId; - /** * @type {number} event create time */ @@ -30,4 +22,24 @@ export default class UserAction { stepId; endTime; + + /** + * @type {number?} + */ + traceId; + + /** + * @type {number?} + */ + applicationId + + /** + * @type {string?} + */ + file; + + /** + * @type {Loc?} + */ + range; } diff --git a/dbux-graph-host/src/graph/asyncGraph/AsyncGraph.js b/dbux-graph-host/src/graph/asyncGraph/AsyncGraph.js index 15a303ac2..fc33c244c 100644 --- a/dbux-graph-host/src/graph/asyncGraph/AsyncGraph.js +++ b/dbux-graph-host/src/graph/asyncGraph/AsyncGraph.js @@ -454,7 +454,7 @@ class AsyncGraph extends GraphBase { if (trace) { const asyncNode = dp.collections.asyncNodes.getById(asyncNodeId); traceSelection.selectTrace(trace); - this.componentManager.externals.emitCallGraphTraceAction(trace, UserActionType.AsyncCallGraphTrace, { asyncNode }); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.AsyncCallGraphTrace, trace, { asyncNode }); } }, selectSchedulerTrace(applicationId, asyncNodeId, schedulerTraceId) { @@ -463,7 +463,7 @@ class AsyncGraph extends GraphBase { if (trace) { const asyncNode = dp.collections.asyncNodes.getById(asyncNodeId); traceSelection.selectTrace(trace); - this.componentManager.externals.emitCallGraphTraceAction(trace, UserActionType.AsyncCallGraphSchedulerTrace, { asyncNode }); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.AsyncCallGraphSchedulerTrace, trace, { asyncNode }); } }, gotoValueTrace(applicationId, asyncNodeId, valueTraceId) { @@ -472,7 +472,7 @@ class AsyncGraph extends GraphBase { if (trace) { const asyncNode = dp.collections.asyncNodes.getById(asyncNodeId); traceSelection.selectTrace(trace); - this.componentManager.externals.emitCallGraphTraceAction(trace, UserActionType.AsyncCallGraphValueTrace, { asyncNode }); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.AsyncCallGraphValueTrace, trace, { asyncNode }); } }, selectSyncInThreads(applicationId, asyncNodeId) { @@ -506,7 +506,7 @@ class AsyncGraph extends GraphBase { const asyncNode = dp.collections.asyncNodes.getById(asyncNodeId); traceSelection.selectTrace(firstError); await this.componentManager.externals.globalAnalysisViewController.revealSelectedError(); - this.componentManager.externals.emitCallGraphTraceAction(firstError, UserActionType.AsyncCallGraphError, { asyncNode }); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.AsyncCallGraphError, firstError, { asyncNode }); } else { this.componentManager.externals.alert('No error in this async node.', false); diff --git a/dbux-graph-host/src/graph/controllers/GraphNode.js b/dbux-graph-host/src/graph/controllers/GraphNode.js index d114dd75a..9ac0a5654 100644 --- a/dbux-graph-host/src/graph/controllers/GraphNode.js +++ b/dbux-graph-host/src/graph/controllers/GraphNode.js @@ -118,18 +118,18 @@ export default class GraphNode extends HostComponentEndpoint { public = { previousMode: () => { - let mode = this.getPreviousMode(); + const mode = this.getPreviousMode(); const { firstTrace: trace } = this.owner; const { context } = this.owner.state; this.setModeUser(mode); - this.componentManager.externals.emitCallGraphAction(UserActionType.CallGraphNodeCollapseChange, { mode, context, trace }); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.CallGraphNodeCollapseChange, trace, { mode, context }); }, nextMode: () => { - let mode = this.getNextMode(); + const mode = this.getNextMode(); const { firstTrace: trace } = this.owner; const { context } = this.owner.state; this.setModeUser(mode); - this.componentManager.externals.emitCallGraphAction(UserActionType.CallGraphNodeCollapseChange, { context, trace, mode }); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.CallGraphNodeCollapseChange, trace, { mode, context }); }, reveal: this.reveal } diff --git a/dbux-graph-host/src/graph/syncGraph/ContextNode.js b/dbux-graph-host/src/graph/syncGraph/ContextNode.js index 69390fcff..b3a1061b5 100644 --- a/dbux-graph-host/src/graph/syncGraph/ContextNode.js +++ b/dbux-graph-host/src/graph/syncGraph/ContextNode.js @@ -244,7 +244,7 @@ class ContextNode extends HostComponentEndpoint { const { firstTrace } = this; if (firstTrace) { traceSelection.selectTrace(firstTrace); - this.componentManager.externals.emitCallGraphTraceAction(firstTrace, UserActionType.CallGraphTrace); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.CallGraphTrace, firstTrace); } else { this.componentManager.externals.alert('Cannot find any trace of this context.', false); @@ -254,7 +254,7 @@ class ContextNode extends HostComponentEndpoint { const { callTrace } = this; if (callTrace) { traceSelection.selectTrace(callTrace); - this.componentManager.externals.emitCallGraphTraceAction(callTrace, UserActionType.CallGraphCallTrace); + this.componentManager.externals.emitCallGraphTraceAction(UserActionType.CallGraphCallTrace, callTrace); } }, // toggleStaticContextHighlight() { diff --git a/dbux-projects/src/ProjectsManager.js b/dbux-projects/src/ProjectsManager.js index 123e1e229..264ea3d32 100644 --- a/dbux-projects/src/ProjectsManager.js +++ b/dbux-projects/src/ProjectsManager.js @@ -19,7 +19,7 @@ import ExerciseStatus from './dataLib/ExerciseStatus'; import BackendController from './backend/BackendController'; import PathwaysDataProvider from './dataLib/PathwaysDataProvider'; import PracticeSessionState from './practiceSession/PracticeSessionState'; -import { emitPracticeSessionEvent, onUserEvent, emitUserEvent } from './userEvents'; +import { emitPracticeSessionEvent, onUserAction, emitUserAction } from './userActions'; import ExerciseDataProvider from './dataLib/ExerciseDataProvider'; import initLang, { getTranslationScope } from './lang'; import upload from './fileUpload'; @@ -136,9 +136,9 @@ export default class ProjectsManager { this.registerPDPEventListeners(); - // NOTE: This is for public API. To emit event in dbux-projects, register event in dbux-projects/src/userEvents.js and import it directly + // NOTE: This is for public API. To emit event in dbux-projects, register event in dbux-projects/src/userActions.js and import it directly // this.onUserEvent = onUserEvent; - this.emitUserEvent = emitUserEvent; + this.emitUserAction = emitUserAction; } async init() { @@ -662,7 +662,7 @@ export default class ProjectsManager { * #########################################################################*/ registerPDPEventListeners() { - onUserEvent((actionData) => { + onUserAction((actionData) => { if (this.practiceSession && !this.practiceSession.isStopped()) { this.practiceSession.pdp.addNewUserAction(actionData); } @@ -1287,7 +1287,7 @@ export default class ProjectsManager { // await this._backend.login(); // // Rules not edit yet, so needs login to read - // let collectionRef = this._backend.db.collection('userEvents'); + // let collectionRef = this._backend.db.collection('userActions'); // let result = await collectionRef.get(); // let allData = []; // result.forEach(doc => { @@ -1299,12 +1299,12 @@ export default class ProjectsManager { // this.externals.editor.showTextInNewFile('all.json', JSON.stringify(allData, null, 2)); } - async deleteUserEvents() { + async deleteUserActions() { await this.getAndInitBackend(); await this._backend.login(); // Rules not edit yet, so needs login to read - let collectionRef = this._backend.db.collection('userEvents'); + let collectionRef = this._backend.db.collection('userActions'); let result = await collectionRef.get(); await result.forEach(async (doc) => { await doc.ref.delete(); diff --git a/dbux-projects/src/backend/containers/UserEventContainer.js b/dbux-projects/src/backend/containers/UserEventContainer.js index 2935e445f..3fd523635 100644 --- a/dbux-projects/src/backend/containers/UserEventContainer.js +++ b/dbux-projects/src/backend/containers/UserEventContainer.js @@ -1,5 +1,5 @@ import { newLogger } from '@dbux/common/src/log/logger'; -import { onUserEvent } from '../../userEvents'; +import { onUserAction } from '../../userActions'; import BufferedFirestoreContainer from '../BufferedFirestoreContainer'; /** @typedef {import('../db').Db} Db */ @@ -15,13 +15,13 @@ export default class UserEventContainer extends BufferedFirestoreContainer { * @param {Db} db */ constructor(db) { - super(db, 'userEvents'); + super(db, 'userActions'); } async init() { super.init(); - onUserEvent(this.addEvent); + onUserAction(this.addEvent); await this.flush(); } diff --git a/dbux-projects/src/checkSystem.js b/dbux-projects/src/checkSystem.js index e713483e1..0eb7a764d 100644 --- a/dbux-projects/src/checkSystem.js +++ b/dbux-projects/src/checkSystem.js @@ -1,11 +1,10 @@ -import merge from 'lodash/merge'; import semver from 'semver'; import isArray from 'lodash/isArray'; import isFunction from 'lodash/isFunction'; import { newLogger } from '@dbux/common/src/log/logger'; import { whichNormalized } from '@dbux/common-node/src/util/pathUtil'; import Process from './util/Process'; -import { emitCheckSystemAction } from './userEvents/index'; +import { emitCheckSystemAction } from './userActions/index'; /** @typedef {import('./ProjectsManager').default} ProjectsManager */ diff --git a/dbux-projects/src/dataLib/ExerciseDataProvider.js b/dbux-projects/src/dataLib/ExerciseDataProvider.js index 6152c5e54..41f4693ad 100644 --- a/dbux-projects/src/dataLib/ExerciseDataProvider.js +++ b/dbux-projects/src/dataLib/ExerciseDataProvider.js @@ -5,7 +5,7 @@ import Collection from '@dbux/data/src/Collection'; import Indexes from '@dbux/data/src/indexes/Indexes'; import ExerciseProgressByExerciseIdIndex from './indexes/ExerciseProgressByExerciseIdIndex'; import ExerciseProgress from './ExerciseProgress'; -import { emitExerciseProgressChanged, emitNewExerciseProgress } from '../userEvents'; +import { emitExerciseProgressChanged, emitNewExerciseProgress } from '../userActions'; // eslint-disable-next-line no-unused-vars diff --git a/dbux-projects/src/dataLib/PathwaysDataProvider.js b/dbux-projects/src/dataLib/PathwaysDataProvider.js index 0e3b7a442..4eeee101f 100644 --- a/dbux-projects/src/dataLib/PathwaysDataProvider.js +++ b/dbux-projects/src/dataLib/PathwaysDataProvider.js @@ -12,7 +12,7 @@ import Indexes from '@dbux/data/src/indexes/Indexes'; import { getGroupTypeByActionType } from '@dbux/data/src/pathways/ActionGroupType'; import StepType, { getStepTypeByActionType } from '@dbux/data/src/pathways/StepType'; import { extractApplicationData, importApplication } from '../dbux-analysis-tools/importExport'; -import { emitNewTestRun, emitNewApplicationsAction } from '../userEvents'; +import { emitNewTestRun, emitNewApplicationsAction } from '../userActions'; import getFirstLine from '../util/readFirstLine'; import PathwaysDataUtil from './pathwaysDataUtil'; import TestRunsByExerciseIdIndex from './indexes/TestRunsByExerciseIdIndex'; diff --git a/dbux-projects/src/dataLib/pathwaysDataUtil.js b/dbux-projects/src/dataLib/pathwaysDataUtil.js index 5a30ee66c..7ef29db8b 100644 --- a/dbux-projects/src/dataLib/pathwaysDataUtil.js +++ b/dbux-projects/src/dataLib/pathwaysDataUtil.js @@ -79,9 +79,7 @@ export default { } const { applicationId } = trace; - - const applicationSet = allApplications; - return applicationSet.getById(applicationId); + return allApplications.getById(applicationId); }, @@ -238,11 +236,11 @@ export default { }, getActionStaticContextId(pdp, action) { - if (action.staticContext) { - // hackfix: in pdp version=1, EditorEvents does not contains applicationId, we can only assume it's 1 now - const { applicationId = 1, staticContext: { staticContextId } } = action; - return { applicationId, staticContextId }; - } + // if (action.staticContext) { + // // hackfix: in pdp version=1, EditorEvents does not contains applicationId, we can only assume it's 1 now + // const { applicationId = 1, staticContext: { staticContextId } } = action; + // return { applicationId, staticContextId }; + // } const dp = pdp.util.getActionApplication(action)?.dataProvider; if (!dp) { diff --git a/dbux-projects/src/practiceSession/PathwaysSession.js b/dbux-projects/src/practiceSession/PathwaysSession.js index 14a5a7898..0ef608351 100644 --- a/dbux-projects/src/practiceSession/PathwaysSession.js +++ b/dbux-projects/src/practiceSession/PathwaysSession.js @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid'; import { newLogger } from '@dbux/common/src/log/logger'; import allApplications from '@dbux/data/src/applications/allApplications'; import PathwaysDataProvider from '../dataLib/PathwaysDataProvider'; -import { emitSessionFinishedEvent } from '../userEvents'; +import { emitSessionFinishedEvent } from '../userActions'; import PracticeSessionState, { isStateFinishedType, isStateStoppedType } from './PracticeSessionState'; // eslint-disable-next-line no-unused-vars diff --git a/dbux-projects/src/practiceSession/PracticeSession.js b/dbux-projects/src/practiceSession/PracticeSession.js index 7e9fc6169..d90235138 100644 --- a/dbux-projects/src/practiceSession/PracticeSession.js +++ b/dbux-projects/src/practiceSession/PracticeSession.js @@ -3,7 +3,7 @@ import allApplications from '@dbux/data/src/applications/allApplications'; import Stopwatch from './Stopwatch'; import PracticeSessionState from './PracticeSessionState'; import ExerciseStatus from '../dataLib/ExerciseStatus'; -import { emitSessionFinishedEvent } from '../userEvents'; +import { emitSessionFinishedEvent } from '../userActions'; import PracticeSessionData from './PracticeSessionData'; import PathwaysSession from './PathwaysSession'; diff --git a/dbux-projects/src/userEvents/index.js b/dbux-projects/src/userActions/index.js similarity index 61% rename from dbux-projects/src/userEvents/index.js rename to dbux-projects/src/userActions/index.js index a9c984668..a94afe1db 100644 --- a/dbux-projects/src/userEvents/index.js +++ b/dbux-projects/src/userActions/index.js @@ -3,13 +3,14 @@ import UserActionType from '@dbux/data/src/pathways/UserActionType'; import EmptyArray from '@dbux/common/src/util/EmptyArray'; import { newLogger } from '@dbux/common/src/log/logger'; +/** @typedef {import('@dbux/data/src/pathways/UserAction').default} UserAction */ /** @typedef {import('../practiceSession/PracticeSession').default} PracticeSession */ // eslint-disable-next-line no-unused-vars -const { log, debug, warn, error: logError } = newLogger('UserEvents'); +const { log, debug, warn, error: logError } = newLogger('UserActions'); -// const Verbose = true; -const Verbose = false; +const Verbose = true; +// const Verbose = false; // ########################################################################### // event registry @@ -20,7 +21,7 @@ const Verbose = false; * @param {PracticeSession} practiceSession */ export function emitPracticeSessionEvent(eventName, practiceSession) { - emitUserEvent(UserActionType.PracticeSessionChanged, { + emitUserAction(UserActionType.PracticeSessionChanged, { eventType: eventName, sessionId: practiceSession.sessionId, exerciseId: practiceSession.exerciseId @@ -32,11 +33,11 @@ export function emitPracticeSessionEvent(eventName, practiceSession) { * @param {number} [createdAt] hackfix to override the time */ export function emitSessionFinishedEvent(state, createdAt) { - emitUserEvent(UserActionType.SessionFinished, { state, createdAt }); + emitUserAction(UserActionType.SessionFinished, { state, createdAt }); } export function emitNewTestRun(testRun) { - emitUserEvent(UserActionType.TestRunFinished, { + emitUserAction(UserActionType.TestRunFinished, { testRun, // new test run always introduces a new step // newStep: true @@ -44,20 +45,20 @@ export function emitNewTestRun(testRun) { } export function emitNewExerciseProgress(exerciseProgress) { - emitUserEvent(UserActionType.NewExerciseProgress, { exerciseProgress }); + emitUserAction(UserActionType.NewExerciseProgress, { exerciseProgress }); } export function emitExerciseProgressChanged(exerciseProgress) { - emitUserEvent(UserActionType.ExerciseProgressChanged, { exerciseProgress }); + emitUserAction(UserActionType.ExerciseProgressChanged, { exerciseProgress }); } export function emitCheckSystemAction(success, result) { - emitUserEvent(UserActionType.CheckSystem, { success, result }); + emitUserAction(UserActionType.CheckSystem, { success, result }); } export function emitNewApplicationsAction(apps = EmptyArray) { const uuids = apps.map(app => app.uuid); - emitUserEvent(UserActionType.NewApplications, { uuids }); + emitUserAction(UserActionType.NewApplications, { uuids }); } // ########################################################################### @@ -70,44 +71,33 @@ export function emitNewApplicationsAction(apps = EmptyArray) { const emitter = new NanoEvents(); /** - * Callback for adding two numbers. - * * @callback onUserEventCb - * @param {UserEvent} e - */ - -/** - * @typedef {Object} UserEvent - * @property {string} name - * @property {string} sessionId - * @property {string} exerciseId - * @property {Object} data - * @property {number} createdAt + * @param {UserAction} e */ /** * @param {onUserEventCb} cb */ -export function onUserEvent(cb) { +export function onUserAction(cb) { return emitter.on('e', cb); } /** * @param {number} eventType - * @param {Object} evtData NOTE: data *must* always be completely serializable, simple data. + * @param {Object} eventData NOTE: data *must* always be completely serializable, simple data. */ -export function emitUserEvent(eventType, evtData) { +export function emitUserAction(eventType, eventData) { emitter.emit('e', { type: eventType, createdAt: Date.now(), - ...evtData + ...eventData }); } if (Verbose) { - onUserEvent((evt) => { - const { type, createdAt, ...evtData } = evt; + onUserAction((userAction) => { + const { type, createdAt, ...evtData } = userAction; const typeName = UserActionType.nameFromForce(type); - log(`Emit user event "${typeName}", additional data=${JSON.stringify(evtData)}`); + log(`Event "${typeName}", data=${JSON.stringify(evtData)}`); }); } \ No newline at end of file diff --git a/docs_site/content/_partials/_dbux-code-commands.mdx b/docs_site/content/_partials/_dbux-code-commands.mdx index 1eb99ce59..27bb08195 100644 --- a/docs_site/content/_partials/_dbux-code-commands.mdx +++ b/docs_site/content/_partials/_dbux-code-commands.mdx @@ -2,7 +2,7 @@ | ----------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | dbux.backendLogin | Dbux: Backend Login | (Feature still in development. Won't work.) | | dbux.debugFile | Dbux: Debug Current File | Run selected file with Dbux, but with Node's `--inspect-brk` enabled. Make sure to enable VSCode's auto attach beforehand. | -| dbux.deleteUserEvents | Dbux Dev: Delete all user events | (Feature still in development. Won't work.) | +| dbux.deleteUserActions | Dbux Dev: Delete all user events | (Feature still in development. Won't work.) | | dbux.diagnostics | Dbux: Diagnostics | | | dbux.doActivate | Dbux: Start Dbux | | | dbux.exportApplicationData | Dbux: Export Application Data | Export raw recorded Dbux data of a previously executed application to a `json` file. | diff --git a/docs_site/data/commands.json b/docs_site/data/commands.json index 0137e1baf..102ad6a83 100644 --- a/docs_site/data/commands.json +++ b/docs_site/data/commands.json @@ -10,7 +10,7 @@ "description": "Run selected file with Dbux, but with Node's `--inspect-brk` enabled. Make sure to enable VSCode's auto attach beforehand." }, { - "command": "dbux.deleteUserEvents", + "command": "dbux.deleteUserActions", "title": "Dbux Dev: Delete all user events", "description": "(Feature still in development. Won't work.)" }, From 893d420833bf0515245575ee5e8679e27746e383 Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 4 May 2022 16:30:13 +0800 Subject: [PATCH 5/8] fix `PathwaysDataBuffer` wont recover data when upload failed --- dbux-code/src/workshop/PathwaysDataBuffer.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/dbux-code/src/workshop/PathwaysDataBuffer.js b/dbux-code/src/workshop/PathwaysDataBuffer.js index 9d9ac7b62..3e21655b4 100644 --- a/dbux-code/src/workshop/PathwaysDataBuffer.js +++ b/dbux-code/src/workshop/PathwaysDataBuffer.js @@ -62,25 +62,19 @@ export default class PathwaysDataBuffer { try { await this.buffer.acquireLock(); - let buffer; - try { - buffer = this.safeGetBuffer(); - await this.buffer.set([]); - } - finally { - this.buffer.releaseLock(); - } - try { + const buffer = this.safeGetBuffer(); await this.addDoc(buffer); } catch (err) { throw new NestedError(`Failed when flushing`, err); } + await this.buffer.set([]); this._previousFlushTime = Date.now(); } finally { + this.buffer.releaseLock(); this._flushing = false; } } From c2a3a52c900ab6e3ce971b41b472ac596d2ee06c Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 4 May 2022 16:30:43 +0800 Subject: [PATCH 6/8] resolve `fs.rmdir` depreacted warning --- dbux-code/src/terminal/TerminalWrapper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbux-code/src/terminal/TerminalWrapper.js b/dbux-code/src/terminal/TerminalWrapper.js index 5dc6e0f46..bdf0534d8 100644 --- a/dbux-code/src/terminal/TerminalWrapper.js +++ b/dbux-code/src/terminal/TerminalWrapper.js @@ -144,7 +144,7 @@ export default class TerminalWrapper { } finally { // this.dispose(); try { - fs.rmdirSync(tmpFolder, { force: true, recursive: true }); + fs.rmSync(tmpFolder, { force: true, recursive: true }); } catch (err) { debug(`(unable to remove temp folder "${tmpFolder}" - ${err.message})`); From 9631d8c2833aae4ac6f69e9d60801f93e488555c Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 4 May 2022 18:39:59 +0800 Subject: [PATCH 7/8] misc fixes for pathways --- .../codeUtil/treeView/TraceContainerNode.js | 6 +-- dbux-code/src/practice/codeEvents.js | 31 ++++++-------- dbux-code/src/preActivateView/ActivateNode.js | 2 +- .../src/traceDetailsView/nodes/ValueNode.js | 13 +++++- .../traceDetailsController.js | 2 +- dbux-code/src/userActions.js | 37 ++++++++++------- dbux-data/src/pathways/UserActionType.js | 32 ++++++--------- .../src/pathways/PathwaysActionGroup.js | 11 +---- .../src/pathways/PathwaysActionGroup.js | 8 +++- .../src/pathways/PathwaysTimeline.js | 2 +- dbux-graph-host/src/pathways/PathwaysView.js | 27 +++++++------ .../src/dataLib/PathwaysDataProvider.js | 19 ++++----- dbux-projects/src/dataLib/pathwaysDataUtil.js | 40 +++++-------------- 13 files changed, 107 insertions(+), 123 deletions(-) diff --git a/dbux-code/src/codeUtil/treeView/TraceContainerNode.js b/dbux-code/src/codeUtil/treeView/TraceContainerNode.js index db8be8d98..0c5282d43 100644 --- a/dbux-code/src/codeUtil/treeView/TraceContainerNode.js +++ b/dbux-code/src/codeUtil/treeView/TraceContainerNode.js @@ -12,7 +12,6 @@ import TraceNode from './TraceNode'; export class GroupNode extends BaseTreeViewNode { static labelSuffix = ''; - static TraceNodeClass = TraceNode; /** * @abstract @@ -104,7 +103,7 @@ export class GroupNode extends BaseTreeViewNode { } buildChildren() { - const { TraceNodeClass } = this.constructor; + const { TraceNodeClass } = this.parent.constructor; return this.childTraces.map(trace => { return this.treeNodeProvider.buildNode(TraceNodeClass, trace, this); }); @@ -121,7 +120,8 @@ export class UngroupedNode extends GroupNode { } static build(rootNode, trace) { - return rootNode.treeNodeProvider.buildNode(this.TraceNodeClass, trace, rootNode); + const { TraceNodeClass } = rootNode.constructor; + return rootNode.treeNodeProvider.buildNode(TraceNodeClass, trace, rootNode); } } diff --git a/dbux-code/src/practice/codeEvents.js b/dbux-code/src/practice/codeEvents.js index 1453daa08..6629fa681 100644 --- a/dbux-code/src/practice/codeEvents.js +++ b/dbux-code/src/practice/codeEvents.js @@ -4,7 +4,7 @@ import { commands, SymbolKind, window } from 'vscode'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; import { newLogger } from '@dbux/common/src/log/logger'; import { emitEditorAction } from '../userActions'; -// import { getOrCreateTracesAtCursor } from '../codeUtil/TracesAtCursor'; +import { getOrCreateTracesAtCursor } from '../codeUtil/TracesAtCursor'; import { codeRangeToBabelLoc } from '../helpers/codeLocHelpers'; /** @typedef {import('@dbux/projects/src/ProjectsManager').default} ProjectsManager */ @@ -22,6 +22,7 @@ const defaultNewEventLineThreshold = 8; */ export function initCodeEvents(manager, context) { let _previousSelectionData, _previousVisibleRangeData; + const traceAtCursor = getOrCreateTracesAtCursor(context); window.onDidChangeTextEditorSelection(async (e) => { if (!manager.practiceSession) { @@ -46,8 +47,9 @@ export function initCodeEvents(manager, context) { if (isNewData(_previousSelectionData, data)) { Verbose && debug('is new'); _previousSelectionData = data; - const extraEditorEventInfo = await getExtraEditorEventInfo(e.textEditor); - emitEditorAction(UserActionType.EditorSelectionChanged, data, extraEditorEventInfo); + const trace = traceAtCursor.getMostInner(); + const symbol = await getCurrentSymbol(e.textEditor); + emitEditorAction(UserActionType.EditorSelectionChanged, { ...data, trace }, { symbol }); } }); @@ -71,29 +73,22 @@ export function initCodeEvents(manager, context) { if (isNewData(_previousVisibleRangeData, data)) { Verbose && debug('is new'); _previousVisibleRangeData = data; - const extraEditorEventInfo = await getExtraEditorEventInfo(e.textEditor); - emitEditorAction(UserActionType.EditorVisibleRangeChanged, data, extraEditorEventInfo); + const trace = traceAtCursor.getMostInner(); + const symbol = await getCurrentSymbol(e.textEditor); + emitEditorAction(UserActionType.EditorVisibleRangeChanged, { ...data, trace }, { symbol }); } }); - - // ########################################################################### - // extra data for code events - // ########################################################################### - - async function getExtraEditorEventInfo(editor) { - // const trace = traceAtCursor.getMostInner(); - const symbol = await getSymbolAt(editor.document.uri, editor.selections[0]?.start); - - return { - symbol: convertVSCodeSymbol(symbol), - }; - } } // ########################################################################### // utils // ########################################################################### +async function getCurrentSymbol(editor) { + const symbol = await getSymbolAt(editor.document.uri, editor.selections[0]?.start); + return convertVSCodeSymbol(symbol); +} + function convertVSCodeSymbol(symbol) { if (symbol) { return { diff --git a/dbux-code/src/preActivateView/ActivateNode.js b/dbux-code/src/preActivateView/ActivateNode.js index 5ad35bcdd..9dd3e72c2 100644 --- a/dbux-code/src/preActivateView/ActivateNode.js +++ b/dbux-code/src/preActivateView/ActivateNode.js @@ -2,7 +2,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; export default class ActivateNode extends TreeItem { constructor() { - super('Start Dbux', TreeItemCollapsibleState.None); + super('Start Dbux (no workshop)', TreeItemCollapsibleState.None); this.command = { command: 'dbux.doActivate' diff --git a/dbux-code/src/traceDetailsView/nodes/ValueNode.js b/dbux-code/src/traceDetailsView/nodes/ValueNode.js index daf743f5f..802dd5763 100644 --- a/dbux-code/src/traceDetailsView/nodes/ValueNode.js +++ b/dbux-code/src/traceDetailsView/nodes/ValueNode.js @@ -5,6 +5,8 @@ import allApplications from '@dbux/data/src/applications/allApplications'; import traceSelection from '@dbux/data/src/traceSelection'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; import BaseTreeViewNode from '../../codeUtil/treeView/BaseTreeViewNode'; +import { emitTraceUserAction } from '../../userActions'; +import { showInformationMessage } from '../../codeUtil/codeModals'; // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('ValueTDNode'); @@ -52,7 +54,13 @@ export default class ValueNode extends BaseTreeViewNode { const writeNode = dp.collections.dataNodes.getById(this.nodeId); const writeTrace = dp.collections.traces.getById(writeNode.traceId); - traceSelection.selectTrace(writeTrace, writeNode.nodeId); + if (writeTrace) { + traceSelection.selectTrace(writeTrace, writeNode.nodeId); + emitTraceUserAction(UserActionType.TDValueWriteTrace, writeTrace); + } + else { + showInformationMessage(`Cannot find the last write trace.`); + } } selectValueCreationTrace() { @@ -62,9 +70,10 @@ export default class ValueNode extends BaseTreeViewNode { if (firstNodeByValue) { const firstTraceByValue = dp.collections.traces.getById(firstNodeByValue.traceId); traceSelection.selectTrace(firstTraceByValue); + emitTraceUserAction(UserActionType.TDValueCreationTrace, firstTraceByValue); } else { - logError(`Cannot find value creation trace for dataNode:${JSON.stringify(this.dataNode)}`); + showInformationMessage(`Cannot find value creation trace.`); } } } \ No newline at end of file diff --git a/dbux-code/src/traceDetailsView/traceDetailsController.js b/dbux-code/src/traceDetailsView/traceDetailsController.js index abad59b71..99b7d3093 100644 --- a/dbux-code/src/traceDetailsView/traceDetailsController.js +++ b/dbux-code/src/traceDetailsView/traceDetailsController.js @@ -105,7 +105,7 @@ class TraceDetailsController { } if (trace) { traceSelection.selectTrace(trace, 'selectTraceAtCursor'); - emitTraceUserAction(UserActionType.selectTrace, trace); + emitTraceUserAction(UserActionType.SelectTrace, trace); } } diff --git a/dbux-code/src/userActions.js b/dbux-code/src/userActions.js index 3c507b066..94480fd5f 100644 --- a/dbux-code/src/userActions.js +++ b/dbux-code/src/userActions.js @@ -10,6 +10,8 @@ import PackageNodeSortMode from './globalAnalysisView/nodes/PackageNodeSortMode' /** @typedef {import('@dbux/common/src/types/Loc').default} Loc */ /** @typedef {import('@dbux/common/src/types/Trace').default} Trace */ +/** @typedef {import('@dbux/common/src/types/StaticContext.default} StaticContext */ +/** @typedef {import('@dbux/common/src/types/StaticTrace').default} StaticTrace */ /** @typedef {import('@dbux/projects/src/ProjectsManager').ProjectsManager} */ // eslint-disable-next-line no-unused-vars @@ -33,7 +35,7 @@ export function initUserAction(_manager) { // ########################################################################### /** - * The most basic `UserActions`, which contains no location nor trace info + * The most basic `UserActions`, which contains no location nor trace */ export function emitBasicUserAction(actionType, data) { emitUserAction(actionType, data); @@ -50,9 +52,10 @@ export function emitLocUserAction(actionType, file, range, moreProp) { * `UserActions` that related to some trace, we use the information to find the related location */ export function emitTraceUserAction(actionType, trace, moreProp = EmptyObject) { - const { file, range, ...moreTraceInformation } = makeTraceLocationInformation(trace); - emitLocUserAction(actionType, file, range, { - ...moreTraceInformation, + const moreTraceInfo = makeTraceStaticInformation(trace); + emitUserAction(actionType, { + trace, + ...moreTraceInfo, ...moreProp }); } @@ -165,8 +168,13 @@ export function emitShowHideErrorLogNotificationAction(isShowing) { /** * NOTE: For editor events e.g. `EditorSelectionChanged` */ -export function emitEditorAction(actionType, { file, range }, moreProp) { - emitLocUserAction(actionType, file, range, moreProp); +export function emitEditorAction(actionType, { file, range, trace }, extraEditorEventInfo) { + if (trace) { + emitTraceUserAction(actionType, trace, extraEditorEventInfo); + } + else { + emitLocUserAction(actionType, file, range, extraEditorEventInfo); + } } export function emitNavigationAction(trace, navigationMethodName) { @@ -218,20 +226,21 @@ export function emitPathwaysAction(actionType, data) { /** * * @param {Trace} trace - * @returns {{file: string, range: Loc, applicationId: number, traceId: number}} + * @returns {{applicationId: number, file: string, staticContext: StaticContext, staticTrace: StaticTrace}} */ -function makeTraceLocationInformation(trace) { - const { applicationId, traceId } = trace; +function makeTraceStaticInformation(trace) { + const { applicationId, traceId, staticTraceId } = trace; const app = allApplications.getById(applicationId); const dp = app.dataProvider; - const file = pathRelative(app.entryPointPath, dp.util.getTraceFilePath(traceId)); - const range = dp.util.getTraceLoc(traceId); + const staticTrace = dp.collections.staticTraces.getById(staticTraceId); + const staticContext = dp.util.getTraceStaticContext(traceId); + const { filePath: file } = dp.collections.staticProgramContexts.getById(staticContext.programId); return { - file, - range, applicationId, - traceId + file, + staticContext, + staticTrace, }; } diff --git a/dbux-data/src/pathways/UserActionType.js b/dbux-data/src/pathways/UserActionType.js index d805b3bc1..a1a24bef0 100644 --- a/dbux-data/src/pathways/UserActionType.js +++ b/dbux-data/src/pathways/UserActionType.js @@ -24,22 +24,24 @@ const UserActionTypeObj = { TDValueClick: 22, TDValueCollapseChange: 23, TDValueRender: 24, - TDTrackObjectUse: 25, - TDTrackObjectTraceUse: 26, + TDValueWriteTrace: 25, + TDValueCreationTrace: 26, + TDTrackObjectUse: 27, + TDTrackObjectTraceUse: 28, /** * Collapse/expand "Executions xN" */ - TDExecutionsUse: 27, + TDExecutionsUse: 29, /** * Select a trace under "Executions xN" */ - TDExecutionsTraceUse: 28, - TDExecutionsGroupModeChanged: 29, - TDTraceUse: 30, - TDDebugUse: 31, - TDAsyncUse: 32, - TDAsyncGoToForkParent: 33, - TDAsyncGoToScheduler: 34, + TDExecutionsTraceUse: 30, + TDExecutionsGroupModeChanged: 31, + TDTraceUse: 32, + TDDebugUse: 33, + TDAsyncUse: 34, + TDAsyncGoToForkParent: 35, + TDAsyncGoToScheduler: 36, NavigationPreviousInContext: 40, NavigationPreviousChildContext: 41, @@ -76,7 +78,7 @@ const UserActionTypeObj = { CallGraphCallTrace: 103, CallGraphVisibilityChanged: 104, CallGraphGraphDocumentModeChanged: 105, - + AsyncCallGraphTrace: 110, AsyncCallGraphSchedulerTrace: 111, AsyncCallGraphValueTrace: 112, @@ -104,11 +106,3 @@ const UserActionType = new Enum(UserActionTypeObj); export default UserActionType; - -const codeActionTypes = new Array(UserActionType.getValueMaxIndex()).map(() => false); -codeActionTypes[UserActionType.EditorSelectionChanged] = true; -codeActionTypes[UserActionType.EditorVisibleRangeChanged] = true; - -export function isCodeActionTypes(actionType) { - return codeActionTypes[actionType]; -} \ No newline at end of file diff --git a/dbux-graph-client/src/pathways/PathwaysActionGroup.js b/dbux-graph-client/src/pathways/PathwaysActionGroup.js index 5229e2876..e472dabcf 100644 --- a/dbux-graph-client/src/pathways/PathwaysActionGroup.js +++ b/dbux-graph-client/src/pathways/PathwaysActionGroup.js @@ -57,16 +57,7 @@ class PathwaysActionGroup extends ClientComponentEndpoint { btn: { async click(evt) { const groupId = this.state._id; - if (this.state.hasTrace) { - this.context.view.remote.selectGroupTrace(groupId); - } - else if (ActionGroupType.is.EditorSelectionChanged(this.state.type)) { - // select staticContext(for EditorSelectionChanged) - // this.context.view.remote.selectStepStaticTrace(this.state.stepId); - - // go to actual select position - await this.context.view.remote.gotoActionGroupEditorPosition(groupId); - } + await this.context.view.remote.selectActionGroup(groupId); document.getSelection().removeAllRanges(); } } diff --git a/dbux-graph-host/src/pathways/PathwaysActionGroup.js b/dbux-graph-host/src/pathways/PathwaysActionGroup.js index 453297077..d25b6c0a6 100644 --- a/dbux-graph-host/src/pathways/PathwaysActionGroup.js +++ b/dbux-graph-host/src/pathways/PathwaysActionGroup.js @@ -3,7 +3,12 @@ import ActionGroupType from '@dbux/data/src/pathways/ActionGroupType'; import HostComponentEndpoint from '../componentLib/HostComponentEndpoint'; import { getIconByActionGroup, makeStepBackground } from './renderSettings'; +/** @typedef {import('@dbux/projects/src/dataLib/PathwaysDataProvider').default} PathwaysDataProvider */ + class PathwaysActionGroup extends HostComponentEndpoint { + /** + * @type {PathwaysDataProvider} + */ get pdp() { return this.context.doc.pdp; } @@ -16,11 +21,12 @@ class PathwaysActionGroup extends HostComponentEndpoint { } = this.state; const { themeMode } = this.context; + const action = this.pdp.util.getActionGroupAction(groupId); this.state.typeName = ActionGroupType.getName(type); this.state.iconUri = this.context.doc.getIconUri(getIconByActionGroup(type)); this.state.timeSpent = formatTime(this.pdp.util.getActionGroupTimeSpent(groupId)); - this.state.hasTrace = !!this.pdp.util.getActionGroupAction(groupId)?.trace; + this.state.hasTrace = !!this.pdp.util.getActionTrace(action); const step = this.pdp.collections.steps.getById(stepId); this.state.background = makeStepBackground(step, themeMode); diff --git a/dbux-graph-host/src/pathways/PathwaysTimeline.js b/dbux-graph-host/src/pathways/PathwaysTimeline.js index 1ca2f6652..a89a418d9 100644 --- a/dbux-graph-host/src/pathways/PathwaysTimeline.js +++ b/dbux-graph-host/src/pathways/PathwaysTimeline.js @@ -75,7 +75,7 @@ class PathwaysTimeline extends HostComponentEndpoint { addedStaticTraceIds.add(trace.staticTraceId); return true; } - const staticContextId = this.pdp.util.getActionStaticContextId(action)?.staticContextId; + const staticContextId = this.pdp.util.getActionStaticContext(action)?.staticContextId; if (staticContextId && !addedStaticContextIds.has(staticContextId)) { addedStaticContextIds.add(staticContextId); return true; diff --git a/dbux-graph-host/src/pathways/PathwaysView.js b/dbux-graph-host/src/pathways/PathwaysView.js index 4852330b8..371b16889 100644 --- a/dbux-graph-host/src/pathways/PathwaysView.js +++ b/dbux-graph-host/src/pathways/PathwaysView.js @@ -13,7 +13,7 @@ import PathwaysActionGroup from './PathwaysActionGroup'; import PathwaysStepGroup from './PathwaysStepGroup'; import { getIconByStep, makeStepBackground } from './renderSettings'; - +/** @typedef {import('@dbux/projects/src/dataLib/PathwaysDataProvider').default} PathwaysDataProvider */ class PathwaysView extends HostComponentEndpoint { /** @@ -33,6 +33,9 @@ class PathwaysView extends HostComponentEndpoint { // */ // actions; + /** + * @type {PathwaysDataProvider} + */ get pdp() { return this.context.doc.pdp; } @@ -256,10 +259,17 @@ class PathwaysView extends HostComponentEndpoint { } }, - selectGroupTrace(groupId) { - const trace = this.pdp.util.getActionGroupAction(groupId)?.trace; - if (trace) { - traceSelection.selectTrace(trace); + async selectActionGroup(groupId) { + const action = this.pdp.util.getActionGroupAction(groupId); + if (!action) { + return; + } + + if (action.file && action.staticTrace) { + await this.componentManager.externals.goToCodeLoc(action.file, action.staticTrace.loc); + } + else if (action.file && action.range) { + await this.componentManager.externals.goToCodeLoc(action.file, action.range); } }, @@ -273,13 +283,6 @@ class PathwaysView extends HostComponentEndpoint { traceSelection.selectTrace(firstTrace); } } - }, - async gotoActionGroupEditorPosition(actionGroupId) { - // const actionGroup = this.pdp.collections.steps.getById(actionGroupId); - const action = this.pdp.indexes.userActions.byGroup.getFirst(actionGroupId); - if (action?.file && action.range) { - await this.componentManager.externals.goToCodeLoc(action.file, action.range); - } } } } diff --git a/dbux-projects/src/dataLib/PathwaysDataProvider.js b/dbux-projects/src/dataLib/PathwaysDataProvider.js index 3b2703c83..4dda0f575 100644 --- a/dbux-projects/src/dataLib/PathwaysDataProvider.js +++ b/dbux-projects/src/dataLib/PathwaysDataProvider.js @@ -5,7 +5,6 @@ import { newLogger } from '@dbux/common/src/log/logger'; import NestedError from '@dbux/common/src/NestedError'; import { pathJoin, pathRelative } from '@dbux/common-node/src/util/pathUtil'; import allApplications from '@dbux/data/src/applications/allApplications'; -import { isCodeActionTypes } from '@dbux/data/src/pathways/UserActionType'; import DataProviderBase from '@dbux/data/src/DataProviderBase'; import Collection from '@dbux/data/src/Collection'; import Indexes from '@dbux/data/src/indexes/Indexes'; @@ -160,7 +159,7 @@ class UserActionCollection extends PathwaysCollection { resolveVisitedStaticTracesIndex(userActions) { const warnedApplications = new Set(); for (const action of userActions) { - const { trace } = action; + const trace = this.dp.util.getActionTrace(action); if (!trace) { continue; } @@ -188,14 +187,14 @@ class UserActionCollection extends PathwaysCollection { serialize(action) { action = { ...action }; - if (isCodeActionTypes(action.type)) { + if (action.file) { action.file = pathRelative(allApplications.appRoot, action.file); } return action; } deserialize(action) { - if (isCodeActionTypes(action.type)) { + if (action.file) { action.file = pathJoin(allApplications.appRoot, action.file); } return action; @@ -310,10 +309,8 @@ export default class PathwaysDataProvider extends DataProviderBase { // ########################################################################### addNewStep(firstAction, writeToLog = true) { - const { - applicationId = 0, - staticContextId = 0 - } = this.util.getActionStaticContextId(firstAction) || EmptyObject; + const { applicationId = 0 } = firstAction; + const { staticContextId = 0 } = this.util.getActionStaticContext(firstAction) || EmptyObject; const { sessionId, @@ -395,10 +392,8 @@ export default class PathwaysDataProvider extends DataProviderBase { shouldAddNewStep(action) { // NOTE: action._id is not set yet (will be set during `addData`) - const { - applicationId, - staticContextId, - } = this.util.getActionStaticContextId(action) || EmptyObject; + const { applicationId } = action; + const { staticContextId } = this.util.getActionStaticContext(action) || EmptyObject; const lastStep = this.collections.steps.getLast(); const lastStepType = lastStep?.type || StepType.None; const lastStaticContextId = lastStep?.staticContextId || 0; diff --git a/dbux-projects/src/dataLib/pathwaysDataUtil.js b/dbux-projects/src/dataLib/pathwaysDataUtil.js index 7ef29db8b..0dc62dbea 100644 --- a/dbux-projects/src/dataLib/pathwaysDataUtil.js +++ b/dbux-projects/src/dataLib/pathwaysDataUtil.js @@ -73,13 +73,8 @@ export default { }, getActionApplication(pdp, action) { - const { trace } = action; - if (!trace) { - return null; - } - - const { applicationId } = trace; - return allApplications.getById(applicationId); + const { applicationId } = action; + return allApplications.getById(applicationId) || null; }, @@ -143,6 +138,11 @@ export default { return pdp.indexes.userActions.byGroup.getFirst(actionGroupId); }, + /** @param {PathwaysDataProvider} pdp */ + getActionTrace(pdp, action) { + return action.trace || null; + }, + getActionGroupTimeSpent(pdp, actionGroupId) { const actionGroup = pdp.collections.actionGroups.getById(actionGroupId); const endTime = pdp.util.getActionGroupEndTime(actionGroupId); @@ -229,28 +229,10 @@ export default { // return dp.util.getCodeChunkId(traceId); // }, - - getStaticContextId(pdp, actionId) { - const action = pdp.collections.userActions.getById(actionId); - return pdp.util.getActionStaticContextId(action); - }, - - getActionStaticContextId(pdp, action) { - // if (action.staticContext) { - // // hackfix: in pdp version=1, EditorEvents does not contains applicationId, we can only assume it's 1 now - // const { applicationId = 1, staticContext: { staticContextId } } = action; - // return { applicationId, staticContextId }; - // } - - const dp = pdp.util.getActionApplication(action)?.dataProvider; - if (!dp) { - return null; - } - - const { trace: { applicationId, contextId } } = action; - const { staticContextId } = dp.collections.executionContexts.getById(contextId); - // const staticContext = dp.collections.staticContexts.getById(staticContextId); - return { applicationId, staticContextId }; + /** @param {PathwaysDataProvider} pdp */ + getActionStaticContext(pdp, action) { + const { staticContext } = action; + return staticContext || null; }, From 371710d9d3c2a8ac179dcbd1aeff9135e71bb0f4 Mon Sep 17 00:00:00 2001 From: MiccWan Date: Wed, 4 May 2022 20:28:17 +0800 Subject: [PATCH 8/8] misc fixes for annoying pathways staff --- dbux-code/src/activate.js | 5 +- dbux-code/src/preActivate.js | 20 +-- dbux-code/src/userActions.js | 4 +- .../src/workshop/PathwaysDataContainer.js | 34 +++-- .../src/workshop/WorkshopSessionController.js | 133 ++++++++++++++++++ dbux-code/src/workshop/index.js | 71 ---------- dbux-projects/src/firestore/upload.js | 10 +- dbux-projects/src/userActions/index.js | 4 +- 8 files changed, 174 insertions(+), 107 deletions(-) create mode 100644 dbux-code/src/workshop/WorkshopSessionController.js delete mode 100644 dbux-code/src/workshop/index.js diff --git a/dbux-code/src/activate.js b/dbux-code/src/activate.js index 5b1418188..f6216b344 100644 --- a/dbux-code/src/activate.js +++ b/dbux-code/src/activate.js @@ -21,7 +21,7 @@ import { initDialogController } from './dialogs/dialogController'; import DialogNodeKind from './dialogs/DialogNodeKind'; import { showInformationMessage } from './codeUtil/codeModals'; import { translate } from './lang'; -import { initWorkshopSession } from './workshop'; +import { getWorkshopSessionController } from './workshop/WorkshopSessionController'; // import { initPlugins } from './PluginMgr'; // eslint-disable-next-line no-unused-vars @@ -37,7 +37,8 @@ export default async function activate(context) { // make sure, projectManager is available const projectsManager = createProjectManager(context); - await initWorkshopSession(projectsManager); + const workshopSessionController = getWorkshopSessionController(); + workshopSessionController.initWorkshopSession(projectsManager); // install dependencies (and show progress bar) right away await installDbuxDependencies(); diff --git a/dbux-code/src/preActivate.js b/dbux-code/src/preActivate.js index e6dc20ccb..d84c3771b 100644 --- a/dbux-code/src/preActivate.js +++ b/dbux-code/src/preActivate.js @@ -1,6 +1,5 @@ import { workspace, commands, window } from 'vscode'; import { newLogger } from '@dbux/common/src/log/logger'; -import EmptyObject from '@dbux/common/src/util/EmptyObject'; import { initMemento, get as mementoGet, set as mementoSet } from './memento'; import { initInstallId } from './installId'; import { initLogging } from './logging'; @@ -10,8 +9,7 @@ import { initPreActivateView } from './preActivateView/preActivateNodeProvider'; import { registerCommand } from './commands/commandUtil'; import initLang from './lang'; import { getCurrentResearch } from './research/Research'; -import { setupWorkshopSession, isValidCode } from './workshop'; -import { showInformationMessage } from './codeUtil/codeModals'; +import { initWorkshopSessionController } from './workshop/WorkshopSessionController'; /** @typedef {import('./dialogs/dialogController').DialogController} DialogController */ @@ -61,6 +59,7 @@ export default async function preActivate(context) { await maybeSelectLanguage(); await initLang(mementoGet('dbux.language')); + const workshopSessionController = initWorkshopSessionController(); // [debugging] // await dialogController.getDialog('survey1').clear(); @@ -79,7 +78,7 @@ export default async function preActivate(context) { } else { initPreActivateView(); - initPreActivateCommand(context); + initPreActivateCommand(context, workshopSessionController); } } catch (err) { @@ -109,21 +108,14 @@ async function ensureActivate(context) { /** * @param {import('vscode').ExtensionContext} context */ -function initPreActivateCommand(context) { +function initPreActivateCommand(context, workshopSessionController) { registerCommand(context, 'dbux.doActivate', async () => ensureActivate(context)); registerCommand(context, 'dbux.doWorkshopActivate', async () => { - const code = await window.showInputBox({ - ignoreFocusOut: true, - placeHolder: 'Enter Workshop Code' - }); - if (isValidCode(code)) { - setupWorkshopSession(code); + const result = await workshopSessionController.askForEnable(); + if (result) { await ensureActivate(context); } - else { - await showInformationMessage(`Workshop code "${code}" is invalid.`, EmptyObject, { modal: true }); - } }); } diff --git a/dbux-code/src/userActions.js b/dbux-code/src/userActions.js index 94480fd5f..8bf78824f 100644 --- a/dbux-code/src/userActions.js +++ b/dbux-code/src/userActions.js @@ -4,6 +4,7 @@ import { newLogger } from '@dbux/common/src/log/logger'; import EmptyObject from '@dbux/common/src/util/EmptyObject'; import allApplications from '@dbux/data/src/applications/allApplications'; import UserActionType from '@dbux/data/src/pathways/UserActionType'; +import { deleteCachedLocRange } from '@dbux/data/src/util/misc'; import DataFlowFilterModeType from './dataFlowView/DataFlowFilterModeType'; import DataFlowSearchModeType from './dataFlowView/DataFlowSearchModeType'; import PackageNodeSortMode from './globalAnalysisView/nodes/PackageNodeSortMode'; @@ -234,8 +235,9 @@ function makeTraceStaticInformation(trace) { const app = allApplications.getById(applicationId); const dp = app.dataProvider; const staticTrace = dp.collections.staticTraces.getById(staticTraceId); - const staticContext = dp.util.getTraceStaticContext(traceId); + const staticContext = { ...dp.util.getTraceStaticContext(traceId) }; const { filePath: file } = dp.collections.staticProgramContexts.getById(staticContext.programId); + deleteCachedLocRange(staticContext.loc); return { applicationId, file, diff --git a/dbux-code/src/workshop/PathwaysDataContainer.js b/dbux-code/src/workshop/PathwaysDataContainer.js index d8f550ac4..8ac427fb4 100644 --- a/dbux-code/src/workshop/PathwaysDataContainer.js +++ b/dbux-code/src/workshop/PathwaysDataContainer.js @@ -2,19 +2,27 @@ import { newLogger } from '@dbux/common/src/log/logger'; import { uploadPathways } from '@dbux/projects/src/firestore/upload'; import PathwaysDataBuffer from './PathwaysDataBuffer'; +/** @typedef {import('./WorkshopSessionController').default} WorkshopSessionController */ + // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('PathwaysDataContainer'); // const Verbose = true; const Verbose = false; -export class PathwaysDataContainer { +export default class PathwaysDataContainer { /** * @type {Object} */ buffers; - constructor() { + /** + * @type {WorkshopSessionController} + */ + workshopSessionController; + + constructor(controller) { + this.workshopSessionController = controller; this.prevListener = null; this.reset(); } @@ -53,7 +61,16 @@ export class PathwaysDataContainer { }); if (this.sessionId) { - await uploadPathways(this.sessionId, 'info', session.serialize()); + const { code, workshopSessionId, nickname } = this.workshopSessionController; + const data = { + code, + workshopSessionId, + nickname, + ...session.serialize() + }; + // remove unused absolute file paths + delete data.logFilePath; + await uploadPathways(this.sessionId, 'info', data); } await flushOldPromise; @@ -74,14 +91,3 @@ export class PathwaysDataContainer { return await Promise.all(Object.values(this.buffers).map((buffer) => buffer.flush())); } } - -/** - * @type {PathwaysDataContainer} - */ -let container; - -export function initPathwaysDataContainer() { - container = new PathwaysDataContainer(); - - return container; -} diff --git a/dbux-code/src/workshop/WorkshopSessionController.js b/dbux-code/src/workshop/WorkshopSessionController.js new file mode 100644 index 000000000..d34482f0f --- /dev/null +++ b/dbux-code/src/workshop/WorkshopSessionController.js @@ -0,0 +1,133 @@ +/** @typedef {import('@dbux/projects/src/ProjectsManager').default} ProjectsManager */ + +import { window } from 'vscode'; +import { v4 as uuidv4 } from 'uuid'; +import NestedError from '@dbux/common/src/NestedError'; +import { newLogger } from '@dbux/common/src/log/logger'; +import EmptyObject from '@dbux/common/src/util/EmptyObject'; +import sleep from '@dbux/common/src/util/sleep'; +import { get as mementoGet, set as mementoSet } from '../memento'; +import { showInformationMessage } from '../codeUtil/codeModals'; +import PathwaysDataContainer from './PathwaysDataContainer'; + +// eslint-disable-next-line no-unused-vars +const { log, debug, warn, error: logError } = newLogger('WorkshopSession'); + +// const Verbose = true; +const Verbose = false; + +const NicknameKeyName = 'dbux.workshopSession.nickname'; +const SessionIdKeyName = 'dbux.workshopSession.workshopSessionId'; + +// const UploadLoopInterval = 2 * 60 * 1000; +const UploadLoopInterval = 10 * 1000; + +export default class WorkshopSessionController { + /** + * @type {string} + */ + code; + + /** + * @type {string} + */ + workshopSessionId; + + /** + * @type {string} + */ + nickname; + + constructor() { + this.ValidCodes = new Set([]); + this.code = ''; + this.workshopSessionId = mementoGet(SessionIdKeyName); + this.nickname = mementoGet(NicknameKeyName); + + // dev only + if (process.env.NODE_ENV === 'development') { + this.ValidCodes.add('1234'); + } + } + + async askForEnable() { + const code = await window.showInputBox({ + ignoreFocusOut: true, + placeHolder: 'Enter Workshop Code' + }); + if (this.isValidCode(code)) { + await this.enableWorkshopSession(code); + return true; + } + else { + await showInformationMessage(`Workshop code "${code}" is invalid.`, EmptyObject, { modal: true }); + return false; + } + } + + /** + * @param {string} code + * @returns {boolean} + */ + isValidCode(code) { + return this.ValidCodes.has(code); + } + + isWorkshopSessionEnabled() { + return !!this.code; + } + + async enableWorkshopSession(code) { + this.code = code; + + if (this.nickname === undefined) { + this.nickname = await window.showInputBox({ + ignoreFocusOut: true, + placeHolder: 'Enter Your Nickname' + }); + await mementoSet(NicknameKeyName, this.nickname); + } + + if (this.workshopSessionId === undefined) { + this.workshopSessionId = uuidv4(); + await mementoSet(SessionIdKeyName, this.workshopSessionId); + } + } + + /** + * @param {ProjectsManager} projectsManager + * @returns + */ + initWorkshopSession(projectsManager) { + if (this.isWorkshopSessionEnabled()) { + this.pathwaysDataContainer = new PathwaysDataContainer(this); + projectsManager.onPracticeSessionStateChanged(this.pathwaysDataContainer.onSessionChanged); + this.scheduleUpload(); + } + } + + async scheduleUpload() { + while (this.isWorkshopSessionEnabled()) { + await sleep(UploadLoopInterval); + try { + await this.pathwaysDataContainer.maybeFlushAll(); + } + catch (err) { + throw new NestedError(`Failed in PathwaysDataContainer upload loop`, err); + } + } + } +} + +/** + * @type {WorkshopSessionController} + */ +let controller; +export function initWorkshopSessionController() { + controller = new WorkshopSessionController(); + return controller; +} + +export function getWorkshopSessionController() { + return controller; +} \ No newline at end of file diff --git a/dbux-code/src/workshop/index.js b/dbux-code/src/workshop/index.js deleted file mode 100644 index 54ee110b2..000000000 --- a/dbux-code/src/workshop/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/** @typedef {import('@dbux/projects/src/ProjectsManager').default} ProjectsManager */ - -import NestedError from '@dbux/common/src/NestedError'; -import { newLogger } from '@dbux/common/src/log/logger'; -import sleep from '@dbux/common/src/util/sleep'; -import { initPathwaysDataContainer } from './PathwaysDataContainer'; - -// eslint-disable-next-line no-unused-vars -const { log, debug, warn, error: logError } = newLogger('WorkshopSession'); - -// const Verbose = true; -const Verbose = false; - -const ValidCodes = new Set([]); -let currentCode = ''; - -if (process.env.NODE_ENV === 'development') { - ValidCodes.add('1234'); -} - -/** - * @param {string} code - * @returns {boolean} - */ -export function isValidCode(code) { - return ValidCodes.has(code); -} - -export function isWorkshopSessionActive() { - return !!currentCode; -} - -export function setupWorkshopSession(code) { - if (isValidCode(code)) { - currentCode = code; - } - else { - throw new Error('Invalid workshop code'); - } -} - -/** ########################################################################### - * pdp listener and log writing - * #########################################################################*/ - -/** - * @param {ProjectsManager} projectsManager - * @returns - */ -export async function initWorkshopSession(projectsManager) { - if (isWorkshopSessionActive()) { - const pathwaysDataContainer = initPathwaysDataContainer(); - projectsManager.onPracticeSessionStateChanged(pathwaysDataContainer.onSessionChanged); - scheduleUpload(pathwaysDataContainer); - } -} - -const UploadLoopInterval = 2 * 60 * 1000; -// const UploadLoopInterval = 10 * 1000; - -async function scheduleUpload(pathwaysDataContainer) { - while (isWorkshopSessionActive()) { - await sleep(UploadLoopInterval); - try { - await pathwaysDataContainer.maybeFlushAll(); - } - catch (err) { - throw new NestedError(`Failed in PathwaysDataContainer upload loop`, err); - } - } -} diff --git a/dbux-projects/src/firestore/upload.js b/dbux-projects/src/firestore/upload.js index 6f6df2997..f44352f83 100644 --- a/dbux-projects/src/firestore/upload.js +++ b/dbux-projects/src/firestore/upload.js @@ -26,8 +26,12 @@ export async function uploadPathways(sessionId, collectionName, entries) { } export async function upload(url, data) { - const serializedData = serialize(data); - const dataString = JSON.stringify({ fields: serializedData }); + // `firestore-rest-serdes` produce redundant structure when serialize + // const serializedData = serialize(data); + // const dataString = JSON.stringify({ fields: serializedData }); + + const serializedData = JSON.stringify(data); + const dataString = JSON.stringify({ fields: { serializedData: { stringValue: serializedData } } }); const options = { method: 'POST', @@ -47,7 +51,7 @@ export async function upload(url, data) { const resString = Buffer.concat(body).toString(); if (res.statusCode < 200 || res.statusCode > 299) { const err = JSON.parse(resString)?.error; - reject(new Error(`HTTP status code ${res.statusCode}\n ${JSON.stringify(err, null, 2)}`)); + reject(new Error(`HTTP status code ${res.statusCode}\n ${err}`)); } else { resolve(resString); diff --git a/dbux-projects/src/userActions/index.js b/dbux-projects/src/userActions/index.js index a94afe1db..469b4e6c2 100644 --- a/dbux-projects/src/userActions/index.js +++ b/dbux-projects/src/userActions/index.js @@ -9,8 +9,8 @@ import { newLogger } from '@dbux/common/src/log/logger'; // eslint-disable-next-line no-unused-vars const { log, debug, warn, error: logError } = newLogger('UserActions'); -const Verbose = true; -// const Verbose = false; +// const Verbose = true; +const Verbose = false; // ########################################################################### // event registry