@mongoosejs/studio 0.5.12 → 0.5.14
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 +7 -0
- package/CLAUDE.md +7 -0
- package/README.md +3 -0
- package/backend/actions/ChatMessage/executeScript.js +7 -5
- package/backend/actions/ChatThread/createChatMessage.js +4 -2
- package/backend/actions/ChatThread/getChatThread.js +5 -2
- package/backend/actions/ChatThread/listChatThreads.js +3 -1
- package/backend/actions/ChatThread/shareChatThread.js +5 -2
- package/backend/actions/ChatThread/streamChatMessage.js +4 -2
- package/backend/actions/ChatThread/toggleAgentMode.js +5 -2
- package/backend/actions/Dashboard/deleteDashboard.js +7 -3
- package/backend/actions/Dashboard/getDashboard.js +21 -10
- package/backend/actions/Dashboard/getDashboards.js +3 -1
- package/backend/actions/Dashboard/updateDashboard.js +3 -2
- package/backend/actions/Model/addField.js +3 -2
- package/backend/actions/Model/analyzeSchema.js +6 -4
- package/backend/actions/Model/deleteDocument.js +3 -3
- package/backend/actions/Model/deleteDocuments.js +3 -1
- package/backend/actions/Model/executeDocumentScript.js +25 -29
- package/backend/actions/Model/exportQueryResults.js +3 -1
- package/backend/actions/Model/getCollectionInfo.js +5 -3
- package/backend/actions/Model/getDocument.js +3 -2
- package/backend/actions/Model/getDocuments.js +15 -5
- package/backend/actions/Model/getDocumentsStream.js +28 -18
- package/backend/actions/Model/getEstimatedDocumentCounts.js +5 -2
- package/backend/actions/Model/updateDocument.js +4 -2
- package/backend/actions/Model/updateDocuments.js +4 -2
- package/backend/actions/Model/validateDocument.js +5 -2
- package/backend/actions/Task/cancelTask.js +6 -3
- package/backend/actions/Task/getTaskOverview.js +3 -2
- package/backend/actions/Task/getTasks.js +3 -2
- package/backend/actions/Task/getTasksOverTime.js +3 -2
- package/backend/actions/Task/rescheduleTask.js +6 -3
- package/backend/actions/Task/runTask.js +6 -3
- package/backend/helpers/omitNullish.js +5 -0
- package/backend/index.js +4 -4
- package/backend/sandbox/createSandbox.js +2 -2
- package/backend/sandbox/createScriptDb.js +42 -19
- package/frontend/public/app.js +3210 -2706
- package/frontend/public/tw.css +68 -0
- package/frontend/src/api.js +5 -1
- package/frontend/src/chat/chat-message/chat-message.js +5 -1
- package/frontend/src/detail-default/detail-default.html +0 -10
- package/frontend/src/detail-default/detail-default.js +29 -1061
- package/frontend/src/document-details/document-property/document-property.html +0 -11
- package/frontend/src/document-details/document-property/document-property.js +6 -14
- package/frontend/src/edit-geojson/edit-geojson.html +17 -0
- package/frontend/src/edit-geojson/edit-geojson.js +1104 -0
- package/frontend/src/models/models.html +29 -8
- package/frontend/src/models/models.js +30 -2
- package/frontend/src/tooltip/tooltip.html +28 -0
- package/frontend/src/tooltip/tooltip.js +94 -0
- package/index.d.ts +1 -1
- package/local.js +3 -2
- package/nest.d.ts +1 -0
- package/package.json +1 -1
- package/seed/connect.js +4 -0
- package/seed/index.js +16 -0
- package/vercel.d.ts +48 -0
- package/vercel.js +3 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Repository guidance
|
|
2
|
+
|
|
3
|
+
The Mongoose connection passed to Studio is owned by the host application.
|
|
4
|
+
Do not change connection-level settings, options, configuration, models, middleware, plugins, collection objects, or methods on the application connection to implement Studio behavior.
|
|
5
|
+
Apply Studio-specific behavior to individual operations whenever possible.
|
|
6
|
+
When isolation requires a derived connection, copy any Mongoose state that `useDb()` shares with its source before changing the derived connection.
|
|
7
|
+
Sandbox wrappers must remain confined to sandbox-owned models and collections.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Repository guidance
|
|
2
|
+
|
|
3
|
+
The Mongoose connection passed to Studio is owned by the host application.
|
|
4
|
+
Do not change connection-level settings, options, configuration, models, middleware, plugins, collection objects, or methods on the application connection to implement Studio behavior.
|
|
5
|
+
Apply Studio-specific behavior to individual operations whenever possible.
|
|
6
|
+
When isolation requires a derived connection, copy any Mongoose state that `useDb()` shares with its source before changing the derived connection.
|
|
7
|
+
Sandbox wrappers must remain confined to sandbox-owned models and collections.
|
package/README.md
CHANGED
|
@@ -39,6 +39,9 @@ opts.model = 'gpt-4o-mini';
|
|
|
39
39
|
opts.openAIAPIKey = process.env.OPENAI_API_KEY;
|
|
40
40
|
opts.anthropicAPIKey = process.env.ANTHROPIC_API_KEY;
|
|
41
41
|
opts.googleGeminiAPIKey = process.env.GOOGLE_GEMINI_API_KEY;
|
|
42
|
+
// Apply a maximum execution time to all read operations, including reads in scripts.
|
|
43
|
+
// MongoDB does not support maxTimeMS for inserts and index operations.
|
|
44
|
+
opts.maxTimeMS = 10000;
|
|
42
45
|
|
|
43
46
|
// Mount Mongoose Studio on '/studio'
|
|
44
47
|
app.use('/studio', await studio('/studio/api', mongoose, opts));
|
|
@@ -4,6 +4,7 @@ const Archetype = require('archetype');
|
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const createSandbox = require('../../sandbox/createSandbox');
|
|
6
6
|
const mongoose = require('mongoose');
|
|
7
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
7
8
|
|
|
8
9
|
const ExecuteScriptParams = new Archetype({
|
|
9
10
|
initiatedById: {
|
|
@@ -24,24 +25,25 @@ const ExecuteScriptParams = new Archetype({
|
|
|
24
25
|
}
|
|
25
26
|
}).compile('ExecuteScriptParams');
|
|
26
27
|
|
|
27
|
-
module.exports = ({ db, studioConnection }) => async function executeScript(params) {
|
|
28
|
+
module.exports = ({ db, studioConnection, options }) => async function executeScript(params) {
|
|
28
29
|
const { initiatedById, chatMessageId, script, dryRun, roles } = new ExecuteScriptParams(params);
|
|
29
30
|
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
30
31
|
const ChatMessage = studioConnection.model('__Studio_ChatMessage');
|
|
31
32
|
|
|
32
33
|
await authorize('ChatMessage.executeScript', roles);
|
|
33
34
|
|
|
34
|
-
const
|
|
35
|
+
const operationOptions = omitNullish({ maxTimeMS: options?.maxTimeMS });
|
|
36
|
+
const chatMessage = await ChatMessage.findById(chatMessageId).setOptions(operationOptions);
|
|
35
37
|
if (!chatMessage) {
|
|
36
38
|
throw new Error('Chat message not found');
|
|
37
39
|
}
|
|
38
|
-
const chatThread = await ChatThread.findById(chatMessage.chatThreadId).orFail();
|
|
40
|
+
const chatThread = await ChatThread.findById(chatMessage.chatThreadId).setOptions(operationOptions).orFail();
|
|
39
41
|
|
|
40
42
|
if (initiatedById && chatThread.userId?.toString() !== initiatedById.toString()) {
|
|
41
43
|
throw new Error('Unauthorized');
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
const sandbox = createSandbox({ db });
|
|
46
|
+
const sandbox = createSandbox({ db, maxTimeMS: options?.maxTimeMS });
|
|
45
47
|
|
|
46
48
|
let output;
|
|
47
49
|
let error;
|
|
@@ -74,7 +76,7 @@ module.exports = ({ db, studioConnection }) => async function executeScript(para
|
|
|
74
76
|
dryRun: !!dryRun
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
|
-
);
|
|
79
|
+
).setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }));
|
|
78
80
|
|
|
79
81
|
throw new Error(`Script execution failed: ${error}`);
|
|
80
82
|
} finally {
|
|
@@ -8,6 +8,7 @@ const callLLM = require('../../integrations/callLLM');
|
|
|
8
8
|
const getAgentTools = require('../../chatAgent/getAgentTools');
|
|
9
9
|
const getModelDescriptions = require('../../helpers/getModelDescriptions');
|
|
10
10
|
const mongoose = require('mongoose');
|
|
11
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
11
12
|
|
|
12
13
|
const CreateChatMessageParams = new Archetype({
|
|
13
14
|
chatThreadId: {
|
|
@@ -36,7 +37,8 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
|
|
|
36
37
|
await authorize('ChatThread.createChatMessage', roles);
|
|
37
38
|
|
|
38
39
|
// Check that the user owns the thread
|
|
39
|
-
const
|
|
40
|
+
const operationOptions = omitNullish({ maxTimeMS: options?.maxTimeMS });
|
|
41
|
+
const chatThread = await ChatThread.findOne({ _id: chatThreadId }).setOptions(operationOptions);
|
|
40
42
|
if (!chatThread) {
|
|
41
43
|
throw new Error('Chat thread not found');
|
|
42
44
|
}
|
|
@@ -44,7 +46,7 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
|
|
|
44
46
|
throw new Error('Not authorized');
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
const messages = await ChatMessage.find({ chatThreadId }).sort({ createdAt: 1 });
|
|
49
|
+
const messages = await ChatMessage.find({ chatThreadId }).setOptions(operationOptions).sort({ createdAt: 1 });
|
|
48
50
|
const llmMessages = messages.map(m => ({
|
|
49
51
|
role: m.role,
|
|
50
52
|
content: [{
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const mongoose = require('mongoose');
|
|
6
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
6
7
|
|
|
7
8
|
const GetChatThreadParams = new Archetype({
|
|
8
9
|
chatThreadId: {
|
|
@@ -19,14 +20,15 @@ const GetChatThreadParams = new Archetype({
|
|
|
19
20
|
}
|
|
20
21
|
}).compile('GetChatThreadParams');
|
|
21
22
|
|
|
22
|
-
module.exports = ({ db, studioConnection }) => async function getChatThread(params) {
|
|
23
|
+
module.exports = ({ db, studioConnection, options }) => async function getChatThread(params) {
|
|
23
24
|
const { chatThreadId, initiatedById, roles, $workspaceId } = new GetChatThreadParams(params);
|
|
24
25
|
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
25
26
|
const ChatMessage = studioConnection.model('__Studio_ChatMessage');
|
|
26
27
|
|
|
27
28
|
await authorize('ChatThread.getChatThread', roles);
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
+
const operationOptions = omitNullish({ maxTimeMS: options?.maxTimeMS });
|
|
31
|
+
const chatThread = await ChatThread.findById(chatThreadId).setOptions(operationOptions);
|
|
30
32
|
|
|
31
33
|
if (!chatThread) {
|
|
32
34
|
throw new Error('Chat thread not found');
|
|
@@ -39,6 +41,7 @@ module.exports = ({ db, studioConnection }) => async function getChatThread(para
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
const chatMessages = await ChatMessage.find({ chatThreadId })
|
|
44
|
+
.setOptions(operationOptions)
|
|
42
45
|
.sort({ createdAt: -1 });
|
|
43
46
|
|
|
44
47
|
return {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const mongoose = require('mongoose');
|
|
6
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
6
7
|
|
|
7
8
|
const ListChatThreadsParams = new Archetype({
|
|
8
9
|
initiatedById: {
|
|
@@ -13,7 +14,7 @@ const ListChatThreadsParams = new Archetype({
|
|
|
13
14
|
}
|
|
14
15
|
}).compile('ListChatThreadsParams');
|
|
15
16
|
|
|
16
|
-
module.exports = ({ db, studioConnection }) => async function listChatThreads(params) {
|
|
17
|
+
module.exports = ({ db, studioConnection, options }) => async function listChatThreads(params) {
|
|
17
18
|
// Validate the params object
|
|
18
19
|
const { initiatedById, roles } = new ListChatThreadsParams(params);
|
|
19
20
|
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
@@ -24,6 +25,7 @@ module.exports = ({ db, studioConnection }) => async function listChatThreads(pa
|
|
|
24
25
|
|
|
25
26
|
// Get all chat threads
|
|
26
27
|
const chatThreads = await ChatThread.find(query)
|
|
28
|
+
.setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }))
|
|
27
29
|
.sort({ updatedAt: -1 }); // Sort by most recently updated
|
|
28
30
|
|
|
29
31
|
return {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const mongoose = require('mongoose');
|
|
6
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
6
7
|
|
|
7
8
|
const ShareChatThreadParams = new Archetype({
|
|
8
9
|
chatThreadId: {
|
|
@@ -22,13 +23,15 @@ const ShareChatThreadParams = new Archetype({
|
|
|
22
23
|
}
|
|
23
24
|
}).compile('ShareChatThreadParams');
|
|
24
25
|
|
|
25
|
-
module.exports = ({ studioConnection }) => async function shareChatThread(params) {
|
|
26
|
+
module.exports = ({ studioConnection, options }) => async function shareChatThread(params) {
|
|
26
27
|
const { chatThreadId, share, initiatedById, roles, $workspaceId } = new ShareChatThreadParams(params);
|
|
27
28
|
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
28
29
|
|
|
29
30
|
await authorize('ChatThread.shareChatThread', roles);
|
|
30
31
|
|
|
31
|
-
const chatThread = await ChatThread.findById(chatThreadId).
|
|
32
|
+
const chatThread = await ChatThread.findById(chatThreadId).
|
|
33
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })).
|
|
34
|
+
orFail();
|
|
32
35
|
if (initiatedById != null && chatThread.userId?.toString() !== initiatedById.toString()) {
|
|
33
36
|
throw new Error('Not authorized');
|
|
34
37
|
}
|
|
@@ -8,6 +8,7 @@ const runChatAgent = require('../../chatAgent/runChatAgent');
|
|
|
8
8
|
const streamLLM = require('../../integrations/streamLLM');
|
|
9
9
|
const getModelDescriptions = require('../../helpers/getModelDescriptions');
|
|
10
10
|
const mongoose = require('mongoose');
|
|
11
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
11
12
|
|
|
12
13
|
const CreateChatMessageParams = new Archetype({
|
|
13
14
|
chatThreadId: {
|
|
@@ -36,7 +37,8 @@ module.exports = ({ db, studioConnection, options }) => async function* streamCh
|
|
|
36
37
|
await authorize('ChatThread.createChatMessage', roles);
|
|
37
38
|
|
|
38
39
|
// Check that the user owns the thread
|
|
39
|
-
const
|
|
40
|
+
const operationOptions = omitNullish({ maxTimeMS: options?.maxTimeMS });
|
|
41
|
+
const chatThread = await ChatThread.findOne({ _id: chatThreadId }).setOptions(operationOptions);
|
|
40
42
|
if (!chatThread) {
|
|
41
43
|
throw new Error('Chat thread not found');
|
|
42
44
|
}
|
|
@@ -44,7 +46,7 @@ module.exports = ({ db, studioConnection, options }) => async function* streamCh
|
|
|
44
46
|
throw new Error('Not authorized');
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
const messages = await ChatMessage.find({ chatThreadId }).sort({ createdAt: 1 });
|
|
49
|
+
const messages = await ChatMessage.find({ chatThreadId }).setOptions(operationOptions).sort({ createdAt: 1 });
|
|
48
50
|
const llmMessages = messages.map(m => ({
|
|
49
51
|
role: m.role,
|
|
50
52
|
content: [{
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const mongoose = require('mongoose');
|
|
6
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
6
7
|
|
|
7
8
|
const ToggleAgentModeParams = new Archetype({
|
|
8
9
|
chatThreadId: {
|
|
@@ -19,13 +20,15 @@ const ToggleAgentModeParams = new Archetype({
|
|
|
19
20
|
}
|
|
20
21
|
}).compile('ToggleAgentModeParams');
|
|
21
22
|
|
|
22
|
-
module.exports = ({ studioConnection }) => async function toggleAgentMode(params) {
|
|
23
|
+
module.exports = ({ studioConnection, options }) => async function toggleAgentMode(params) {
|
|
23
24
|
const { chatThreadId, agentMode, initiatedById, roles } = new ToggleAgentModeParams(params);
|
|
24
25
|
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
25
26
|
|
|
26
27
|
await authorize('ChatThread.toggleAgentMode', roles);
|
|
27
28
|
|
|
28
|
-
const chatThread = await ChatThread.findById(chatThreadId).
|
|
29
|
+
const chatThread = await ChatThread.findById(chatThreadId).
|
|
30
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })).
|
|
31
|
+
orFail();
|
|
29
32
|
if (initiatedById != null && chatThread.userId?.toString() !== initiatedById.toString()) {
|
|
30
33
|
throw new Error('Not authorized');
|
|
31
34
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const DeleteDashboardParams = new Archetype({
|
|
7
8
|
dashboardId: {
|
|
@@ -13,14 +14,17 @@ const DeleteDashboardParams = new Archetype({
|
|
|
13
14
|
}
|
|
14
15
|
}).compile('DeleteDashboardParams');
|
|
15
16
|
|
|
16
|
-
module.exports = ({ studioConnection }) => async function deleteDashboard(params) {
|
|
17
|
+
module.exports = ({ studioConnection, options }) => async function deleteDashboard(params) {
|
|
17
18
|
const { dashboardId, roles } = new DeleteDashboardParams(params);
|
|
18
19
|
const Dashboard = studioConnection.model('__Studio_Dashboard');
|
|
19
20
|
const DashboardResult = studioConnection.model('__Studio_DashboardResult');
|
|
20
21
|
|
|
21
22
|
await authorize('Dashboard.deleteDashboard', roles);
|
|
22
23
|
|
|
23
|
-
const result = await Dashboard.deleteOne({ _id: dashboardId }).
|
|
24
|
-
|
|
24
|
+
const result = await Dashboard.deleteOne({ _id: dashboardId }).
|
|
25
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })).
|
|
26
|
+
orFail();
|
|
27
|
+
await DashboardResult.deleteMany({ dashboardId }).
|
|
28
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }));
|
|
25
29
|
return { result };
|
|
26
30
|
};
|
|
@@ -4,6 +4,7 @@ const Archetype = require('archetype');
|
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const createSandbox = require('../../sandbox/createSandbox');
|
|
6
6
|
const mongoose = require('mongoose');
|
|
7
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
7
8
|
const { defaultMothershipURL } = require('../../../constants');
|
|
8
9
|
|
|
9
10
|
const GetDashboardParams = new Archetype({
|
|
@@ -36,10 +37,11 @@ module.exports = ({ db, studioConnection, options }) => async function getDashbo
|
|
|
36
37
|
|
|
37
38
|
await authorize('Dashboard.getDashboard', roles);
|
|
38
39
|
|
|
39
|
-
const dashboard = await Dashboard.findOne({ _id: dashboardId })
|
|
40
|
+
const dashboard = await Dashboard.findOne({ _id: dashboardId }).
|
|
41
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }));
|
|
40
42
|
if (evaluate) {
|
|
41
43
|
let result = null;
|
|
42
|
-
const sandbox = createSandbox({ db });
|
|
44
|
+
const sandbox = createSandbox({ db, maxTimeMS: options?.maxTimeMS });
|
|
43
45
|
const startExec = startDashboardEvaluate(DashboardResult, dashboardId, $workspaceId, userId);
|
|
44
46
|
startExec.catch(() => {}); // Avoid unhandled promise rejections - we will handle this error later.
|
|
45
47
|
try {
|
|
@@ -56,7 +58,8 @@ module.exports = ({ db, studioConnection, options }) => async function getDashbo
|
|
|
56
58
|
dashboardResult._id,
|
|
57
59
|
null,
|
|
58
60
|
{ message: error.message },
|
|
59
|
-
'failed'
|
|
61
|
+
'failed',
|
|
62
|
+
options
|
|
60
63
|
);
|
|
61
64
|
});
|
|
62
65
|
return { dashboard, dashboardResult, error: { message: error.message } };
|
|
@@ -79,7 +82,8 @@ module.exports = ({ db, studioConnection, options }) => async function getDashbo
|
|
|
79
82
|
dashboardResult._id,
|
|
80
83
|
result,
|
|
81
84
|
undefined,
|
|
82
|
-
'completed'
|
|
85
|
+
'completed',
|
|
86
|
+
options
|
|
83
87
|
);
|
|
84
88
|
});
|
|
85
89
|
|
|
@@ -93,21 +97,25 @@ module.exports = ({ db, studioConnection, options }) => async function getDashbo
|
|
|
93
97
|
dashboardId,
|
|
94
98
|
$workspaceId,
|
|
95
99
|
authorization,
|
|
96
|
-
mothershipUrl
|
|
100
|
+
mothershipUrl,
|
|
101
|
+
options
|
|
97
102
|
);
|
|
98
103
|
return { dashboard, dashboardResults };
|
|
99
104
|
}
|
|
100
105
|
};
|
|
101
106
|
|
|
102
|
-
async function completeDashboardEvaluate(Dashboard, DashboardResult, dashboardResultId, result, error, status) {
|
|
103
|
-
const dashboardResult = await DashboardResult.findById(dashboardResultId).
|
|
107
|
+
async function completeDashboardEvaluate(Dashboard, DashboardResult, dashboardResultId, result, error, status, options) {
|
|
108
|
+
const dashboardResult = await DashboardResult.findById(dashboardResultId).
|
|
109
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })).
|
|
110
|
+
orFail();
|
|
104
111
|
const finishedEvaluatingAt = new Date();
|
|
105
112
|
dashboardResult.finishedEvaluatingAt = finishedEvaluatingAt;
|
|
106
113
|
dashboardResult.result = result;
|
|
107
114
|
dashboardResult.error = error;
|
|
108
115
|
dashboardResult.status = status;
|
|
109
116
|
await dashboardResult.save();
|
|
110
|
-
await Dashboard.updateOne({ _id: dashboardResult.dashboardId }, { $set: { lastEvaluatedAt: finishedEvaluatingAt } })
|
|
117
|
+
await Dashboard.updateOne({ _id: dashboardResult.dashboardId }, { $set: { lastEvaluatedAt: finishedEvaluatingAt } }).
|
|
118
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }));
|
|
111
119
|
return { dashboardResult };
|
|
112
120
|
}
|
|
113
121
|
|
|
@@ -123,14 +131,17 @@ async function startDashboardEvaluate(DashboardResult, dashboardId, workspaceId,
|
|
|
123
131
|
return { dashboardResult };
|
|
124
132
|
}
|
|
125
133
|
|
|
126
|
-
async function getDashboardResults(DashboardResult, dashboardId, workspaceId, authorization, mothershipUrl) {
|
|
134
|
+
async function getDashboardResults(DashboardResult, dashboardId, workspaceId, authorization, mothershipUrl, options) {
|
|
127
135
|
const filter = { dashboardId };
|
|
128
136
|
if (workspaceId != null) {
|
|
129
137
|
filter.workspaceId = workspaceId;
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
const [localResultsRes, remoteResultsRes] = await Promise.allSettled([
|
|
133
|
-
DashboardResult.find(filter).
|
|
141
|
+
DashboardResult.find(filter).
|
|
142
|
+
sort({ _id: -1 }).
|
|
143
|
+
limit(10).
|
|
144
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })),
|
|
134
145
|
getMothershipDashboardResults(dashboardId, workspaceId, authorization, mothershipUrl)
|
|
135
146
|
]);
|
|
136
147
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const GetDashboardParams = new Archetype({
|
|
7
8
|
roles: {
|
|
@@ -9,7 +10,7 @@ const GetDashboardParams = new Archetype({
|
|
|
9
10
|
}
|
|
10
11
|
}).compile('GetDashboardParams');
|
|
11
12
|
|
|
12
|
-
module.exports = ({ studioConnection }) => async function getDashboards(params) {
|
|
13
|
+
module.exports = ({ studioConnection, options }) => async function getDashboards(params) {
|
|
13
14
|
const Dashboard = studioConnection.model('__Studio_Dashboard');
|
|
14
15
|
const { roles } = new GetDashboardParams(params);
|
|
15
16
|
|
|
@@ -17,6 +18,7 @@ module.exports = ({ studioConnection }) => async function getDashboards(params)
|
|
|
17
18
|
|
|
18
19
|
const dashboards = await Dashboard
|
|
19
20
|
.find()
|
|
21
|
+
.setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }))
|
|
20
22
|
.sort({ isPinned: -1, createdAt: -1, _id: -1 });
|
|
21
23
|
|
|
22
24
|
return { dashboards };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const UpdateDashboardParams = new Archetype({
|
|
7
8
|
dashboardId: {
|
|
@@ -25,7 +26,7 @@ const UpdateDashboardParams = new Archetype({
|
|
|
25
26
|
}
|
|
26
27
|
}).compile('UpdateDashboardParams');
|
|
27
28
|
|
|
28
|
-
module.exports = ({ studioConnection }) => async function updateDashboard(params) {
|
|
29
|
+
module.exports = ({ studioConnection, options }) => async function updateDashboard(params) {
|
|
29
30
|
const { dashboardId, code, title, description, isPinned, roles, evaluate } = new UpdateDashboardParams(params);
|
|
30
31
|
|
|
31
32
|
const Dashboard = studioConnection.models['__Studio_Dashboard'];
|
|
@@ -54,7 +55,7 @@ module.exports = ({ studioConnection }) => async function updateDashboard(params
|
|
|
54
55
|
dashboardId,
|
|
55
56
|
updateObj,
|
|
56
57
|
{ sanitizeFilter: true, returnDocument: 'after' }
|
|
57
|
-
);
|
|
58
|
+
).setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }));
|
|
58
59
|
|
|
59
60
|
return { doc };
|
|
60
61
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const AddFieldParams = new Archetype({
|
|
7
8
|
model: {
|
|
@@ -25,7 +26,7 @@ const AddFieldParams = new Archetype({
|
|
|
25
26
|
}
|
|
26
27
|
}).compile('AddFieldParams');
|
|
27
28
|
|
|
28
|
-
module.exports = ({ db }) => async function addField(params) {
|
|
29
|
+
module.exports = ({ db, options }) => async function addField(params) {
|
|
29
30
|
const { model, _id, fieldName, fieldValue, roles } = new AddFieldParams(params);
|
|
30
31
|
|
|
31
32
|
await authorize('Model.updateDocument', roles);
|
|
@@ -48,7 +49,7 @@ module.exports = ({ db }) => async function addField(params) {
|
|
|
48
49
|
runValidators: false,
|
|
49
50
|
strict: false
|
|
50
51
|
}
|
|
51
|
-
);
|
|
52
|
+
).setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS }));
|
|
52
53
|
|
|
53
54
|
return { doc };
|
|
54
55
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
5
|
const { Buffer } = require('buffer');
|
|
6
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
6
7
|
const validateDocumentWithTimeout = require('../../helpers/validateDocumentWithTimeout');
|
|
7
8
|
|
|
8
9
|
const SAMPLE_SIZE = 1000;
|
|
@@ -18,7 +19,7 @@ const AnalyzeSchemaParams = new Archetype({
|
|
|
18
19
|
}
|
|
19
20
|
}).compile('AnalyzeSchemaParams');
|
|
20
21
|
|
|
21
|
-
module.exports = ({ db }) => async function analyzeSchema(params) {
|
|
22
|
+
module.exports = ({ db, options }) => async function analyzeSchema(params) {
|
|
22
23
|
const { model, roles } = new AnalyzeSchemaParams(params);
|
|
23
24
|
|
|
24
25
|
await authorize('Model.analyzeSchema', roles);
|
|
@@ -28,10 +29,11 @@ module.exports = ({ db }) => async function analyzeSchema(params) {
|
|
|
28
29
|
throw new Error(`Model ${model} not found`);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
+
const operationOptions = omitNullish({ maxTimeMS: options?.maxTimeMS });
|
|
33
|
+
const documentCount = await Model.collection.countDocuments({}, operationOptions);
|
|
32
34
|
const rawDocs = documentCount > SAMPLE_SIZE ?
|
|
33
|
-
await Model.collection.aggregate([{ $sample: { size: SAMPLE_SIZE } }]).toArray() :
|
|
34
|
-
await Model.collection.find({}).toArray();
|
|
35
|
+
await Model.collection.aggregate([{ $sample: { size: SAMPLE_SIZE } }], operationOptions).toArray() :
|
|
36
|
+
await Model.collection.find({}, operationOptions).toArray();
|
|
35
37
|
|
|
36
38
|
const paths = getSchemaPaths(Model.schema);
|
|
37
39
|
const pathTypeCounts = {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const DeleteDocumentParams = new Archetype({
|
|
7
8
|
model: {
|
|
@@ -17,7 +18,7 @@ const DeleteDocumentParams = new Archetype({
|
|
|
17
18
|
}
|
|
18
19
|
}).compile('DeleteDocumentParams');
|
|
19
20
|
|
|
20
|
-
module.exports = ({ db }) => async function DeleteDocument(params) {
|
|
21
|
+
module.exports = ({ db, options }) => async function DeleteDocument(params) {
|
|
21
22
|
const { model, documentId, roles } = new DeleteDocumentParams(params);
|
|
22
23
|
|
|
23
24
|
const Model = db.models[model];
|
|
@@ -30,9 +31,8 @@ module.exports = ({ db }) => async function DeleteDocument(params) {
|
|
|
30
31
|
|
|
31
32
|
const doc = await Model.
|
|
32
33
|
deleteOne({ _id: documentId }).
|
|
33
|
-
setOptions({ sanitizeFilter: true }).
|
|
34
|
+
setOptions(omitNullish({ sanitizeFilter: true, maxTimeMS: options?.maxTimeMS })).
|
|
34
35
|
orFail();
|
|
35
|
-
console.log('what is doc', doc);
|
|
36
36
|
|
|
37
37
|
return { doc };
|
|
38
38
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const DeleteDocumentsParams = new Archetype({
|
|
7
8
|
model: {
|
|
@@ -17,7 +18,7 @@ const DeleteDocumentsParams = new Archetype({
|
|
|
17
18
|
}
|
|
18
19
|
}).compile('DeleteDocumentsParams');
|
|
19
20
|
|
|
20
|
-
module.exports = ({ db }) => async function DeleteDocuments(params) {
|
|
21
|
+
module.exports = ({ db, options }) => async function DeleteDocuments(params) {
|
|
21
22
|
const { model, documentIds, roles } = new DeleteDocumentsParams(params);
|
|
22
23
|
|
|
23
24
|
const Model = db.models[model];
|
|
@@ -30,6 +31,7 @@ module.exports = ({ db }) => async function DeleteDocuments(params) {
|
|
|
30
31
|
|
|
31
32
|
await Model.
|
|
32
33
|
deleteMany({ _id: { $in: documentIds } }).
|
|
34
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })).
|
|
33
35
|
orFail();
|
|
34
36
|
|
|
35
37
|
return { };
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const vm = require('vm');
|
|
5
|
+
const createSandbox = require('../../sandbox/createSandbox');
|
|
6
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
8
7
|
|
|
9
8
|
const ExecuteDocumentScriptParams = new Archetype({
|
|
10
9
|
model: {
|
|
@@ -24,38 +23,35 @@ const ExecuteDocumentScriptParams = new Archetype({
|
|
|
24
23
|
}
|
|
25
24
|
}).compile('ExecuteDocumentScriptParams');
|
|
26
25
|
|
|
27
|
-
module.exports = ({ db }) => async function executeDocumentScript(params) {
|
|
26
|
+
module.exports = ({ db, options }) => async function executeDocumentScript(params) {
|
|
28
27
|
const { model, documentId, script, roles } = new ExecuteDocumentScriptParams(params);
|
|
29
28
|
|
|
30
29
|
await authorize('Model.executeDocumentScript', roles);
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
if (Model == null) {
|
|
31
|
+
if (db.models[model] == null) {
|
|
34
32
|
throw new Error(`Model ${model} not found`);
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
const sandbox = createSandbox({ db, maxTimeMS: options?.maxTimeMS });
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const Model = sandbox.db.models[model];
|
|
39
|
+
const doc = await Model.findById(documentId).
|
|
40
|
+
setOptions(omitNullish({ sanitizeFilter: true, maxTimeMS: options?.maxTimeMS })).
|
|
41
|
+
orFail();
|
|
42
|
+
sandbox.context.doc = doc;
|
|
43
|
+
|
|
44
|
+
const result = await sandbox.runScript({ script });
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
result,
|
|
48
|
+
logs: sandbox.getLogs()
|
|
49
|
+
};
|
|
50
|
+
} finally {
|
|
51
|
+
try {
|
|
52
|
+
await sandbox.close();
|
|
53
|
+
} catch (_) {
|
|
54
|
+
// Ignore sandbox cleanup errors so they do not mask the primary result.
|
|
55
|
+
}
|
|
42
56
|
}
|
|
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
57
|
};
|
|
58
|
-
|
|
59
|
-
const wrappedScript = script => `(async () => {
|
|
60
|
-
${script}
|
|
61
|
-
})()`;
|
|
@@ -5,6 +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 omitNullish = require('../../helpers/omitNullish');
|
|
8
9
|
|
|
9
10
|
const GetDocumentsParams = new Archetype({
|
|
10
11
|
model: {
|
|
@@ -29,7 +30,7 @@ const GetDocumentsParams = new Archetype({
|
|
|
29
30
|
}
|
|
30
31
|
}).compile('GetDocumentsParams');
|
|
31
32
|
|
|
32
|
-
module.exports = ({ db }) => async function exportQueryResults(params, req, res) {
|
|
33
|
+
module.exports = ({ db, options }) => async function exportQueryResults(params, req, res) {
|
|
33
34
|
params = new GetDocumentsParams(params);
|
|
34
35
|
const { model, propertiesToInclude, roles, searchText } = params;
|
|
35
36
|
|
|
@@ -45,6 +46,7 @@ module.exports = ({ db }) => async function exportQueryResults(params, req, res)
|
|
|
45
46
|
|
|
46
47
|
const docs = await Model.
|
|
47
48
|
find(filter).
|
|
49
|
+
setOptions(omitNullish({ maxTimeMS: options?.maxTimeMS })).
|
|
48
50
|
sort({ _id: -1 });
|
|
49
51
|
|
|
50
52
|
const rows = [propertiesToInclude];
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const omitNullish = require('../../helpers/omitNullish');
|
|
5
6
|
|
|
6
7
|
const GetCollectionInfoParams = new Archetype({
|
|
7
8
|
model: {
|
|
@@ -13,7 +14,7 @@ const GetCollectionInfoParams = new Archetype({
|
|
|
13
14
|
}
|
|
14
15
|
}).compile('GetCollectionInfoParams');
|
|
15
16
|
|
|
16
|
-
module.exports = ({ db }) => async function getCollectionInfo(params) {
|
|
17
|
+
module.exports = ({ db, options }) => async function getCollectionInfo(params) {
|
|
17
18
|
const { model, roles } = new GetCollectionInfoParams(params);
|
|
18
19
|
|
|
19
20
|
await authorize('Model.getCollectionInfo', roles);
|
|
@@ -23,8 +24,9 @@ module.exports = ({ db }) => async function getCollectionInfo(params) {
|
|
|
23
24
|
throw new Error(`Model ${model} not found`);
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
const operationOptions = omitNullish({ maxTimeMS: options?.maxTimeMS });
|
|
26
28
|
const [collectionOptions, stats] = await Promise.all([
|
|
27
|
-
Model.collection.options(),
|
|
29
|
+
Model.collection.options(operationOptions),
|
|
28
30
|
Model.aggregate([
|
|
29
31
|
{
|
|
30
32
|
$collStats: {
|
|
@@ -32,7 +34,7 @@ module.exports = ({ db }) => async function getCollectionInfo(params) {
|
|
|
32
34
|
count: {}
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
|
-
]).then(res => res[0] ?? {})
|
|
37
|
+
]).option(operationOptions).then(res => res[0] ?? {})
|
|
36
38
|
]);
|
|
37
39
|
|
|
38
40
|
return {
|