3737 */
3838
3939const
40- { Box } = require ( '@sensebox/opensensemap-api-models' ) ,
40+ { Box, User , Claim } = require ( '@sensebox/opensensemap-api-models' ) ,
4141 { addCache, clearCache, checkContentType, redactEmail, postToSlack } = require ( '../helpers/apiUtils' ) ,
4242 { point } = require ( '@turf/helpers' ) ,
4343 classifyTransformer = require ( '../transformers/classifyTransformer' ) ,
4444 {
4545 retrieveParameters,
4646 parseAndValidateTimeParamsForFindAllBoxes,
4747 validateFromToTimeParams,
48- checkPrivilege
48+ checkPrivilege,
49+ validateDateNotPast
4950 } = require ( '../helpers/userParamHelpers' ) ,
5051 handleError = require ( '../helpers/errorHandler' ) ,
5152 jsonstringify = require ( 'stringify-stream' ) ;
@@ -487,24 +488,185 @@ const deleteBox = async function deleteBox (req, res, next) {
487488 }
488489} ;
489490
491+ /**
492+ * @api {get } /boxes/transfer/:senseBoxId Get transfer information for a senseBox
493+ * @apiDescription Get transfer information for a senseBox
494+ * @apiName getTransfer
495+ * @apiGroup Boxes
496+ * @apiUse JWTokenAuth
497+ * @apiUse BoxIdParam
498+ */
499+ const getTransfer = async function getTransfer ( req , res , next ) {
500+ const { boxId } = req . _userParams ;
501+ try {
502+ const transfer = await Claim . findClaimByDeviceID ( boxId ) ;
503+ res . send ( 200 , {
504+ data : transfer ,
505+ } ) ;
506+ } catch ( err ) {
507+ handleError ( err , next ) ;
508+ }
509+ } ;
510+
511+ /**
512+ * @api {post } /boxes/transfer Mark a senseBox for transferring to a different user
513+ * @apiDescription This will mark a senseBox for transfering it to a different user account
514+ * @apiName createTransfer
515+ * @apiGroup Boxes
516+ * @apiParam (RequestBody) {String} boxId ID of the senseBox you want to transfer.
517+ * @apiParam (RequestBody) {RFC3339Date} expiresAt Expiration date for transfer token (default: 24 hours from now).
518+ * @apiUse JWTokenAuth
519+ */
520+ const createTransfer = async function createTransfer ( req , res , next ) {
521+ const { boxId, date } = req . _userParams ;
522+ try {
523+ const transferCode = await req . user . transferBox ( boxId , date ) ;
524+ res . send ( 201 , {
525+ message : 'Box successfully prepared for transfer' ,
526+ data : transferCode ,
527+ } ) ;
528+ } catch ( err ) {
529+ handleError ( err , next ) ;
530+ }
531+ } ;
532+
533+ /**
534+ * @api {put } /boxes/transfer/:senseBoxId Update a transfer token
535+ * @apiDescription Update the expiration date of a transfer token
536+ * @apiName updateTransfer
537+ * @apiGroup Boxes
538+ * @apiParam (RequestBody) {String} Transfer token you want to update.
539+ * @apiParam (RequestBody) {RFC3339Date} expiresAt Expiration date for transfer token (default: 24 hours from now).
540+ * @apiUse JWTokenAuth
541+ * @apiUse BoxIdParam
542+ */
543+ const updateTransfer = async function updateTransfer ( req , res , next ) {
544+ const { boxId, token, date } = req . _userParams ;
545+ try {
546+ const transfer = await req . user . updateTransfer ( boxId , token , date ) ;
547+ res . send ( 200 , {
548+ message : 'Transfer successfully updated' ,
549+ data : transfer ,
550+ } ) ;
551+ } catch ( err ) {
552+ handleError ( err , next ) ;
553+ }
554+ } ;
555+
556+ /**
557+ * @api {delete } /boxes/transfer Revoke transfer token and remove senseBox from transfer
558+ * @apiDescription This will revoke the transfer token and remove the senseBox from transfer
559+ * @apiName removeTransfer
560+ * @apiGroup Boxes
561+ * @apiParam (RequestBody) {String} boxId ID of the senseBox you want to remove from transfer.
562+ * @apiParam (RequestBody) {String} token Transfer token you want to revoke.
563+ * @apiUse JWTokenAuth
564+ */
565+ const removeTransfer = async function removeTransfer ( req , res , next ) {
566+ const { boxId, token } = req . _userParams ;
567+ try {
568+ await req . user . removeTransfer ( boxId , token ) ;
569+ res . send ( 204 ) ;
570+ } catch ( err ) {
571+ handleError ( err , next ) ;
572+ }
573+ } ;
574+
575+ /**
576+ * @api {post } /boxes/claim Claim a senseBox marked for transfer
577+ * @apiDescription This will claim a senseBox marked for transfer
578+ * @apiName claimBox
579+ * @apiGroup Boxes
580+ * @apiUse ContentTypeJSON
581+ * @apiParam (RequestBody) {String} token the token to claim a senseBox
582+ * @apiUse JWTokenAuth
583+ */
584+ const claimBox = async function claimBox ( req , res , next ) {
585+ const { token } = req . _userParams ;
586+
587+ try {
588+ const { owner, claim } = await req . user . claimBox ( token ) ;
589+ await User . transferOwnershipOfBox ( owner , claim . boxId ) ;
590+
591+ await claim . expireToken ( ) ;
592+
593+ res . send ( 200 , { message : 'Device successfully claimed!' } ) ;
594+ } catch ( err ) {
595+ handleError ( err , next ) ;
596+ }
597+ } ;
598+
490599module . exports = {
491600 // auth required
492601 deleteBox : [
493602 checkContentType ,
494603 retrieveParameters ( [
495604 { predef : 'boxId' , required : true } ,
496- { predef : 'password' }
605+ { predef : 'password' } ,
606+ ] ) ,
607+ checkPrivilege ,
608+ deleteBox ,
609+ ] ,
610+ getTransfer : [
611+ retrieveParameters ( [ { predef : 'boxId' , required : true } ] ) ,
612+ checkPrivilege ,
613+ getTransfer ,
614+ ] ,
615+ createTransfer : [
616+ retrieveParameters ( [
617+ { predef : 'boxId' , required : true } ,
618+ { predef : 'dateNoDefault' } ,
619+ ] ) ,
620+ validateDateNotPast ,
621+ checkPrivilege ,
622+ createTransfer ,
623+ ] ,
624+ updateTransfer : [
625+ retrieveParameters ( [
626+ { predef : 'boxId' , required : true } ,
627+ { name : 'token' , dataType : 'String' } ,
628+ { predef : 'dateNoDefault' , required : true } ,
497629 ] ) ,
630+ validateDateNotPast ,
498631 checkPrivilege ,
499- deleteBox
632+ updateTransfer ,
633+ ] ,
634+ removeTransfer : [
635+ retrieveParameters ( [
636+ { predef : 'boxId' , required : true } ,
637+ { name : 'token' , dataType : 'String' } ,
638+ ] ) ,
639+ checkPrivilege ,
640+ removeTransfer ,
641+ ] ,
642+ claimBox : [
643+ checkContentType ,
644+ retrieveParameters ( [ { name : 'token' , dataType : 'String' } ] ) ,
645+ claimBox ,
500646 ] ,
501647 getSketch : [
502648 retrieveParameters ( [
503649 { predef : 'boxId' , required : true } ,
504- { name : 'serialPort' , dataType : 'String' , allowedValues : [ 'Serial1' , 'Serial2' ] } ,
505- { name : 'soilDigitalPort' , dataType : 'String' , allowedValues : [ 'A' , 'B' , 'C' ] } ,
506- { name : 'soundMeterPort' , dataType : 'String' , allowedValues : [ 'A' , 'B' , 'C' ] } ,
507- { name : 'windSpeedPort' , dataType : 'String' , allowedValues : [ 'A' , 'B' , 'C' ] } ,
650+ {
651+ name : 'serialPort' ,
652+ dataType : 'String' ,
653+ allowedValues : [ 'Serial1' , 'Serial2' ] ,
654+ } ,
655+ {
656+ name : 'soilDigitalPort' ,
657+ dataType : 'String' ,
658+ allowedValues : [ 'A' , 'B' , 'C' ] ,
659+ } ,
660+ {
661+ name : 'soundMeterPort' ,
662+ dataType : 'String' ,
663+ allowedValues : [ 'A' , 'B' , 'C' ] ,
664+ } ,
665+ {
666+ name : 'windSpeedPort' ,
667+ dataType : 'String' ,
668+ allowedValues : [ 'A' , 'B' , 'C' ] ,
669+ } ,
508670 { name : 'ssid' , dataType : 'StringWithEmpty' } ,
509671 { name : 'password' , dataType : 'StringWithEmpty' } ,
510672 { name : 'devEUI' , dataType : 'StringWithEmpty' } ,
@@ -513,7 +675,7 @@ module.exports = {
513675 { name : 'display_enabled' , allowedValues : [ 'true' , 'false' ] } ,
514676 ] ) ,
515677 checkPrivilege ,
516- getSketch
678+ getSketch ,
517679 ] ,
518680 updateBox : [
519681 checkContentType ,
@@ -531,21 +693,25 @@ module.exports = {
531693 { name : 'addons' , dataType : 'object' } ,
532694 { predef : 'location' } ,
533695 { name : 'useAuth' , allowedValues : [ 'true' , 'false' ] } ,
534- { name : 'generate_access_token' , allowedValues : [ 'true' , 'false' ] }
696+ { name : 'generate_access_token' , allowedValues : [ 'true' , 'false' ] } ,
535697 ] ) ,
536698 checkPrivilege ,
537- updateBox
699+ updateBox ,
538700 ] ,
539701 // no auth required
540702 getBoxLocations : [
541703 retrieveParameters ( [
542704 { predef : 'boxId' , required : true } ,
543- { name : 'format' , defaultValue : 'json' , allowedValues : [ 'json' , 'geojson' ] } ,
705+ {
706+ name : 'format' ,
707+ defaultValue : 'json' ,
708+ allowedValues : [ 'json' , 'geojson' ] ,
709+ } ,
544710 { predef : 'toDate' } ,
545711 { predef : 'fromDate' } ,
546712 validateFromToTimeParams ,
547713 ] ) ,
548- getBoxLocations
714+ getBoxLocations ,
549715 ] ,
550716 postNewBox : [
551717 checkContentType ,
@@ -555,44 +721,100 @@ module.exports = {
555721 { name : 'exposure' , allowedValues : Box . BOX_VALID_EXPOSURES } ,
556722 { name : 'model' , allowedValues : Box . BOX_VALID_MODELS } ,
557723 { name : 'sensors' , dataType : [ 'object' ] } ,
558- { name : 'sensorTemplates' , dataType : [ 'String' ] , allowedValues : [ 'hdc1080' , 'bmp280' , 'sds 011' , 'tsl45315' , 'veml6070' , 'bme680' , 'smt50' , 'soundlevelmeter' , 'windspeed' , 'scd30' , 'dps310' ] } ,
559- { name : 'serialPort' , dataType : 'String' , defaultValue : 'Serial1' , allowedValues : [ 'Serial1' , 'Serial2' ] } ,
560- { name : 'soilDigitalPort' , dataType : 'String' , defaultValue : 'A' , allowedValues : [ 'A' , 'B' , 'C' ] } ,
561- { name : 'soundMeterPort' , dataType : 'String' , defaultValue : 'B' , allowedValues : [ 'A' , 'B' , 'C' ] } ,
562- { name : 'windSpeedPort' , dataType : 'String' , defaultValue : 'C' , allowedValues : [ 'A' , 'B' , 'C' ] } ,
724+ {
725+ name : 'sensorTemplates' ,
726+ dataType : [ 'String' ] ,
727+ allowedValues : [
728+ 'hdc1080' ,
729+ 'bmp280' ,
730+ 'sds 011' ,
731+ 'tsl45315' ,
732+ 'veml6070' ,
733+ 'bme680' ,
734+ 'smt50' ,
735+ 'soundlevelmeter' ,
736+ 'windspeed' ,
737+ 'scd30' ,
738+ 'dps310' ,
739+ ] ,
740+ } ,
741+ {
742+ name : 'serialPort' ,
743+ dataType : 'String' ,
744+ defaultValue : 'Serial1' ,
745+ allowedValues : [ 'Serial1' , 'Serial2' ] ,
746+ } ,
747+ {
748+ name : 'soilDigitalPort' ,
749+ dataType : 'String' ,
750+ defaultValue : 'A' ,
751+ allowedValues : [ 'A' , 'B' , 'C' ] ,
752+ } ,
753+ {
754+ name : 'soundMeterPort' ,
755+ dataType : 'String' ,
756+ defaultValue : 'B' ,
757+ allowedValues : [ 'A' , 'B' , 'C' ] ,
758+ } ,
759+ {
760+ name : 'windSpeedPort' ,
761+ dataType : 'String' ,
762+ defaultValue : 'C' ,
763+ allowedValues : [ 'A' , 'B' , 'C' ] ,
764+ } ,
563765 { name : 'mqtt' , dataType : 'object' } ,
564766 { name : 'ttn' , dataType : 'object' } ,
565767 { name : 'useAuth' , allowedValues : [ 'true' , 'false' ] } ,
566- { predef : 'location' , required : true }
768+ { predef : 'location' , required : true } ,
567769 ] ) ,
568- postNewBox
770+ postNewBox ,
569771 ] ,
570772 getBox : [
571773 retrieveParameters ( [
572774 { predef : 'boxId' , required : true } ,
573- { name : 'format' , defaultValue : 'json' , allowedValues : [ 'json' , 'geojson' ] }
775+ {
776+ name : 'format' ,
777+ defaultValue : 'json' ,
778+ allowedValues : [ 'json' , 'geojson' ] ,
779+ } ,
574780 ] ) ,
575- getBox
781+ getBox ,
576782 ] ,
577783 getBoxes : [
578784 retrieveParameters ( [
579785 { name : 'name' , dataType : 'String' } ,
580786 { name : 'limit' , dataType : 'Number' , defaultValue : 5 , min : 1 , max : 20 } ,
581- { name : 'exposure' , allowedValues : Box . BOX_VALID_EXPOSURES , dataType : [ 'String' ] } ,
787+ {
788+ name : 'exposure' ,
789+ allowedValues : Box . BOX_VALID_EXPOSURES ,
790+ dataType : [ 'String' ] ,
791+ } ,
582792 { name : 'model' , dataType : [ 'StringWithEmpty' ] } ,
583793 { name : 'grouptag' , dataType : [ 'StringWithEmpty' ] } ,
584794 { name : 'phenomenon' , dataType : 'StringWithEmpty' } ,
585795 { name : 'date' , dataType : [ 'RFC 3339' ] } ,
586- { name : 'format' , defaultValue : 'json' , allowedValues : [ 'json' , 'geojson' ] } ,
587- { name : 'classify' , defaultValue : 'false' , allowedValues : [ 'true' , 'false' ] } ,
588- { name : 'minimal' , defaultValue : 'false' , allowedValues : [ 'true' , 'false' ] } ,
796+ {
797+ name : 'format' ,
798+ defaultValue : 'json' ,
799+ allowedValues : [ 'json' , 'geojson' ] ,
800+ } ,
801+ {
802+ name : 'classify' ,
803+ defaultValue : 'false' ,
804+ allowedValues : [ 'true' , 'false' ] ,
805+ } ,
806+ {
807+ name : 'minimal' ,
808+ defaultValue : 'false' ,
809+ allowedValues : [ 'true' , 'false' ] ,
810+ } ,
589811 { name : 'full' , defaultValue : 'false' , allowedValues : [ 'true' , 'false' ] } ,
590812 { name : 'near' } ,
591813 { name : 'maxDistance' } ,
592814 { predef : 'bbox' } ,
593815 ] ) ,
594816 parseAndValidateTimeParamsForFindAllBoxes ,
595817 addCache ( '5 minutes' , 'getBoxes' ) ,
596- getBoxes
597- ]
818+ getBoxes ,
819+ ] ,
598820} ;
0 commit comments