@mongoosejs/studio 0.0.82 → 0.0.84
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/astra.js +159 -0
- package/backend/actions/ChatMessage/executeScript.js +2 -2
- package/backend/actions/ChatThread/createChatMessage.js +3 -3
- package/backend/actions/ChatThread/createChatThread.js +2 -2
- package/backend/actions/ChatThread/getChatThread.js +4 -4
- package/backend/actions/ChatThread/listChatThreads.js +2 -2
- package/backend/actions/Model/deleteDocuments.js +38 -0
- package/backend/actions/Model/getDocuments.js +2 -2
- package/backend/actions/Model/index.js +2 -0
- package/backend/actions/Model/updateDocuments.js +46 -0
- package/backend/index.js +6 -7
- package/express.js +1 -1
- package/frontend/public/app.js +6592 -6344
- package/frontend/public/tw.css +62 -0
- package/frontend/src/api.js +12 -0
- package/frontend/src/chat/chat-message-script/chat-message-script.html +12 -6
- package/frontend/src/chat/chat-message-script/chat-message-script.js +11 -0
- package/frontend/src/models/models.html +55 -4
- package/frontend/src/models/models.js +52 -0
- package/frontend/src/update-document/update-document.css +0 -0
- package/frontend/src/update-document/update-document.html +25 -0
- package/frontend/src/update-document/update-document.js +70 -0
- package/package.json +1 -1
package/astra.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const mongoose = require('mongoose');
|
|
4
|
+
const { createAstraUri, driver, Vectorize } = require('@datastax/astra-mongoose');
|
|
5
|
+
const express = require('express');
|
|
6
|
+
const studio = require('./express');
|
|
7
|
+
|
|
8
|
+
const app = express();
|
|
9
|
+
|
|
10
|
+
const m = new mongoose.Mongoose();
|
|
11
|
+
m.setDriver(driver);
|
|
12
|
+
m.set('autoCreate', false);
|
|
13
|
+
m.set('autoIndex', false);
|
|
14
|
+
|
|
15
|
+
(function () {
|
|
16
|
+
const schema = new m.Schema({
|
|
17
|
+
email: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
unique: true
|
|
21
|
+
},
|
|
22
|
+
firstName: {
|
|
23
|
+
type: String,
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
lastName: {
|
|
27
|
+
type: String,
|
|
28
|
+
required: true
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
schema.virtual('displayName').get(function() {
|
|
33
|
+
return this.firstName + ' ' + this.lastName.slice(0, 1) + '.';
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
m.model('User', schema);
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
(function () {
|
|
40
|
+
const schema = new m.Schema({
|
|
41
|
+
make: {
|
|
42
|
+
type: String,
|
|
43
|
+
required: true
|
|
44
|
+
},
|
|
45
|
+
model: {
|
|
46
|
+
type: String,
|
|
47
|
+
required: true
|
|
48
|
+
},
|
|
49
|
+
year: {
|
|
50
|
+
type: Number,
|
|
51
|
+
required: true,
|
|
52
|
+
validate: (v) => Number.isInteger(v) && v >= 1950
|
|
53
|
+
},
|
|
54
|
+
images: {
|
|
55
|
+
type: [String]
|
|
56
|
+
},
|
|
57
|
+
numReviews: {
|
|
58
|
+
type: Number,
|
|
59
|
+
required: true,
|
|
60
|
+
default: 0
|
|
61
|
+
},
|
|
62
|
+
averageReview: {
|
|
63
|
+
type: Number,
|
|
64
|
+
required: true,
|
|
65
|
+
default: 0
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
m.model('Vehicle', schema);
|
|
70
|
+
})();
|
|
71
|
+
|
|
72
|
+
(function () {
|
|
73
|
+
const schema = new m.Schema({
|
|
74
|
+
rating: {
|
|
75
|
+
type: Number,
|
|
76
|
+
required: true,
|
|
77
|
+
validate: (v) => Number.isInteger(v)
|
|
78
|
+
},
|
|
79
|
+
text: {
|
|
80
|
+
type: String,
|
|
81
|
+
required: true,
|
|
82
|
+
validate: (v) => v.length >= 30
|
|
83
|
+
},
|
|
84
|
+
userId: {
|
|
85
|
+
type: 'ObjectId',
|
|
86
|
+
required: true
|
|
87
|
+
},
|
|
88
|
+
vehicleId: {
|
|
89
|
+
type: 'ObjectId',
|
|
90
|
+
required: true
|
|
91
|
+
}
|
|
92
|
+
}, { timestamps: true });
|
|
93
|
+
|
|
94
|
+
schema.virtual('user', {
|
|
95
|
+
ref: 'User',
|
|
96
|
+
localField: 'userId',
|
|
97
|
+
foreignField: '_id',
|
|
98
|
+
justOne: true
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
schema.virtual('vehicle', {
|
|
102
|
+
ref: 'Vehicle',
|
|
103
|
+
localField: 'vehicleId',
|
|
104
|
+
foreignField: '_id',
|
|
105
|
+
justOne: true
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
schema.pre('save', async function updateVehicleRating() {
|
|
109
|
+
if (!this.isNew) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const vehicle = await mongoose.model('Vehicle').findById(this.vehicleId).orFail();
|
|
113
|
+
vehicle.numReviews += 1;
|
|
114
|
+
const vehicleReviews = await mongoose.model('Review').find({ vehicleId: this.vehicleId });
|
|
115
|
+
const reviewRatings = vehicleReviews.map((entry) => entry.rating);
|
|
116
|
+
reviewRatings.push(this.rating);
|
|
117
|
+
const average = calculateAverage(reviewRatings);
|
|
118
|
+
vehicle.averageReview = average;
|
|
119
|
+
await vehicle.save();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
function calculateAverage(ratings) {
|
|
123
|
+
if (ratings.length === 0) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
let sum = 0;
|
|
127
|
+
for (let i = 0; i < ratings.length; i++) {
|
|
128
|
+
sum += ratings[i];
|
|
129
|
+
}
|
|
130
|
+
const average = sum / ratings.length;
|
|
131
|
+
return average;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
m.model('Review', schema);
|
|
135
|
+
})();
|
|
136
|
+
|
|
137
|
+
const uri = createAstraUri(
|
|
138
|
+
'https://fbced748-d6ed-40b1-ab9d-b618015a2c2c-us-east-2.apps.astra.datastax.com',
|
|
139
|
+
'AstraCS:kjSUArrrkLKrDlrMpUQOUYlw:50c9eda620df1099ad2dd77eb36e7755daaa48494acb158ff6de8df3deee40f6',
|
|
140
|
+
'reviews_dev2'
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
void async function main() {
|
|
144
|
+
await m.connect(uri);
|
|
145
|
+
const studioConnection = await mongoose.createConnection('mongodb://127.0.0.1:27017/mongoose_studio_test').asPromise();
|
|
146
|
+
|
|
147
|
+
app.use('/studio', await studio('/studio/api', m, { __build: true, apiKey: '123456789', studioConnection }));
|
|
148
|
+
|
|
149
|
+
await app.listen(7770);
|
|
150
|
+
console.log('Listening on port 7770');
|
|
151
|
+
}();
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
# Astra Notes
|
|
155
|
+
|
|
156
|
+
1. Must use collections. Tables don't support `countDocuments()` or `estimatedDocumentCount()`.
|
|
157
|
+
2. Annoying issue: collections don't let you store keys that start with '$'
|
|
158
|
+
|
|
159
|
+
*/
|
|
@@ -16,9 +16,9 @@ const ExecuteScriptParams = new Archetype({
|
|
|
16
16
|
}
|
|
17
17
|
}).compile('ExecuteScriptParams');
|
|
18
18
|
|
|
19
|
-
module.exports = ({ db }) => async function executeScript(params) {
|
|
19
|
+
module.exports = ({ db, studioConnection }) => async function executeScript(params) {
|
|
20
20
|
const { userId, chatMessageId, script } = new ExecuteScriptParams(params);
|
|
21
|
-
const ChatMessage =
|
|
21
|
+
const ChatMessage = studioConnection.model('__Studio_ChatMessage');
|
|
22
22
|
|
|
23
23
|
const chatMessage = await ChatMessage.findById(chatMessageId);
|
|
24
24
|
if (!chatMessage) {
|
|
@@ -55,10 +55,10 @@ return { numUsers: users.length };
|
|
|
55
55
|
Here is a description of the user's models. Assume these are the only models available in the system unless explicitly instructed otherwise by the user.
|
|
56
56
|
`.trim();
|
|
57
57
|
|
|
58
|
-
module.exports = ({ db }) => async function createChatMessage(params) {
|
|
58
|
+
module.exports = ({ db, studioConnection }) => async function createChatMessage(params) {
|
|
59
59
|
const { chatThreadId, userId, content, script, authorization } = new CreateChatMessageParams(params);
|
|
60
|
-
const ChatThread =
|
|
61
|
-
const ChatMessage =
|
|
60
|
+
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
61
|
+
const ChatMessage = studioConnection.model('__Studio_ChatMessage');
|
|
62
62
|
|
|
63
63
|
// Check that the user owns the thread
|
|
64
64
|
const chatThread = await ChatThread.findOne({ _id: chatThreadId });
|
|
@@ -9,9 +9,9 @@ const CreateChatThreadParams = new Archetype({
|
|
|
9
9
|
}
|
|
10
10
|
}).compile('CreateChatThreadParams');
|
|
11
11
|
|
|
12
|
-
module.exports = ({
|
|
12
|
+
module.exports = ({ studioConnection }) => async function createChatThread(params) {
|
|
13
13
|
const { userId } = new CreateChatThreadParams(params);
|
|
14
|
-
const ChatThread =
|
|
14
|
+
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
15
15
|
|
|
16
16
|
const chatThread = await ChatThread.create({ userId });
|
|
17
17
|
|
|
@@ -12,17 +12,17 @@ const GetChatThreadParams = new Archetype({
|
|
|
12
12
|
}
|
|
13
13
|
}).compile('GetChatThreadParams');
|
|
14
14
|
|
|
15
|
-
module.exports = ({ db }) => async function getChatThread(params) {
|
|
15
|
+
module.exports = ({ db, studioConnection }) => async function getChatThread(params) {
|
|
16
16
|
const { chatThreadId, userId } = new GetChatThreadParams(params);
|
|
17
|
-
const ChatThread =
|
|
18
|
-
const ChatMessage =
|
|
17
|
+
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
18
|
+
const ChatMessage = studioConnection.model('__Studio_ChatMessage');
|
|
19
19
|
|
|
20
20
|
const chatThread = await ChatThread.findById(chatThreadId);
|
|
21
21
|
|
|
22
22
|
if (!chatThread) {
|
|
23
23
|
throw new Error('Chat thread not found');
|
|
24
24
|
}
|
|
25
|
-
if (userId && chatThread.userId
|
|
25
|
+
if (userId && chatThread.userId?.toString() !== userId.toString()) {
|
|
26
26
|
throw new Error('Not authorized');
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -9,10 +9,10 @@ const ListChatThreadsParams = new Archetype({
|
|
|
9
9
|
}
|
|
10
10
|
}).compile('ListChatThreadsParams');
|
|
11
11
|
|
|
12
|
-
module.exports = ({ db }) => async function listChatThreads(params) {
|
|
12
|
+
module.exports = ({ db, studioConnection }) => async function listChatThreads(params) {
|
|
13
13
|
// Just validate the params object, but no actual parameters needed
|
|
14
14
|
const { userId } = new ListChatThreadsParams(params);
|
|
15
|
-
const ChatThread =
|
|
15
|
+
const ChatThread = studioConnection.model('__Studio_ChatThread');
|
|
16
16
|
|
|
17
17
|
// Get all chat threads
|
|
18
18
|
const chatThreads = await ChatThread.find(userId ? { userId } : {})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
|
|
5
|
+
const DeleteDocumentsParams = new Archetype({
|
|
6
|
+
model: {
|
|
7
|
+
$type: 'string',
|
|
8
|
+
$required: true
|
|
9
|
+
},
|
|
10
|
+
documentIds: {
|
|
11
|
+
$type: 'string',
|
|
12
|
+
$required: true
|
|
13
|
+
},
|
|
14
|
+
roles: {
|
|
15
|
+
$type: ['string'],
|
|
16
|
+
}
|
|
17
|
+
}).compile('DeleteDocumentsParams');
|
|
18
|
+
|
|
19
|
+
module.exports = ({ db }) => async function DeleteDocuments(params) {
|
|
20
|
+
const { model, documentIds, roles } = new DeleteDocumentsParams(params);
|
|
21
|
+
|
|
22
|
+
const Model = db.models[model];
|
|
23
|
+
|
|
24
|
+
if (roles && roles.includes('readonly')) {
|
|
25
|
+
throw new Error('Not authorized');
|
|
26
|
+
}
|
|
27
|
+
if (Model == null) {
|
|
28
|
+
throw new Error(`Model ${model} not found`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await Model.
|
|
32
|
+
deleteMany({_id: { $in: documentIds }}).
|
|
33
|
+
setOptions({ sanitizeFilter: true }).
|
|
34
|
+
orFail();
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
return { };
|
|
38
|
+
};
|
|
@@ -65,10 +65,10 @@ module.exports = ({ db }) => async function getDocuments(params) {
|
|
|
65
65
|
const numDocuments = filter == null ?
|
|
66
66
|
await Model.estimatedDocumentCount() :
|
|
67
67
|
await Model.countDocuments(filter);
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
return {
|
|
70
70
|
docs: docs.map(doc => doc.toJSON({ virtuals: false, getters: false, transform: false })),
|
|
71
71
|
schemaPaths,
|
|
72
72
|
numDocs: numDocuments
|
|
73
73
|
};
|
|
74
|
-
};
|
|
74
|
+
};
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
exports.createDocument = require('./createDocument')
|
|
4
4
|
exports.deleteDocument = require('./deleteDocument');
|
|
5
|
+
exports.deleteDocuments = require('./deleteDocuments');
|
|
5
6
|
exports.exportQueryResults = require('./exportQueryResults');
|
|
6
7
|
exports.getDocument = require('./getDocument');
|
|
7
8
|
exports.getDocuments = require('./getDocuments');
|
|
8
9
|
exports.getIndexes = require('./getIndexes');
|
|
9
10
|
exports.listModels = require('./listModels');
|
|
10
11
|
exports.updateDocument = require('./updateDocument');
|
|
12
|
+
exports.updateDocuments = require('./updateDocuments');
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const mongoose = require('mongoose');
|
|
5
|
+
|
|
6
|
+
const UpdateDocumentsParams = new Archetype({
|
|
7
|
+
model: {
|
|
8
|
+
$type: 'string',
|
|
9
|
+
$required: true
|
|
10
|
+
},
|
|
11
|
+
_id: {
|
|
12
|
+
$type: ['string'],
|
|
13
|
+
$required: true
|
|
14
|
+
},
|
|
15
|
+
update: {
|
|
16
|
+
$type: Object,
|
|
17
|
+
$required: true
|
|
18
|
+
},
|
|
19
|
+
roles: {
|
|
20
|
+
$type: ['string'],
|
|
21
|
+
}
|
|
22
|
+
}).compile('UpdateDocumentsParams');
|
|
23
|
+
|
|
24
|
+
module.exports = ({ db }) => async function updateDocuments(params) {
|
|
25
|
+
const { model, _id, update, roles } = new UpdateDocumentsParams(params);
|
|
26
|
+
|
|
27
|
+
if (roles && roles.includes('readonly')) {
|
|
28
|
+
throw new Error('Not authorized');
|
|
29
|
+
}
|
|
30
|
+
const Model = db.models[model];
|
|
31
|
+
if (Model == null) {
|
|
32
|
+
throw new Error(`Model ${model} not found`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let processedUpdate = update;
|
|
36
|
+
if (Object.keys(update).length > 0) {
|
|
37
|
+
processedUpdate = Object.fromEntries(
|
|
38
|
+
Object.entries(update).map(([key, value]) => [key, value === 'null' ? null : value === 'undefined' ? undefined : value])
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = await Model.
|
|
43
|
+
updateMany({ _id: { $in: _id } }, processedUpdate, { overwriteImmutable: true, runValidators: false });
|
|
44
|
+
|
|
45
|
+
return { result };
|
|
46
|
+
};
|
package/backend/index.js
CHANGED
|
@@ -8,15 +8,14 @@ const chatMessageSchema = require('./db/chatMessageSchema');
|
|
|
8
8
|
const chatThreadSchema = require('./db/chatThreadSchema');
|
|
9
9
|
const dashboardSchema = require('./db/dashboardSchema');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
module.exports = function backend(db) {
|
|
11
|
+
module.exports = function backend(db, studioConnection) {
|
|
14
12
|
db = db || mongoose.connection;
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
14
|
+
studioConnection = studioConnection ?? db;
|
|
15
|
+
const Dashboard = studioConnection.model('__Studio_Dashboard', dashboardSchema, 'studio__dashboards');
|
|
16
|
+
const ChatMessage = studioConnection.model('__Studio_ChatMessage', chatMessageSchema, 'studio__chatMessages');
|
|
17
|
+
const ChatThread = studioConnection.model('__Studio_ChatThread', chatThreadSchema, 'studio__chatThreads');
|
|
19
18
|
|
|
20
|
-
const actions = applySpec(Actions, { db });
|
|
19
|
+
const actions = applySpec(Actions, { db, studioConnection });
|
|
21
20
|
return actions;
|
|
22
21
|
};
|