Skip to content

Commit 785b108

Browse files
mpfeilumut0
andauthored
Shared boxes for users (#605)
* shared boxes for users, TODO: function to share box with others * use third test box for grouptag tests Co-authored-by: Umut Tas <[email protected]>
1 parent d9f7642 commit 785b108

File tree

7 files changed

+213
-8
lines changed

7 files changed

+213
-8
lines changed

packages/api/lib/controllers/boxesController.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ const getBox = async function getBox (req, res, next) {
397397
* @apiParam (RequestBody) {Object} [mqtt] specify parameters of the MQTT integration for external measurement upload. Please see below for the accepted parameters
398398
* @apiParam (RequestBody) {Object} [ttn] specify parameters for the TTN integration for measurement from TheThingsNetwork.org upload. Please see below for the accepted parameters
399399
* @apiParam (RequestBody) {Boolean="true","false"} [useAuth] whether to use access_token or not for authentication
400+
* @apiParam (RequestBody) {Boolean="true","false"} [sharedBox] whether to share this box (allows transfer to another user while still being able to read the secret and commit measurements)
400401
*
401402
* @apiUse LocationBody
402403
* @apiUse SensorBody
@@ -774,6 +775,7 @@ module.exports = {
774775
{ name: 'ttn', dataType: 'object' },
775776
{ name: 'useAuth', allowedValues: ['true', 'false'] },
776777
{ predef: 'location', required: true },
778+
{ name: 'sharedBox', allowedValues: ['true', 'false'] }
777779
]),
778780
postNewBox,
779781
],

packages/api/lib/controllers/usersController.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,18 @@ const confirmEmailAddress = async function confirmEmailAddress (req, res, next)
199199
};
200200

