@mongoosejs/studio 0.5.14 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +69 -2
- package/README.md +24 -0
- package/backend/actions/ChatMessage/executeScript.js +14 -6
- package/backend/actions/ChatThread/createChatMessage.js +7 -4
- package/backend/actions/ChatThread/getChatThread.js +7 -4
- package/backend/actions/ChatThread/listChatThreads.js +5 -2
- package/backend/actions/ChatThread/shareChatThread.js +5 -2
- package/backend/actions/ChatThread/streamChatMessage.js +7 -4
- package/backend/actions/ChatThread/toggleAgentMode.js +5 -2
- package/backend/actions/Dashboard/createDashboard.js +3 -0
- package/backend/actions/Dashboard/deleteDashboard.js +9 -3
- package/backend/actions/Dashboard/getDashboard.js +23 -11
- package/backend/actions/Dashboard/getDashboards.js +8 -2
- package/backend/actions/Dashboard/updateDashboard.js +8 -2
- package/backend/actions/Model/addField.js +8 -2
- package/backend/actions/Model/analyzeSchema.js +15 -5
- package/backend/actions/Model/createChatMessage.js +3 -0
- package/backend/actions/Model/createDocument.js +3 -0
- package/backend/actions/Model/deleteDocument.js +8 -2
- package/backend/actions/Model/deleteDocuments.js +8 -2
- package/backend/actions/Model/dropCollection.js +3 -0
- package/backend/actions/Model/dropIndex.js +3 -0
- package/backend/actions/Model/executeDocumentScript.js +14 -3
- package/backend/actions/Model/exportQueryResults.js +12 -2
- package/backend/actions/Model/getCollectionInfo.js +14 -4
- package/backend/actions/Model/getDocument.js +12 -2
- package/backend/actions/Model/getDocuments.js +13 -3
- package/backend/actions/Model/getDocumentsStream.js +13 -3
- package/backend/actions/Model/getEstimatedDocumentCounts.js +12 -2
- package/backend/actions/Model/getIndexes.js +3 -0
- package/backend/actions/Model/getSuggestedProjection.js +3 -0
- package/backend/actions/Model/listModels.js +3 -0
- package/backend/actions/Model/streamChatMessage.js +3 -0
- package/backend/actions/Model/streamDocumentChanges.js +3 -0
- package/backend/actions/Model/updateDocument.js +8 -2
- package/backend/actions/Model/updateDocuments.js +8 -2
- package/backend/actions/Model/validateDocument.js +12 -2
- package/backend/actions/Script/createScript.js +36 -0
- package/backend/actions/Script/executeScript.js +89 -0
- package/backend/actions/Script/getScript.js +43 -0
- package/backend/actions/Script/index.js +5 -0
- package/backend/actions/Task/cancelTask.js +5 -2
- package/backend/actions/Task/getTaskOverview.js +6 -3
- package/backend/actions/Task/getTasks.js +6 -3
- package/backend/actions/Task/getTasksOverTime.js +6 -3
- package/backend/actions/Task/rescheduleTask.js +5 -2
- package/backend/actions/Task/runTask.js +5 -2
- package/backend/actions/index.js +1 -0
- package/backend/authorize.js +6 -1
- package/backend/db/scriptSchema.js +29 -0
- package/backend/helpers/operationOptions.js +26 -0
- package/backend/helpers/readOperationOptions.js +31 -0
- package/backend/index.js +2 -0
- package/backend/integrations/mothership.js +28 -0
- package/backend/mcp.js +140 -0
- package/backend/netlify.js +5 -3
- package/backend/next.js +5 -3
- package/backend/sandbox/createSandbox.js +2 -2
- package/backend/sandbox/createScriptDb.js +40 -23
- package/backend/util/archetypeToZodSchema.js +74 -0
- package/docs/mcp-oauth.md +106 -0
- package/eslint.config.js +1 -0
- package/eval/agent/run.js +1 -1
- package/express.js +183 -41
- package/frontend/index.js +3 -0
- package/frontend/public/app.js +441 -48
- package/frontend/public/tw.css +8 -0
- package/frontend/src/api.js +22 -0
- package/frontend/src/index.js +9 -1
- package/frontend/src/mcp/mcp.html +55 -0
- package/frontend/src/mcp/mcp.js +27 -0
- package/frontend/src/navbar/navbar.html +8 -0
- package/frontend/src/navbar/navbar.js +3 -0
- package/frontend/src/routes.js +16 -0
- package/frontend/src/script/script.html +105 -0
- package/frontend/src/script/script.js +98 -0
- package/index.d.ts +10 -1
- package/nest.d.ts +3 -0
- package/package.json +5 -3
- package/vercel.d.ts +2 -0
- package/CLAUDE.md +0 -7
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const operationOptions = require('../../helpers/operationOptions');
|
|
6
6
|
|
|
7
7
|
const DeleteDocumentParams = new Archetype({
|
|
8
8
|
model: {
|
|
@@ -15,6 +15,9 @@ const DeleteDocumentParams = new Archetype({
|
|
|
15
15
|
},
|
|
16
16
|
roles: {
|
|
17
17
|
$type: ['string']
|
|
18
|
+
},
|
|
19
|
+
maxTimeMS: {
|
|
20
|
+
$type: 'number'
|
|
18
21
|
}
|
|
19
22
|
}).compile('DeleteDocumentParams');
|
|
20
23
|
|
|
@@ -31,8 +34,11 @@ module.exports = ({ db, options }) => async function DeleteDocument(params) {
|
|
|
31
34
|
|
|
32
35
|
const doc = await Model.
|
|
33
36
|
deleteOne({ _id: documentId }).
|
|
34
|
-
setOptions(
|
|
37
|
+
setOptions(operationOptions(options, params, { sanitizeFilter: true })).
|
|
35
38
|
orFail();
|
|
36
39
|
|
|
37
40
|
return { doc };
|
|
38
41
|
};
|
|
42
|
+
|
|
43
|
+
module.exports.paramsType = DeleteDocumentParams;
|
|
44
|
+
module.exports.tags = ['destructive'];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const operationOptions = require('../../helpers/operationOptions');
|
|
6
6
|
|
|
7
7
|
const DeleteDocumentsParams = new Archetype({
|
|
8
8
|
model: {
|
|
@@ -15,6 +15,9 @@ const DeleteDocumentsParams = new Archetype({
|
|
|
15
15
|
},
|
|
16
16
|
roles: {
|
|
17
17
|
$type: ['string']
|
|
18
|
+
},
|
|
19
|
+
maxTimeMS: {
|
|
20
|
+
$type: 'number'
|
|
18
21
|
}
|
|
19
22
|
}).compile('DeleteDocumentsParams');
|
|
20
23
|
|
|
@@ -31,8 +34,11 @@ module.exports = ({ db, options }) => async function DeleteDocuments(params) {
|
|
|
31
34
|
|
|
32
35
|
await Model.
|
|
33
36
|
deleteMany({ _id: { $in: documentIds } }).
|
|
34
|
-
setOptions(
|
|
37
|
+
setOptions(operationOptions(options, params)).
|
|
35
38
|
orFail();
|
|
36
39
|
|
|
37
40
|
return { };
|
|
38
41
|
};
|
|
42
|
+
|
|
43
|
+
module.exports.paramsType = DeleteDocumentsParams;
|
|
44
|
+
module.exports.tags = ['destructive'];
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const createSandbox = require('../../sandbox/createSandbox');
|
|
6
|
-
const
|
|
6
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
7
|
+
const operationOptions = require('../../helpers/operationOptions');
|
|
7
8
|
|
|
8
9
|
const ExecuteDocumentScriptParams = new Archetype({
|
|
9
10
|
model: {
|
|
@@ -20,6 +21,13 @@ const ExecuteDocumentScriptParams = new Archetype({
|
|
|
20
21
|
},
|
|
21
22
|
roles: {
|
|
22
23
|
$type: ['string']
|
|
24
|
+
},
|
|
25
|
+
maxTimeMS: {
|
|
26
|
+
$type: 'number'
|
|
27
|
+
},
|
|
28
|
+
readPreference: {
|
|
29
|
+
$type: 'string',
|
|
30
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
23
31
|
}
|
|
24
32
|
}).compile('ExecuteDocumentScriptParams');
|
|
25
33
|
|
|
@@ -32,12 +40,12 @@ module.exports = ({ db, options }) => async function executeDocumentScript(param
|
|
|
32
40
|
throw new Error(`Model ${model} not found`);
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
const sandbox = createSandbox(
|
|
43
|
+
const sandbox = createSandbox(db, { ...readOperationOptions(options, params) });
|
|
36
44
|
|
|
37
45
|
try {
|
|
38
46
|
const Model = sandbox.db.models[model];
|
|
39
47
|
const doc = await Model.findById(documentId).
|
|
40
|
-
setOptions(
|
|
48
|
+
setOptions(operationOptions(options, params, { sanitizeFilter: true })).
|
|
41
49
|
orFail();
|
|
42
50
|
sandbox.context.doc = doc;
|
|
43
51
|
|
|
@@ -55,3 +63,6 @@ module.exports = ({ db, options }) => async function executeDocumentScript(param
|
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
};
|
|
66
|
+
|
|
67
|
+
module.exports.paramsType = ExecuteDocumentScriptParams;
|
|
68
|
+
module.exports.tags = [];
|
|
@@ -5,7 +5,7 @@ const mongoose = require('mongoose');
|
|
|
5
5
|
const { stringify } = require('csv-stringify/sync');
|
|
6
6
|
const authorize = require('../../authorize');
|
|
7
7
|
const evaluateFilter = require('../../helpers/evaluateFilter');
|
|
8
|
-
const
|
|
8
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
9
9
|
|
|
10
10
|
const GetDocumentsParams = new Archetype({
|
|
11
11
|
model: {
|
|
@@ -27,6 +27,13 @@ const GetDocumentsParams = new Archetype({
|
|
|
27
27
|
},
|
|
28
28
|
roles: {
|
|
29
29
|
$type: ['string']
|
|
30
|
+
},
|
|
31
|
+
maxTimeMS: {
|
|
32
|
+
$type: 'number'
|
|
33
|
+
},
|
|
34
|
+
readPreference: {
|
|
35
|
+
$type: 'string',
|
|
36
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
30
37
|
}
|
|
31
38
|
}).compile('GetDocumentsParams');
|
|
32
39
|
|
|
@@ -46,7 +53,7 @@ module.exports = ({ db, options }) => async function exportQueryResults(params,
|
|
|
46
53
|
|
|
47
54
|
const docs = await Model.
|
|
48
55
|
find(filter).
|
|
49
|
-
setOptions(
|
|
56
|
+
setOptions(readOperationOptions(options, params)).
|
|
50
57
|
sort({ _id: -1 });
|
|
51
58
|
|
|
52
59
|
const rows = [propertiesToInclude];
|
|
@@ -68,3 +75,6 @@ module.exports = ({ db, options }) => async function exportQueryResults(params,
|
|
|
68
75
|
res.setHeader('Content-Disposition', `attachment; filename="${model.toLowerCase()}-export.csv"`);
|
|
69
76
|
return csv;
|
|
70
77
|
};
|
|
78
|
+
|
|
79
|
+
module.exports.paramsType = GetDocumentsParams;
|
|
80
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
6
6
|
|
|
7
7
|
const GetCollectionInfoParams = new Archetype({
|
|
8
8
|
model: {
|
|
@@ -11,6 +11,13 @@ const GetCollectionInfoParams = new Archetype({
|
|
|
11
11
|
},
|
|
12
12
|
roles: {
|
|
13
13
|
$type: ['string']
|
|
14
|
+
},
|
|
15
|
+
maxTimeMS: {
|
|
16
|
+
$type: 'number'
|
|
17
|
+
},
|
|
18
|
+
readPreference: {
|
|
19
|
+
$type: 'string',
|
|
20
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
14
21
|
}
|
|
15
22
|
}).compile('GetCollectionInfoParams');
|
|
16
23
|
|
|
@@ -24,9 +31,9 @@ module.exports = ({ db, options }) => async function getCollectionInfo(params) {
|
|
|
24
31
|
throw new Error(`Model ${model} not found`);
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
const
|
|
34
|
+
const dbOptions = readOperationOptions(options, params);
|
|
28
35
|
const [collectionOptions, stats] = await Promise.all([
|
|
29
|
-
Model.collection.options(
|
|
36
|
+
Model.collection.options(dbOptions),
|
|
30
37
|
Model.aggregate([
|
|
31
38
|
{
|
|
32
39
|
$collStats: {
|
|
@@ -34,7 +41,7 @@ module.exports = ({ db, options }) => async function getCollectionInfo(params) {
|
|
|
34
41
|
count: {}
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
|
-
]).option(
|
|
44
|
+
]).option(dbOptions).then(res => res[0] ?? {})
|
|
38
45
|
]);
|
|
39
46
|
|
|
40
47
|
return {
|
|
@@ -49,3 +56,6 @@ module.exports = ({ db, options }) => async function getCollectionInfo(params) {
|
|
|
49
56
|
}
|
|
50
57
|
};
|
|
51
58
|
};
|
|
59
|
+
|
|
60
|
+
module.exports.paramsType = GetCollectionInfoParams;
|
|
61
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -4,7 +4,7 @@ const Archetype = require('archetype');
|
|
|
4
4
|
const getRefFromSchemaType = require('../../helpers/getRefFromSchemaType');
|
|
5
5
|
const removeSpecifiedPaths = require('../../helpers/removeSpecifiedPaths');
|
|
6
6
|
const authorize = require('../../authorize');
|
|
7
|
-
const
|
|
7
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
8
8
|
|
|
9
9
|
const GetDocumentParams = new Archetype({
|
|
10
10
|
model: {
|
|
@@ -17,6 +17,13 @@ const GetDocumentParams = new Archetype({
|
|
|
17
17
|
},
|
|
18
18
|
roles: {
|
|
19
19
|
$type: ['string']
|
|
20
|
+
},
|
|
21
|
+
maxTimeMS: {
|
|
22
|
+
$type: 'number'
|
|
23
|
+
},
|
|
24
|
+
readPreference: {
|
|
25
|
+
$type: 'string',
|
|
26
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
20
27
|
}
|
|
21
28
|
}).compile('GetDocumentParams');
|
|
22
29
|
|
|
@@ -32,7 +39,7 @@ module.exports = ({ db, options }) => async function getDocument(params) {
|
|
|
32
39
|
|
|
33
40
|
const doc = await Model.
|
|
34
41
|
findById(documentId).
|
|
35
|
-
setOptions(
|
|
42
|
+
setOptions(readOperationOptions(options, { sanitizeFilter: true })).
|
|
36
43
|
orFail();
|
|
37
44
|
const schemaPaths = {};
|
|
38
45
|
for (const path of Object.keys(Model.schema.paths)) {
|
|
@@ -50,3 +57,6 @@ module.exports = ({ db, options }) => async function getDocument(params) {
|
|
|
50
57
|
|
|
51
58
|
return { doc: doc.toJSON({ virtuals: true, getters: false, transform: false }), schemaPaths, virtualPaths };
|
|
52
59
|
};
|
|
60
|
+
|
|
61
|
+
module.exports.paramsType = GetDocumentParams;
|
|
62
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -7,7 +7,7 @@ const getRefFromSchemaType = require('../../helpers/getRefFromSchemaType');
|
|
|
7
7
|
const getSuggestedProjection = require('../../helpers/getSuggestedProjection');
|
|
8
8
|
const parseProjectionParam = require('../../helpers/parseProjectionParam');
|
|
9
9
|
const authorize = require('../../authorize');
|
|
10
|
-
const
|
|
10
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
11
11
|
|
|
12
12
|
const GetDocumentsParams = new Archetype({
|
|
13
13
|
model: {
|
|
@@ -38,6 +38,13 @@ const GetDocumentsParams = new Archetype({
|
|
|
38
38
|
},
|
|
39
39
|
roles: {
|
|
40
40
|
$type: ['string']
|
|
41
|
+
},
|
|
42
|
+
maxTimeMS: {
|
|
43
|
+
$type: 'number'
|
|
44
|
+
},
|
|
45
|
+
readPreference: {
|
|
46
|
+
$type: 'string',
|
|
47
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
41
48
|
}
|
|
42
49
|
}).compile('GetDocumentsParams');
|
|
43
50
|
|
|
@@ -76,7 +83,7 @@ module.exports = ({ db, options }) => async function getDocuments(params) {
|
|
|
76
83
|
if (projection != null) {
|
|
77
84
|
query = query.select(projection);
|
|
78
85
|
}
|
|
79
|
-
query.setOptions(
|
|
86
|
+
query.setOptions(readOperationOptions(options, params));
|
|
80
87
|
const cursor = await query.cursor();
|
|
81
88
|
const docs = [];
|
|
82
89
|
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
|
|
@@ -114,7 +121,7 @@ module.exports = ({ db, options }) => async function getDocuments(params) {
|
|
|
114
121
|
let numDocuments = null;
|
|
115
122
|
let numDocsError = null;
|
|
116
123
|
try {
|
|
117
|
-
numDocuments = await countQuery.setOptions(
|
|
124
|
+
numDocuments = await countQuery.setOptions(readOperationOptions(options, params));
|
|
118
125
|
} catch (err) {
|
|
119
126
|
numDocsError = err?.message;
|
|
120
127
|
}
|
|
@@ -129,3 +136,6 @@ module.exports = ({ db, options }) => async function getDocuments(params) {
|
|
|
129
136
|
numDocsError
|
|
130
137
|
};
|
|
131
138
|
};
|
|
139
|
+
|
|
140
|
+
module.exports.paramsType = GetDocumentsParams;
|
|
141
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -7,7 +7,7 @@ const getRefFromSchemaType = require('../../helpers/getRefFromSchemaType');
|
|
|
7
7
|
const getSuggestedProjection = require('../../helpers/getSuggestedProjection');
|
|
8
8
|
const parseProjectionParam = require('../../helpers/parseProjectionParam');
|
|
9
9
|
const authorize = require('../../authorize');
|
|
10
|
-
const
|
|
10
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
11
11
|
|
|
12
12
|
const GetDocumentsParams = new Archetype({
|
|
13
13
|
model: {
|
|
@@ -38,6 +38,13 @@ const GetDocumentsParams = new Archetype({
|
|
|
38
38
|
},
|
|
39
39
|
roles: {
|
|
40
40
|
$type: ['string']
|
|
41
|
+
},
|
|
42
|
+
maxTimeMS: {
|
|
43
|
+
$type: 'number'
|
|
44
|
+
},
|
|
45
|
+
readPreference: {
|
|
46
|
+
$type: 'string',
|
|
47
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
41
48
|
}
|
|
42
49
|
}).compile('GetDocumentsParams');
|
|
43
50
|
|
|
@@ -76,7 +83,7 @@ module.exports = ({ db, options }) => async function* getDocumentsStream(params)
|
|
|
76
83
|
if (projection != null) {
|
|
77
84
|
query = query.select(projection);
|
|
78
85
|
}
|
|
79
|
-
query.setOptions(
|
|
86
|
+
query.setOptions(readOperationOptions(options, params));
|
|
80
87
|
|
|
81
88
|
const schemaPaths = {};
|
|
82
89
|
for (const path of Object.keys(Model.schema.paths)) {
|
|
@@ -112,7 +119,7 @@ module.exports = ({ db, options }) => async function* getDocumentsStream(params)
|
|
|
112
119
|
? Model.estimatedDocumentCount()
|
|
113
120
|
: Model.countDocuments(filter);
|
|
114
121
|
const numDocsPromise = countQuery.
|
|
115
|
-
setOptions(
|
|
122
|
+
setOptions(readOperationOptions(options, params)).
|
|
116
123
|
exec();
|
|
117
124
|
|
|
118
125
|
let numDocsYielded = false;
|
|
@@ -159,3 +166,6 @@ module.exports = ({ db, options }) => async function* getDocumentsStream(params)
|
|
|
159
166
|
await cursor.close();
|
|
160
167
|
}
|
|
161
168
|
};
|
|
169
|
+
|
|
170
|
+
module.exports.paramsType = GetDocumentsParams;
|
|
171
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
6
6
|
|
|
7
7
|
const GetEstimatedDocumentCountsParams = new Archetype({
|
|
8
8
|
roles: {
|
|
9
9
|
$type: ['string']
|
|
10
|
+
},
|
|
11
|
+
maxTimeMS: {
|
|
12
|
+
$type: 'number'
|
|
13
|
+
},
|
|
14
|
+
readPreference: {
|
|
15
|
+
$type: 'string',
|
|
16
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
10
17
|
}
|
|
11
18
|
}).compile('GetEstimatedDocumentCountsParams');
|
|
12
19
|
|
|
@@ -22,7 +29,7 @@ module.exports = ({ db, options }) => async function getEstimatedDocumentCounts(
|
|
|
22
29
|
modelNames.map(name => {
|
|
23
30
|
const Model = db.models[name];
|
|
24
31
|
return Model.estimatedDocumentCount().
|
|
25
|
-
setOptions(
|
|
32
|
+
setOptions(readOperationOptions(options, params)).
|
|
26
33
|
exec();
|
|
27
34
|
})
|
|
28
35
|
);
|
|
@@ -39,3 +46,6 @@ module.exports = ({ db, options }) => async function getEstimatedDocumentCounts(
|
|
|
39
46
|
|
|
40
47
|
return { counts };
|
|
41
48
|
};
|
|
49
|
+
|
|
50
|
+
module.exports.paramsType = GetEstimatedDocumentCountsParams;
|
|
51
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const operationOptions = require('../../helpers/operationOptions');
|
|
6
6
|
|
|
7
7
|
const UpdateDocumentsParams = new Archetype({
|
|
8
8
|
model: {
|
|
@@ -22,6 +22,9 @@ const UpdateDocumentsParams = new Archetype({
|
|
|
22
22
|
},
|
|
23
23
|
roles: {
|
|
24
24
|
$type: ['string']
|
|
25
|
+
},
|
|
26
|
+
maxTimeMS: {
|
|
27
|
+
$type: 'number'
|
|
25
28
|
}
|
|
26
29
|
}).compile('UpdateDocumentsParams');
|
|
27
30
|
|
|
@@ -60,6 +63,9 @@ module.exports = ({ db, options }) => async function updateDocument(params) {
|
|
|
60
63
|
|
|
61
64
|
const doc = await Model.
|
|
62
65
|
findByIdAndUpdate(_id, updateOperation, { sanitizeFilter: true, returnDocument: 'after', overwriteImmutable: true, runValidators: false }).
|
|
63
|
-
setOptions(
|
|
66
|
+
setOptions(operationOptions(options, params));
|
|
64
67
|
return { doc };
|
|
65
68
|
};
|
|
69
|
+
|
|
70
|
+
module.exports.paramsType = UpdateDocumentsParams;
|
|
71
|
+
module.exports.tags = [];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const operationOptions = require('../../helpers/operationOptions');
|
|
6
6
|
|
|
7
7
|
const UpdateDocumentsParams = new Archetype({
|
|
8
8
|
model: {
|
|
@@ -19,6 +19,9 @@ const UpdateDocumentsParams = new Archetype({
|
|
|
19
19
|
},
|
|
20
20
|
roles: {
|
|
21
21
|
$type: ['string']
|
|
22
|
+
},
|
|
23
|
+
maxTimeMS: {
|
|
24
|
+
$type: 'number'
|
|
22
25
|
}
|
|
23
26
|
}).compile('UpdateDocumentsParams');
|
|
24
27
|
|
|
@@ -62,7 +65,10 @@ module.exports = ({ db, options }) => async function updateDocuments(params) {
|
|
|
62
65
|
|
|
63
66
|
const result = await Model.
|
|
64
67
|
updateMany({ _id: { $in: _id } }, updateOperation, { overwriteImmutable: true, runValidators: false }).
|
|
65
|
-
setOptions(
|
|
68
|
+
setOptions(operationOptions(options, params));
|
|
66
69
|
|
|
67
70
|
return { result };
|
|
68
71
|
};
|
|
72
|
+
|
|
73
|
+
module.exports.paramsType = UpdateDocumentsParams;
|
|
74
|
+
module.exports.tags = [];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
5
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
6
6
|
const validateDocumentWithTimeout = require('../../helpers/validateDocumentWithTimeout');
|
|
7
7
|
|
|
8
8
|
const ValidateDocumentParams = new Archetype({
|
|
@@ -16,6 +16,13 @@ const ValidateDocumentParams = new Archetype({
|
|
|
16
16
|
},
|
|
17
17
|
roles: {
|
|
18
18
|
$type: ['string']
|
|
19
|
+
},
|
|
20
|
+
maxTimeMS: {
|
|
21
|
+
$type: 'number'
|
|
22
|
+
},
|
|
23
|
+
readPreference: {
|
|
24
|
+
$type: 'string',
|
|
25
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
19
26
|
}
|
|
20
27
|
}).compile('ValidateDocumentParams');
|
|
21
28
|
|
|
@@ -30,9 +37,12 @@ module.exports = ({ db, options }) => async function validateDocument(params) {
|
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
const doc = await Model.findById(documentId).
|
|
33
|
-
setOptions(
|
|
40
|
+
setOptions(readOperationOptions(options, { sanitizeFilter: true })).
|
|
34
41
|
orFail();
|
|
35
42
|
return {
|
|
36
43
|
result: await validateDocumentWithTimeout(doc)
|
|
37
44
|
};
|
|
38
45
|
};
|
|
46
|
+
|
|
47
|
+
module.exports.paramsType = ValidateDocumentParams;
|
|
48
|
+
module.exports.tags = ['readOnly'];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const mongoose = require('mongoose');
|
|
5
|
+
|
|
6
|
+
const authorize = require('../../authorize');
|
|
7
|
+
|
|
8
|
+
const CreateScriptParams = new Archetype({
|
|
9
|
+
initiatedById: {
|
|
10
|
+
$type: mongoose.Types.ObjectId
|
|
11
|
+
},
|
|
12
|
+
script: {
|
|
13
|
+
$type: 'string',
|
|
14
|
+
$required: true
|
|
15
|
+
},
|
|
16
|
+
roles: {
|
|
17
|
+
$type: ['string']
|
|
18
|
+
}
|
|
19
|
+
}).compile('CreateScriptParams');
|
|
20
|
+
|
|
21
|
+
module.exports = ({ studioConnection, options }) => async function createScript(params) {
|
|
22
|
+
const { initiatedById, script, roles } = new CreateScriptParams(params);
|
|
23
|
+
|
|
24
|
+
await authorize('Script.createScript', roles);
|
|
25
|
+
|
|
26
|
+
const Script = studioConnection.model('__Studio_Script');
|
|
27
|
+
const createdScript = await Script.create({ script, userId: initiatedById });
|
|
28
|
+
const baseUrl = options?.workspace?.baseUrl?.replace(/\/+$/, '');
|
|
29
|
+
const url = `${baseUrl ? `${baseUrl}/` : ''}#/script/${createdScript._id}`;
|
|
30
|
+
|
|
31
|
+
return { script: createdScript, url };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports.paramsType = CreateScriptParams;
|
|
35
|
+
module.exports.tags = [];
|
|
36
|
+
module.exports.description = 'Create a script for the user to review, edit, dry run, or run in Mongoose Studio. This tool does not execute the script and returns a review URL.';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const mongoose = require('mongoose');
|
|
5
|
+
|
|
6
|
+
const authorize = require('../../authorize');
|
|
7
|
+
const readOperationOptions = require('../../helpers/readOperationOptions');
|
|
8
|
+
const createSandbox = require('../../sandbox/createSandbox');
|
|
9
|
+
|
|
10
|
+
const ExecuteScriptParams = new Archetype({
|
|
11
|
+
initiatedById: {
|
|
12
|
+
$type: mongoose.Types.ObjectId
|
|
13
|
+
},
|
|
14
|
+
scriptId: {
|
|
15
|
+
$type: mongoose.Types.ObjectId,
|
|
16
|
+
$required: true
|
|
17
|
+
},
|
|
18
|
+
script: {
|
|
19
|
+
$type: 'string'
|
|
20
|
+
},
|
|
21
|
+
dryRun: {
|
|
22
|
+
$type: 'boolean',
|
|
23
|
+
$default: false
|
|
24
|
+
},
|
|
25
|
+
roles: {
|
|
26
|
+
$type: ['string']
|
|
27
|
+
},
|
|
28
|
+
maxTimeMS: {
|
|
29
|
+
$type: 'number'
|
|
30
|
+
},
|
|
31
|
+
readPreference: {
|
|
32
|
+
$type: 'string',
|
|
33
|
+
$enum: ['secondary', 'secondaryPreferred', 'primary']
|
|
34
|
+
}
|
|
35
|
+
}).compile('ExecuteScriptParams');
|
|
36
|
+
|
|
37
|
+
module.exports = ({ db, studioConnection, options }) => async function executeScript(params) {
|
|
38
|
+
const { initiatedById, scriptId, script, dryRun, roles } = new ExecuteScriptParams(params);
|
|
39
|
+
|
|
40
|
+
await authorize('Script.executeScript', roles);
|
|
41
|
+
|
|
42
|
+
const Script = studioConnection.model('__Studio_Script');
|
|
43
|
+
const scriptDoc = await Script.findById(scriptId);
|
|
44
|
+
if (scriptDoc == null) {
|
|
45
|
+
throw new Error('Script not found');
|
|
46
|
+
}
|
|
47
|
+
if (initiatedById && scriptDoc.userId?.toString() !== initiatedById.toString()) {
|
|
48
|
+
throw new Error('Unauthorized');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let output = null;
|
|
52
|
+
let error = null;
|
|
53
|
+
|
|
54
|
+
if (script != null) {
|
|
55
|
+
scriptDoc.script = script;
|
|
56
|
+
}
|
|
57
|
+
scriptDoc.dryRun = dryRun;
|
|
58
|
+
scriptDoc.executionStartedAt = new Date();
|
|
59
|
+
scriptDoc.executionFinishedAt = null;
|
|
60
|
+
scriptDoc.executionResult = null;
|
|
61
|
+
await scriptDoc.save();
|
|
62
|
+
|
|
63
|
+
const sandbox = createSandbox(db, { ...readOperationOptions(options, params) });
|
|
64
|
+
try {
|
|
65
|
+
output = await sandbox.runScript({ script: scriptDoc.script, dryRun });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
error = err;
|
|
68
|
+
} finally {
|
|
69
|
+
try {
|
|
70
|
+
await sandbox.close();
|
|
71
|
+
} catch (_) {
|
|
72
|
+
// Ignore sandbox cleanup errors so they do not mask the primary result.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
scriptDoc.executionFinishedAt = new Date();
|
|
77
|
+
scriptDoc.executionResult = { output, logs: sandbox.getLogs(), error: error?.message ?? null };
|
|
78
|
+
await scriptDoc.save();
|
|
79
|
+
|
|
80
|
+
if (error != null) {
|
|
81
|
+
throw new Error(`Script execution failed: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { script: scriptDoc };
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
module.exports.paramsType = ExecuteScriptParams;
|
|
88
|
+
module.exports.tags = [];
|
|
89
|
+
module.exports.mcp = false;
|