@mongoosejs/studio 0.2.10 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const Archetype = require('archetype');
4
+ const authorize = require('../../authorize');
5
+ const mongoose = require('mongoose');
6
+ const util = require('util');
7
+ const vm = require('vm');
8
+
9
+ const ExecuteDocumentScriptParams = new Archetype({
10
+ model: {
11
+ $type: 'string',
12
+ $required: true
13
+ },
14
+ documentId: {
15
+ $type: 'string',
16
+ $required: true
17
+ },
18
+ script: {
19
+ $type: 'string',
20
+ $required: true
21
+ },
22
+ roles: {
23
+ $type: ['string']
24
+ }
25
+ }).compile('ExecuteDocumentScriptParams');
26
+
27
+ module.exports = ({ db }) => async function executeDocumentScript(params) {
28
+ const { model, documentId, script, roles } = new ExecuteDocumentScriptParams(params);
29
+
30
+ await authorize('Model.executeDocumentScript', roles);
31
+
32
+ const Model = db.models[model];
33
+ if (Model == null) {
34
+ throw new Error(`Model ${model} not found`);
35
+ }
36
+
37
+ const doc = await Model.findById(documentId).setOptions({ sanitizeFilter: true }).orFail();
38
+
39
+ const logs = [];
40
+ if (!db.Types) {
41
+ db.Types = mongoose.Types;
42
+ }
43
+ const sandbox = { db, mongoose, doc, console: {}, ObjectId: mongoose.Types.ObjectId };
44
+
45
+ sandbox.console.log = function() {
46
+ const args = Array.from(arguments);
47
+ logs.push(args.map(arg => typeof arg === 'object' ? util.inspect(arg) : arg).join(' '));
48
+ };
49
+
50
+ const context = vm.createContext(sandbox);
51
+ const result = await vm.runInContext(wrappedScript(script), context);
52
+
53
+ return {
54
+ result,
55
+ logs: logs.join('\n')
56
+ };
57
+ };
58
+
59
+ const wrappedScript = script => `(async () => {
60
+ ${script}
61
+ })()`;
@@ -6,6 +6,7 @@ exports.createDocument = require('./createDocument');
6
6
  exports.deleteDocument = require('./deleteDocument');
7
7
  exports.deleteDocuments = require('./deleteDocuments');
8
8
  exports.dropIndex = require('./dropIndex');
9
+ exports.executeDocumentScript = require('./executeDocumentScript');
9
10
  exports.exportQueryResults = require('./exportQueryResults');
10
11
  exports.getDocument = require('./getDocument');