201201
/**
202-
* @api {post} /users/me/boxes list all boxes of the signed in user
202+
* @api {post} /users/me/boxes list all boxes and sharedBoxes of the signed in user
203203
* @apiName getUserBoxes
204-
* @apiDescription List all boxes of the signed in user with secret fields
204+
* @apiDescription List all boxes and sharedBoxes of the signed in user with secret fields
205205
* @apiGroup Users
206206
* @apiSuccess {String} code `Ok`
207207
* @apiSuccess {String} data A json object with a single `boxes` array field
208208
*/
209209
const getUserBoxes = async function getUserBoxes (req, res, next) {
210210
try {
211211
const boxes = await req.user.getBoxes();
212-
res.send(200, { code: 'Ok', data: { boxes } });
212+
const sharedBoxes = await req.user.getSharedBoxes();
213+
res.send(200, { code: 'Ok', data: { boxes: boxes, sharedBoxes: sharedBoxes } });
213214
} catch (err) {
214215
handleError(err, next);
215216
}

packages/models/src/box/box.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ boxSchema.statics.findMeasurementsOfBoxesStream = function findMeasurementsOfBox
727727
// store all matching sensors under sensors[sensorId]
728728
for (let i = 0, len = boxData.length; i < len; i++) {
729729
for (let j = 0, sensorslen = boxData[i].sensors.length; j < sensorslen; j++) {
730-
if (boxData[i].sensors[j][sensorProperty].toString() === phenomenon) {
730+
if (boxData[i].sensors[j][sensorProperty] && boxData[i].sensors[j][sensorProperty].toString() === phenomenon) {
731731
const sensor = boxData[i].sensors[j];
732732

733733
sensor.lat = boxData[i].currentLocation.coordinates[1];

packages/models/src/user/user.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ const userSchema = new mongoose.Schema({
6161
type: mongoose.Schema.Types.ObjectId,
6262
ref: 'Box'
6363
}],
64+
sharedBoxes: [{
65+
type: mongoose.Schema.Types.ObjectId,
66+
ref: 'Box'
67+
}],
6468
language: {
6569
type: String,
6670
trim: true,
@@ -286,6 +290,9 @@ userSchema.methods.addBox = function addBox (params) {
286290
// persist the saved box in the user
287291
savedBox.serialPort = serialPort;
288292
user.boxes.addToSet(savedBox._id);
293+
if (params.sharedBox) {
294+
user.sharedBoxes.addToSet(savedBox._id);
295+
}
289296

290297
return user.save()
291298
.then(function () {
@@ -550,6 +557,14 @@ userSchema.methods.getBoxes = function getBoxes () {
550557
});
551558
};
552559

560+
userSchema.methods.getSharedBoxes = function getSharedBoxes () {
561+
return Box.find({ _id: { $in: this.sharedBoxes } })
562+
.populate(Box.BOX_SUB_PROPS_FOR_POPULATION)
563+
.then(function (boxes) {
564+
return boxes.map(b => b.toJSON({ includeSecrets: true }));
565+
});
566+
};
567+
553568
userSchema.statics.confirmEmail = function confirmEmail ({ token, email }) {
554569
return this.findOne({ $and: [{ $or: [{ email: email }, { unconfirmedEmail: email }] }, { emailConfirmationToken: token }] })
555570
.exec()

tests/data/getUserBoxesSchema.js

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,195 @@ module.exports = {
193193
'integrations'
194194
]
195195
}
196+
},
197+
'sharedBoxes': {
198+
'type': 'array',
199+
'items': {
200+
'type': 'object',
201+
'properties': {
202+
'createdAt': {
203+
'type': 'string'
204+
},
205+
'exposure': {
206+
'type': 'string'
207+
},
208+
'model': {
209+
'type': 'string'
210+
},
211+
'name': {
212+
'type': 'string'
213+
},
214+
'updatedAt': {
215+
'type': 'string'
216+
},
217+
'useAuth': {
218+
'type': 'boolean'
219+
},
220+
'access_token': {
221+
'type': 'string'
222+
},
223+
'currentLocation': {
224+
'type': 'object',
225+
'properties': {
226+
'coordinates': {
227+
'type': 'array',
228+
'items': { 'type': 'number' }
229+
},
230+
'timestamp': { 'type': 'string' },
231+
'type': { 'type': 'string' }
232+
},
233+
'required': [
234+
'coordinates',
235+
'timestamp',
236+
'type'
237+
]
238+
},
239+
'loc': {
240+
'type': 'array',
241+
'items': {
242+
'type': 'object',
243+
'properties': {
244+
'geometry': {
245+
'type': 'object',
246+
'properties': {
247+
'coordinates': {
248+
'type': 'array',
249+
'items': {
250+
'type': 'number'
251+
}
252+
},
253+
'type': {
254+
'type': 'string'
255+
}
256+
},
257+
'required': [
258+
'coordinates',
259+
'type'
260+
]
261+
},
262+
'type': {
263+
'type': 'string'
264+
}
265+
},
266+
'required': [
267+
'geometry',
268+
'type'
269+
]
270+
}
271+
},
272+
'sensors': {
273+
'type': 'array',
274+
'items': {
275+
'type': 'object',
276+
'properties': {
277+
'title': {
278+
'type': 'string'
279+
},
280+
'unit': {
281+
'type': 'string'
282+
},
283+
'sensorType': {
284+
'type': 'string'
285+
},
286+
'icon': {
287+
'type': 'string'
288+
},
289+
'_id': {
290+
'type': 'string'
291+
},
292+
'lastMeasurement': {
293+
'type': 'object',
294+
'properties': {
295+
'value': {
296+
'type': 'string'
297+
},
298+
'createdAt': {
299+
'type': 'string'
300+
}
301+
},
302+
'required': [
303+
'value',
304+
'createdAt'
305+
]
306+
}
307+
},
308+
'required': [
309+
'title',
310+
'unit',
311+
'sensorType',
312+
'_id'
313+
]
314+
}
315+
},
316+
'_id': {
317+
'type': 'string'
318+
},
319+
'integrations': {
320+
'type': 'object',
321+
'properties': {
322+
'mqtt': {
323+
'type': 'object',
324+
'properties': {
325+
'url': {
326+
'type': 'string'
327+
},
328+
'topic': {
329+
'type': 'string'
330+
},
331+
'decodeOptions': {
332+
'type': 'string'
333+
},
334+
'connectionOptions': {
335+
'type': 'string'
336+
},
337+
'messageFormat': {
338+
'type': 'string'
339+
},
340+
'enabled': {
341+
'type': 'boolean'
342+
}
343+
},
344+
'required': [
345+
'enabled'
346+
]
347+
},
348+
'ttn': {
349+
'type': 'object',
350+
'properties': {
351+
'dev_id': {
352+
'type': 'string'
353+
},
354+
'app_id': {
355+
'type': 'string'
356+
},
357+
'profile': {
358+
'type': 'string'
359+
}
360+
},
361+
}
362+
},
363+
'required': [
364+
'mqtt'
365+
]
366+
}
367+
},
368+
'required': [
369+
'createdAt',
370+
'exposure',
371+
'name',
372+
'updatedAt',
373+
'currentLocation',
374+
'loc',
375+
'sensors',
376+
'_id',
377+
'integrations'
378+
]
379+
}
196380
}
197381
},
198382
'required': [
199-
'boxes'
383+
'boxes',
384+
'sharedBoxes'
200385
]
201386
}
202387
},

tests/data/valid_sensebox.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module.exports = function ({ bbox, model, sensors, lonlat, name = '', sensorTemp
2626
'exposure': 'indoor',
2727
'weblink': 'https://api.opensensemap.org',
2828
'location': loc,
29+
'sharedBox': true,
2930
'mqtt': {
3031
'enabled': false,
3132
'url': '',

tests/tests/005-create-boxes-test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ describe('openSenseMap API Routes: /boxes', function () {
156156
expect(response).to.have.header('content-type', 'application/json; charset=utf-8');
157157

158158
boxCount = boxCount + 1;
159-
//boxIds.push(response.body.data._id);
159+
boxIds.push(response.body.data._id);
160160

161161
return chakram.get(`${BASE_URL}/boxes/${response.body.data._id}`);
162162
})
@@ -199,12 +199,13 @@ describe('openSenseMap API Routes: /boxes', function () {
199199
});
200200
});
201201

202-
it('should let users retrieve their box with all fields', function () {
202+
it('should let users retrieve their boxes and sharedBoxes with all fields', function () {
203203
return chakram.get(`${BASE_URL}/users/me/boxes`, { headers: { 'Authorization': `Bearer ${jwt}` } })
204204
.then(function (response) {
205205
expect(response).to.have.status(200);
206206
expect(response).to.have.schema(getUserBoxesSchema);
207207
expect(response).to.comprise.of.json('data.boxes.0.integrations.mqtt', { enabled: false });
208+
expect(response).to.comprise.of.json('data.sharedBoxes.0.integrations.mqtt', { enabled: false });
208209

209210
return chakram.wait();
210211
});
@@ -598,7 +599,7 @@ describe('openSenseMap API Routes: /boxes', function () {
598599
it('should allow to update the box via PUT with array as grouptags', function () {
599600
const update_payload = { name: 'neuername', exposure: 'outdoor', grouptag: ['newgroup'], description: 'total neue beschreibung', location: { lat: 54.2, lng: 21.1 }, weblink: 'http://www.google.de', image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=' };
600601

601-
return chakram.put(`${BASE_URL}/boxes/${boxIds[2]}`, update_payload, { headers: { 'Authorization': `Bearer ${jwt2}` } })
602+
return chakram.put(`${BASE_URL}/boxes/${boxIds[3]}`, update_payload, { headers: { 'Authorization': `Bearer ${jwt2}` } })
602603
.then(function (response) {
603604
expect(response).to.have.status(200);
604605
expect(response).to.comprise.of.json('data.name', update_payload.name);

0 commit comments

Comments
 (0)