UNPKG

8.36 kBMarkdownView Raw
1# startupjs dm-sharedb-server
2> Express.js server with ShareDB, configs system, and react-router support for rendering client apps.
3
4## Usage
5
6```javascript
7import startupjsServer from '@startupjs/server'
8
9startupjsServer({ getHead }, ee => {
10 ee.on('routes', expressApp => {
11 expressApp.get('/api', async (req, res) => {
12 let { model } = req
13 let $counter = model.at('counters.first')
14 await $counter.subscribe()
15 res.json({ name: 'Test API', counter: $counter.get() })
16 })
17 })
18})
19
20const getHead = appName => `
21 <title>HelloWorld</title>
22 <!-- Put vendor JS and CSS here -->
23`
24```
25
26## @startupjs/sharedb-access connection
27
28### Usage
29Add `accessControl: true` in options of your `startupjsServer`. For example:
30
31```js
32// server/index.js
33startupjsServer(
34{
35 getHead,
36 appRoutes: [
37 ...getMainRoutes()
38 ],
39 accessControl: true
40}, ee => {
41 ee.on('routes', expressApp => {
42 expressApp.get('/api', async (req, res) => {
43 let { model } = req
44 let $counter = model.at('counters.first')
45 await $counter.subscribe()
46 res.json({ name: 'Test API', counter: $counter.get() })
47 })
48 })
49})
50```
51
52Using `@startupjs/sharedb-access` you can control `create`, `read`, `update`, and `delete`
53database operation for every collection. You can define `allow` rules for each CRUD operations
54in your orm model. By default all the operations are denied.
55
56The functions should return `true` if they think the operation should be allowed for
57`allow` rules. Otherwise they should return `false`, or nothing at all (`undefined`).
58
59#### Initialize
60You can describe access rules in the model. Create `static access` object in your orm model.
61Template of `access`:
62
63```js
64static access = {
65 create: async (backend, collection, docId, doc, session) => { your code }
66 read: async (backend, collection, docId, doc, session) => { your code },
67 update: async (backend, collection, docId, oldDoc, session, ops, newDoc) => { your code },
68 delete: async (backend, collection, docId, doc, session) => { your code }
69}
70```
71You can describe only those fields that are necessary. But keep in mind that without describing
72the permission rule for the operation, it is considered prohibited by default.
73
74#### Create
75```js
76// Allow create-operation for collection 'items'
77
78// docId - id of your doc for access-control
79// doc - document object
80// session - your connect session
81class ItemModel {
82 static access = {
83 create: async (backend, collection, docId, doc, session) => {
84 return true
85 }
86 }
87}
88
89// For example, let only admins can create docs in 'items' collection
90// access will be:
91
92class ItemModel {
93 static access = {
94 create: async (backend, collection, docId, doc, session) => {
95 return session.isAdmin
96 }
97 }
98}
99```
100#### Read
101
102Interface is like `create`-operation
103
104```js
105class ItemModel {
106 static access = {
107 // Only if the reader is owner of the doc
108 read: async (backend, collection, docId, doc, session) => {
109 return doc.ownerId === session.userId
110 }
111 }
112}
113```
114
115#### Delete
116
117Interface is like `create`-operation
118
119```js
120class ItemModel {
121 static access = {
122 // Only owners can delete docs, but nobody can delete doc with special typ
123 delete: async (backend, collection, docId, doc, session) => {
124 return doc.ownerId === session.userId && doc.type !== 'liveForever'
125 }
126 }
127}
128```
129
130#### Update
131
132```js
133// docId - id of your doc for access-control
134// oldDoc - document object (before update)
135// newDoc - document object (after update)
136// ops - array of OT operations
137// session - your connect session
138
139const allowUpdateAll = async (backend, collection, docId, oldDoc, session, ops, newDoc) => {
140 return true
141}
142
143class ItemModel {
144 static access = {
145 update: allowUpdateAll
146 }
147}
148```
149
150#### Allow Create, Read, Update, Delete
151```js
152class ItemModel {
153 static access = {
154 create: async (backend, collection, docId, doc, session) => {
155 return true
156 },
157 read: async (backend, collection, docId, doc, session) => {
158 return true
159 },
160 update: async (backend, collection, docId, oldDoc, session, ops, newDoc) => {
161 return true
162 },
163 delete: async (backend, collection, docId, doc, session) => {
164 return true
165 }
166 }
167}
168```
169
170
171## @startupjs/sharedb-schema connection
172
173## Usage
174
1751. In `server/index.js` add `validateSchema: true` to `startupjsServer()` options
1762. Go to one of your ORM document entities (for example, `UserModel`, which targets `users.*`) and add a static method `schema`:
177
178```js
179import { BaseModel } from 'startupjs/orm'
180
181export default class UserModel extends BaseModel {
182 static schema = {
183 nickname: {
184 type: 'string',
185 minLength: 1,
186 maxLength: 10,
187 },
188 email: {
189 type: 'string',
190 format: 'email',
191 },
192 age: {
193 description: 'Age in years',
194 type: 'integer',
195 minimum: 0,
196 },
197 roleId: {
198 type: 'string'
199 },
200 hobbies: {
201 type: 'array',
202 maxItems: 3,
203 items: {
204 type: 'string',
205 },
206 uniqueItems: true,
207 },
208 }
209}
210
211```
212
213
214## @startupjs/sharedb-aggregate connection
215
216### Usage
217Add `serverAggregate: true` in options of your `startupjsServer`. For example:
218
219```js
220// server/index.js
221startupjsServer(
222{
223 getHead,
224 appRoutes: [
225 ...getMainRoutes()
226 ],
227 serverAggregate: true
228}, ee => {
229 // your code
230})
231```
232After connecting the library, all requests with the `$aggregate` parameter will be **blocked**. In order to execute a query with aggregation,
233it must be declared in the `static aggregations` of model for which the aggregation will be performed. For example:
234
235```js
236import { BaseModel } from 'startupjs/orm'
237
238export default class EventsModel extends BaseModel {
239 static aggregations = {
240 openEvents: async (params, shareRequest) => {
241 return [
242 {$match: {status: 'open'}}
243 ]
244 }
245
246 }
247}
248
249```
250
251Here we have created an aggregation for the `events` collection named `openEvents`. Here `params` an object with parameters specified when calling a query;
252`shareRequest` is the standard sharedb request [context object](https://github.com/share/sharedb#middlewares).
253
254## Using queries (on the client):
255
256**Only** aggregations defined in the model can be called on the client. The call is made by name and has the following form:
257
258```js
259model.query('events', {
260 $aggregationName: 'openEvents',
261 $params: {
262 // your params
263 }
264 })
265```
266
267Or yo can use hook `useQuery`:
268```js
269const [openEvents] = useQuery('events', {
270 $aggregationName: 'openEvents',
271 $params: {
272 // your params
273 }
274 })
275```
276
277## how params works
278If you call such a query:
279
280```js
281model.query('events', {
282 $aggregationName: 'openEvents',
283 $params: {
284 title: 'Test Event'
285 }
286})
287```
288
289then `params` in `openEvents` will contain:
290
291```js
292{
293 title: 'Test Event'
294}
295```
296
297This way you can customize your aggregations in difinition. For example you can want to get parameters for `$match`:
298
299```js
300import { BaseModel } from 'startupjs/orm'
301
302export default class EventsModel extends BaseModel {
303 static aggregations = {
304 matchByParams: async (params, shareRequest) => {
305 return [
306 {$match: params}
307 ]
308 }
309 }
310}
311```
312
313Now you need to send necessary parameter in `$params`:
314
315```js
316model.query('events', {
317 $aggregationName: 'matchByParams',
318 $params: {
319 title: 'Custom Params Name',
320 status: 'close'
321 }
322})
323```
324
325## IMPORTANT! Using With Permissions
326
327You can use this component with permission library for checking user roles and permissions. For it you need to add object with field `customCheck` in `serverAggregate`. `customCheck` is a function for additional checking. You can read how it works in [server-aggregate documentation](https://github.com/startupjs/startupjs/tree/master/packages/server-aggregate). We have special function for it in permissions library.
328
329```js
330import { checkAggregationPermission } from '@dmapper/permissions/access'
331
332startupjsServer({
333 getHead,
334 appRoutes: [
335 ...getMainRoutes()
336 ],
337 accessControl: true,
338 serverAggregate: {
339 customCheck: checkAggregationPermission
340 }
341```
342
343Now in our orm will be checked the permissions to perform the aggregation. See documentation on `@dmapper/permissions` library in `core` (private for dmapper) to find out how to allow users to perform aggregations.
344
345## MIT Licence
346
347Copyright (c) 2016 Pavel Zhukov