11
12
  exports.getDocuments = require('./getDocuments');
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const Archetype = require('archetype');
4
+ const mongoose = require('mongoose');
5
+
6
+ const CancelTaskParams = new Archetype({
7
+ taskId: {
8
+ $type: mongoose.Types.ObjectId,
9
+ $required: true
10
+ }
11
+ }).compile('CancelTaskParams');
12
+
13
+ module.exports = ({ db }) => async function cancelTask(params) {
14
+ params = new CancelTaskParams(params);
15
+ const { taskId } = params;
16
+ const { Task } = db.models;
17
+
18
+ const task = await Task.findOne({ _id: taskId }).orFail();
19
+
20
+ const cancelledTask = await Task.cancelTask({ _id: taskId });
21
+ return {
22
+ task: cancelledTask
23
+ };
24
+ };
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const Archetype = require('archetype');
4
+
5
+ const CreateTaskParams = new Archetype({
6
+ name: {
7
+ $type: 'string',
8
+ $required: true
9
+ },
10
+ scheduledAt: {
11
+ $type: Date,
12
+ $required: true
13
+ },
14
+ repeatAfterMS: {
15
+ $type: 'number'
16
+ },
17
+ payload: {
18
+ $type: Archetype.Any
19
+ }
20
+ }).compile('CreateTaskParams');
21
+
22
+ module.exports = ({ db }) => async function createTask(params) {
23
+ params = new CreateTaskParams(params);
24
+
25
+ const { name, scheduledAt, payload, repeatAfterMS } = params;
26
+ const { Task } = db.models;
27
+
28
+ const task = await Task.schedule(name, scheduledAt, payload, repeatAfterMS);
29
+
30
+ return {
31
+ task
32
+ };
33
+ };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const Archetype = require('archetype');
4
+
5
+ const GetTasksParams = new Archetype({
6
+ start: {
7
+ $type: Date
8
+ },
9
+ end: {
10
+ $type: Date
11
+ },
12
+ status: {
13
+ $type: 'string'
14
+ },
15
+ name: {
16
+ $type: 'string'
17
+ }
18
+ }).compile('GetTasksParams');
19
+
20
+ module.exports = ({ db }) => async function getTasks(params) {
21
+ params = new GetTasksParams(params);
22
+ const { start, end, status, name } = params;
23
+ const { Task } = db.models;
24
+
25
+ const filter = {};
26
+
27
+ if (start && end) {
28
+ filter.scheduledAt = { $gte: start, $lt: end };
29
+ } else if (start) {
30
+ filter.scheduledAt = { $gte: start };
31
+ }
32
+ if (status) {
33
+ filter.status = status;
34
+ }
35
+ if (name) {
36
+ filter.name = { $regex: name, $options: 'i' };
37
+ }
38
+
39
+ const tasks = await Task.find(filter);
40
+
41
+ // Define all possible statuses
42
+ const allStatuses = ['pending', 'in_progress', 'succeeded', 'failed', 'cancelled', 'unknown'];
43
+
44
+ // Initialize groupedTasks with all statuses
45
+ const groupedTasks = allStatuses.reduce((groups, status) => {
46
+ groups[status] = [];
47
+ return groups;
48
+ }, {});
49
+
50
+ // Group tasks by status
51
+ tasks.forEach(task => {
52
+ const taskStatus = task.status || 'unknown';
53
+ if (groupedTasks.hasOwnProperty(taskStatus)) {
54
+ groupedTasks[taskStatus].push(task);
55
+ }
56
+ });
57
+
58
+ return {
59
+ tasks,
60
+ groupedTasks
61
+ };
62
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ exports.cancelTask = require('./cancelTask');
4
+ exports.createTask = require('./createTask');
5
+ exports.getTasks = require('./getTasks');
6
+ exports.rescheduleTask = require('./rescheduleTask');
7
+ exports.runTask = require('./runTask');
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const Archetype = require('archetype');
4
+ const mongoose = require('mongoose');
5
+
6
+ const RescheduleTaskParams = new Archetype({
7
+ taskId: {
8
+ $type: mongoose.Types.ObjectId,
9
+ $required: true
10
+ },
11
+ scheduledAt: {
12
+ $type: Date,
13
+ $required: true
14
+ }
15
+ }).compile('RescheduleTaskParams');
16
+
17
+ module.exports = ({ db }) => async function rescheduleTask(params) {
18
+ params = new RescheduleTaskParams(params);
19
+ const { taskId, scheduledAt } = params;
20
+ const { Task } = db.models;
21
+
22
+ const task = await Task.findOne({ _id: taskId }).orFail();
23
+
24
+ if (scheduledAt < Date.now()) {
25
+ throw new Error('Cannot reschedule a task for the past');
26
+ }
27
+
28
+ if (task.status != 'pending') {
29
+ throw new Error('Cannot reschedule a task that is not pending');
30
+ }
31
+
32
+ task.scheduledAt = scheduledAt;
33
+
34
+ await task.save();
35
+
36
+ return {
37
+ task
38
+ };
39
+ };
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const Archetype = require('archetype');
4
+ const mongoose = require('mongoose');
5
+
6
+ const RunTaskParams = new Archetype({
7
+ taskId: {
8
+ $type: mongoose.Types.ObjectId,
9
+ $required: true
10
+ }
11
+ }).compile('RunTaskParams');
12
+
13
+ module.exports = ({ db }) => async function runTask(params) {
14
+ params = new RunTaskParams(params);
15
+ const { taskId } = params;
16
+ const { Task } = db.models;
17
+
18
+ const task = await Task.findOne({ _id: taskId }).orFail();
19
+
20
+ const executedTask = await Task.execute(task);
21
+
22
+ return {
23
+ task: executedTask
24
+ };
25
+ };
@@ -6,3 +6,4 @@ exports.Dashboard = require('./Dashboard');
6
6
  exports.Model = require('./Model');
7
7
  exports.Script = require('./Script');
8
8
  exports.status = require('./status');
9
+ exports.Task = require('./Task');
@@ -17,6 +17,7 @@ const actionsToRequiredRoles = {
17
17
  'Model.deleteDocument': ['owner', 'admin', 'member'],
18
18
  'Model.deleteDocuments': ['owner', 'admin', 'member'],
19
19
  'Model.dropIndex': ['owner', 'admin'],
20
+ 'Model.executeDocumentScript': ['owner', 'admin', 'member'],
20
21
  'Model.exportQueryResults': ['owner', 'admin', 'member', 'readonly'],
21
22
  'Model.getDocument': ['owner', 'admin', 'member', 'readonly'],
22
23
  'Model.getDocuments': ['owner', 'admin', 'member', 'readonly'],
package/backend/index.js CHANGED
@@ -18,7 +18,7 @@ module.exports = function backend(db, studioConnection, options) {
18
18
 
19
19
  let changeStream = null;
20
20
  if (options?.changeStream) {
21
- changeStream = db.watch();
21
+ changeStream = db instanceof mongoose.Mongoose ? db.connection.watch() : db.watch();
22
22
  }
23
23
 
24
24
  const actions = applySpec(Actions, { db, studioConnection, options, changeStream });
package/eslint.config.js CHANGED
@@ -37,6 +37,7 @@ module.exports = defineConfig([
37
37
  fetch: true,
38
38
  __dirname: true,
39
39
  process: true,
40
+ clearTimeout: true,
40
41
  setTimeout: true,
41
42
  navigator: true,
42
43
  TextDecoder: true,
package/express.js CHANGED
@@ -81,6 +81,7 @@ module.exports = async function mongooseStudioExpressApp(apiUrl, conn, options)
81
81
  );
82
82
 
83
83
  const { config } = await frontend(apiUrl, false, options, workspace);
84
+ config.enableTaskVisualizer = options.enableTaskVisualizer;
84
85
  router.get('/config.js', function (req, res) {
85
86
  res.setHeader('Content-Type', 'application/javascript');
86
87
  res.end(`window.MONGOOSE_STUDIO_CONFIG = ${JSON.stringify(config, null, 2)};`);