├── .gitignore ├── examples └── client.js ├── package.json ├── README.md ├── server.js ├── fprintserver.json ├── lib └── FingerprintReader.js └── node-fprint-server.paw /.gitignore: -------------------------------------------------------------------------------- 1 | db.json 2 | node_modules 3 | -------------------------------------------------------------------------------- /examples/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const socketIoClient = require('socket.io-client'); 4 | 5 | const socket = socketIoClient( process.argv[2] || 'http://localhost:4444' ); 6 | socket 7 | .on('connect', function(){ 8 | console.log('[on connect]'); 9 | }) 10 | .on('disconnect', function(){ 11 | console.log('[on disconnect]'); 12 | }) 13 | .on('identify', function( userId, userData ){ 14 | console.log('[on identify]', 'userId:', userId, 'userData:', userData ); 15 | }) 16 | .on('user-add', function( userId, userData ){ 17 | console.log('[on user-add]', 'userId:', userId, 'userData:', userData ); 18 | }) 19 | .on('user-update', function( userId, userData ){ 20 | console.log('[on user-update]', 'userId:', userId, 'userData:', userData ); 21 | }) 22 | .on('user-delete', function( userId ){ 23 | console.log('[on user-delete]', 'userId:', userId ); 24 | }) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-fprint-server", 3 | "version": "1.0.0", 4 | "description": "HTTP server for libfprint to enroll & identify remotely", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/WeeJeWel/node-fprint-server.git" 12 | }, 13 | "author": "Emile Nijssen", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/WeeJeWel/node-fprint-server/issues" 17 | }, 18 | "homepage": "https://github.com/WeeJeWel/node-fprint-server#readme", 19 | "dependencies": { 20 | "body-parser": "^1.15.2", 21 | "cors": "^2.8.1", 22 | "express": "^4.14.0", 23 | "fs-extra": "^0.30.0", 24 | "node-fprint": "0.0.7", 25 | "node-uuid": "^1.4.7", 26 | "socket.io": "^1.5.0", 27 | "underscore": "^1.8.3", 28 | "yargs": "^6.3.0" 29 | }, 30 | "devDependencies": { 31 | "socket.io-client": "^1.5.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-fprint-server 2 | HTTP server for libfprint to enroll & identify remotely. Users can be retrieved, registered, updated and deleted using HTTP, identify events are emitted using Socket.io. 3 | 4 | This project is not meant to be used as a Node.js module but rather as a standalone application. 5 | 6 | ## Installation 7 | 8 | Make sure you have the Linux packages `libfprint0` and `libfprint-dev` installed. 9 | 10 | Execute in your console: 11 | 12 | ``` 13 | git clone https://github.com/WeeJeWel/node-fprint-server 14 | cd node-fprint-server 15 | npm install 16 | node server.js --db /writable/path/to/database.json --device "Name of your fingerprint device" --port 4444 17 | ``` 18 | 19 | e.g. when running on a Raspberry Pi with a Microsoft Fingerprint Reader: 20 | 21 | ``` 22 | node server.js --db /home/pi/fingerprintserver.json --device "Digital Persona U.are.U 4000/4000B/4500" 23 | ``` 24 | 25 | _Hint: run the server once to get a list of devices._ 26 | 27 | ## HTTP API 28 | 29 | ### Get all users ### 30 | Append `?fingerprint=1` to get the binary fingerprint data as well. 31 | 32 | ``` 33 | GET /api/user 34 | ``` 35 | 36 | ### Get a single user ### 37 | 38 | ``` 39 | GET /api/user/:id 40 | ``` 41 | 42 | ###Create a user ### 43 | After invoking this call, the user must place a finger on the Fingerprint Reader Device. 44 | 45 | The body is a 'data' object, where additional information can be placed such as a name. 46 | 47 | ``` 48 | POST /api/user 49 | 50 | { 51 | "name": "Chares" 52 | } 53 | ``` 54 | 55 | ### Edit a user's data object ### 56 | 57 | ``` 58 | PUT /api/user 59 | 60 | { 61 | "name": "Charles" 62 | } 63 | ``` 64 | 65 | ### Delete a user ### 66 | ``` 67 | DELETE /api/user/:id 68 | ``` 69 | 70 | ## Socket.io API 71 | 72 | Also see `./examples/client.js` 73 | 74 | ``` 75 | socket 76 | 77 | // when a user is identified (finger recognized) 78 | .on('identify', function( userId, userData ){ 79 | console.log('[on identify]', 'userId:', userId, 'userData:', userData ); 80 | }) 81 | 82 | // when a user is added 83 | .on('user-add', function( userId, userData ){ 84 | console.log('[on user-add]', 'userId:', userId, 'userData:', userData ); 85 | }) 86 | 87 | // when a user is updated 88 | .on('user-update', function( userId, userData ){ 89 | console.log('[on user-update]', 'userId:', userId, 'userData:', userData ); 90 | }) 91 | 92 | // when a user is deleted 93 | .on('user-delete', function( userId ){ 94 | console.log('[on user-delete]', 'userId:', userId ); 95 | }) 96 | ``` -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const socketIo = require('socket.io'); 5 | const cors = require('cors'); 6 | const bodyParser = require('body-parser'); 7 | const yargs = require('yargs').argv; 8 | 9 | const FingerprintReader = require('./lib/FingerprintReader.js'); 10 | 11 | const port = process.env.FPRINT_SERVER_PORT || yargs.port || 4444; 12 | 13 | const reader = new FingerprintReader({ 14 | deviceId : process.env.FPRINT_SERVER_DEVICE || yargs.device || 'Digital Persona U.are.U 4000/4000B/4500', 15 | settingsPath : process.env.FPRINT_SERVER_CONFPATH || yargs.db || './db.json', 16 | debug : true 17 | }) 18 | .on('identify', _onIdentify) 19 | .on('user-add', _onUserAdd) 20 | .on('user-update', _onUserUpdate) 21 | .on('user-delete', _onUserDelete) 22 | .init(); 23 | 24 | const server = express() 25 | 26 | .use( cors() ) 27 | .use( bodyParser.json() ) 28 | 29 | .use(( req, res, next ) => { 30 | 31 | res.locals.success = function( message, statusCode ) { 32 | return res 33 | .status( statusCode || 200 ) 34 | .json({ 35 | success: true, 36 | message: message 37 | }) 38 | } 39 | 40 | res.locals.error = function( message, statusCode ) { 41 | 42 | if( message instanceof Error ) 43 | message = message.message || message.toString() 44 | 45 | return res 46 | .status( statusCode || 500 ) 47 | .json({ 48 | success: false, 49 | message: message 50 | }) 51 | } 52 | 53 | next(); 54 | 55 | }) 56 | 57 | .get( '/api/user/:id', ( req, res ) => { 58 | 59 | let user = reader.getUser( req.params.id, ( req.query.fingerprints === '1' ) ); 60 | if( user instanceof Error ) return res.locals.error( user ); 61 | return res.locals.success( user ); 62 | 63 | }) 64 | .get( '/api/user', ( req, res ) => { 65 | 66 | let users = reader.getUsers( ( req.query.fingerprints === '1' ) ); 67 | if( users instanceof Error ) return res.locals.error( users ); 68 | return res.locals.success( users ); 69 | 70 | }) 71 | .put( '/api/user/:id', ( req, res ) => { 72 | 73 | let user = reader.updateUser( req.params.id, req.body ); 74 | if( user instanceof Error ) return res.locals.error( user ); 75 | return res.locals.success( user ); 76 | 77 | }) 78 | .post( '/api/user', ( req, res ) => { 79 | 80 | reader.addUser( req.body, ( err, result ) => { 81 | if( err ) return res.locals.error( err ); 82 | return res.locals.success( result ); 83 | }); 84 | 85 | }) 86 | .delete( '/api/user/:id', ( req, res ) => { 87 | 88 | let user = reader.deleteUser( req.params.id ); 89 | if( user instanceof Error ) return res.locals.error( user ); 90 | return res.locals.success( user ); 91 | 92 | }) 93 | 94 | .listen( port, () => { 95 | console.log(`fprint server running on port ${port}...`); 96 | }); 97 | 98 | const io = socketIo( server ); 99 | 100 | function _onIdentify( userId, userData ) { 101 | io.sockets.emit('identify', userId, userData ); 102 | } 103 | 104 | function _onUserAdd( userId, userData ) { 105 | io.sockets.emit('user-add', userId, userData ); 106 | } 107 | 108 | function _onUserUpdate( userId, userData ) { 109 | io.sockets.emit('user-update', userId, userData ); 110 | } 111 | 112 | function _onUserDelete( userId ) { 113 | io.sockets.emit('user-delete', userId ); 114 | } -------------------------------------------------------------------------------- /fprintserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "fingerprints": [ 3 | { 4 | "id": "0f8d718e-c799-4033-9495-f994226308fb", 5 | "data": { 6 | "name": "Henk" 7 | }, 8 | "fingerprint": "789cedd3cd2b445118c7f173a764c16242d7ce428abc2c341b921066a79952a6a6c65b23458d506a0a1389ac14d9906441940d61256c689642368a855262234d6433be4ff7f10f4816dca73ef777ee39f7addb79fcc14a8f91b2aa39b4a113c31843021398c42c96b08a756ca84d6c6317fb38c0118e7182535ca82b5cab7bf580273ce305af78c707322d63bcc847812a42314ae1431d1ad00c3f82684504ed88a20ffd18b08c5b6e7dbb06518e10a2c6d9a33558432e7beb864ca2105e7419a7273cace5c8de641cc3384690853ce3f4db224af4794da8c7b4717a298019d83ca383cc265bc80594e14dbfc562be961c4286f403e9c39d717aee0cbd8823a26379ef28e670fed33fccad7f5515984aa7d342c63132a06993dda490fd26fb39aef35f6b8dc6e91d5befb9c5bc9e8718a7b0c27899947ba57ff650a529d7c877c87c0f52facc2d1d4b86f5dd722ed7ec908fba2699c4a5ce1deafd017d67f817fea15b7fb73e01f198a010" 9 | }, 10 | { 11 | "id": "11f024f5-47b9-4d39-9755-d017a0cefc6c", 12 | "data": { 13 | "name": "Henk (updated)" 14 | }, 15 | "fingerprint": "789cedd5cf2bc3711cc7f18f39a29cec261cccc56197998cd42285467e15075148b228074a142b254573a0504849b92849993838a05ccc81d28eb41325071ce6f9f679fb037670d1f75d8f7d3fdfcfe7b3efdeadcfab6f6d93d765a432027cd4a14135a3159d18c618a6308d592c611d9bd8c20e76b187431ce1182788e114e7b8c025ae708d1b15c73d1ef088049ef182577ce0135f3fdd1ae35299c8462edcf0c00b1fca104015bad08d5ef4239c619c722aad9ac1b6b167bc1a5eace20da3e84004f93843014a508e102a316eec397e42213c18c02de68ccd95e4a91d2b28c61a2ab08f79d41b9bcd6d5d5bc690b119bd430f068dcd6b180b907c4b3e1b8dcd6f1b7c7023872cb468cf92f51a6373ef9453e95424954a4da094711059907191ae05f57e12bddcc721677ec4d8f316d3efcb5a07128832ef479e3ee777ad53e7928ce577127a95b945c66bba4fe6e4ead71ee5dd14d21e24c71b78d7de92fa5ce9a94ff705759ff418d5eb01fbc49ffd914efdcbfa0676269633" 16 | }, 17 | { 18 | "id": "58191d6c-38fd-4e25-81e6-97aec7b2adb3", 19 | "data": { 20 | "name": "Henk (updated)" 21 | }, 22 | "fingerprint": "789cedd5cf2b047118c7f1ef8813e5a27570d89213d9926893e326dbe6c7c9d62607e1b86d9b4438282527591c1c912d270907b5ff810391526a4f94b9b929a5f17ef27172b617df4fbde679e6db3c33d334d30c8df7d4394b9060539405ac6203dbd8c101ca38c7052ab8c60d6e712f0ff2882754f18c106ff28e4fd407ce35a0092d68451c1d48600029a491c1282630893c0a2806cec7a72699c711ee90c61ae6b08741adf763189bea17f1817d34f3ae76515f7088599ca01719cc604bf512a738c6325e51d03547708529ec22a59938dad0893ef7fd2d97fee651f8f8fc4a12dd6847258aa2758c69cddedd3cfb2bea97e84b9ac9d2dbcc343544a3fa1c358650eb76be9866b33aceaa9ddffe4976ad2afb49cdd8bcfdc37262eb3ff754a69ec1eeb356cfc7e77fe70bee947e6e" 23 | } 24 | ], 25 | "users": [ 26 | { 27 | "id": "e1f5d859-84ca-4617-956c-38c845dbfdd1", 28 | "data": { 29 | "name": "Henk" 30 | }, 31 | "fingerprint": "789cedd5cf2b04611cc7f167f6228d1c955cb45729d42eebb0175abf0aed45c94162dbdada9413ad42c4859670b01637071485a2e522b50739b8b96c71919ce5a4f5fef67c0ffb076c0eccb75ef399799ae799697abe4d64b0d967a49c00872e441147025398c32ad26ab3c41632c8620ffb38c2314e708a735c23871bdce20ef778c20bdef0816f5438c654a11ab5f0a309010411421811f4208a11c4541c092495575e95ab644f2f18bb8f436830b64746f18a2fd4c18f793ca0122bc6f6ce00ea11c4123ad18f3076d18b196c630d8f7a5f2b0ed08d595da7053ec73eaf0f171887ebd89e6dc7156258c72476f4fa1987b82ce7c7f1eadf57b2582c369263e41936389f263bc8215214745c7ac435f6df21f36ac8617cea3d722d6b6490d7b98b3aeeea7af26f7927db3465fe32268ced91ac9ecbde9767a6742ca5e3055d3bafef5b9a39cddffa765efdfdfa01585b9e98" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /lib/FingerprintReader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const events = require('events'); 4 | const fs = require('fs-extra'); 5 | const fprint = require('node-fprint'); 6 | const uuid = require('node-uuid'); 7 | const _ = require('underscore'); 8 | 9 | class FingerprintReader extends events.EventEmitter { 10 | 11 | constructor( opts ) { 12 | super(); 13 | 14 | this.opts = Object.assign({}, { 15 | settingsPath: './', 16 | deviceId : undefined, 17 | debug : false 18 | }, opts); 19 | 20 | fprint.setDebug(3); 21 | if( !fprint.init() ) throw new Error('fprint not loaded'); 22 | 23 | this._settings = {}; 24 | this._devices = []; 25 | this._state = undefined; 26 | 27 | } 28 | 29 | init() { 30 | this._getSettings(); 31 | this._refreshDevices(); 32 | this._initDevice(); 33 | 34 | return this; 35 | } 36 | 37 | _debug() { 38 | if( this.opts.debug ) this._log.apply( null, arguments ); 39 | } 40 | 41 | _log() { 42 | console.log.apply( null, arguments ); 43 | } 44 | 45 | _getSettings() { 46 | this._debug('_getSettings'); 47 | 48 | try { 49 | this._settings = fs.readJsonSync( this.opts.settingsPath ); 50 | } catch( err ) { 51 | this._settings = {}; 52 | } 53 | 54 | } 55 | 56 | _setSettings() { 57 | this._debug('_setSettings'); 58 | 59 | fs.outputJsonSync( this.opts.settingsPath, this._settings ); 60 | 61 | } 62 | 63 | _refreshDevices() { 64 | this._debug('_refreshDevices'); 65 | 66 | this._devices = fprint.discoverDevices(); 67 | if( this._devices.length < 1 ) throw new Error('no devices found'); 68 | 69 | this._devices.forEach((device, i) => { 70 | this._log('device found:', device) 71 | }); 72 | 73 | } 74 | 75 | _initDevice() { 76 | this._debug('_initDevice'); 77 | 78 | if( this._devices.indexOf( this.opts.deviceId ) < 0 ) 79 | throw new Error(`device not found: ${this.opts.deviceId}`); 80 | 81 | this._log('using device:', this.opts.deviceId); 82 | 83 | this._deviceHandle = fprint.openDevice( this.opts.deviceId ); 84 | this._startIdentify(); 85 | 86 | } 87 | 88 | _startIdentify( callback ) { 89 | this._debug('_startIdentify'); 90 | 91 | callback = callback || function(){} 92 | 93 | if( !this._deviceHandle ) 94 | throw new Error('missing device handle'); 95 | 96 | let usersArray = []; 97 | let users = this.getUsers(); 98 | users.forEach(( fingerprintObj ) => { 99 | usersArray.push( fingerprintObj.fingerprint ); 100 | }); 101 | 102 | fprint.identifyStart(this._deviceHandle, usersArray, ( state, message, index ) => { 103 | this._debug('identifyStart state', state, 'message', message, 'index', index) 104 | 105 | this._state = 'identify'; 106 | 107 | if( message === 'identify-succeeded' ) 108 | this.emit('identify', users[ index ].id, users[ index ].data ); 109 | 110 | setTimeout(() => { 111 | this._stopIdentify(() => { 112 | setTimeout(() => { 113 | this._startIdentify(); 114 | }, 2500); 115 | }); 116 | }); 117 | }); 118 | } 119 | 120 | _stopIdentify( callback ) { 121 | this._debug('_stopIdentify'); 122 | 123 | callback = callback || function(){} 124 | 125 | if( !this._deviceHandle ) 126 | throw new Error('missing device handle'); 127 | 128 | if( this._state === 'identify' ) { 129 | fprint.identifyStop( this._deviceHandle, () => { 130 | this._state = undefined; 131 | callback(); 132 | }); 133 | } else { 134 | callback(); 135 | } 136 | 137 | } 138 | 139 | getUsers( returnFingerprint ) { 140 | 141 | let result = []; 142 | 143 | this._settings.users = this._settings.users || []; 144 | this._settings.users.forEach((user) => { 145 | result.push( this.getUser( user.id, returnFingerprint ) ); 146 | }); 147 | 148 | return result; 149 | } 150 | 151 | getUser( userId, returnFingerprint ) { 152 | 153 | if( typeof returnFingerprint !== 'boolean' ) 154 | returnFingerprint = true; 155 | 156 | let user = _.findWhere( this._settings.users, { 157 | id: userId 158 | }); 159 | 160 | if( user ) { 161 | if( returnFingerprint === true ) return user; 162 | let userClone = _.clone( user ); 163 | delete userClone.fingerprint; 164 | return userClone; 165 | } 166 | 167 | return new Error('invalid_user'); 168 | } 169 | 170 | addUser( data, callback ) { 171 | callback = callback || function(){} 172 | 173 | if( !this._deviceHandle ) 174 | throw new Error('missing device handle'); 175 | 176 | let userId = uuid.v4() 177 | 178 | data = Object.assign({ 179 | name : 'New User' 180 | }, data); 181 | 182 | this._stopIdentify(() => { 183 | 184 | fprint.enrollStart( this._deviceHandle, ( state, message, fingerprint ) => { 185 | this._debug('enrollStart state', state, 'message', message, 'fingerprint', fingerprint) 186 | 187 | this._state = 'enroll'; 188 | 189 | if( message === 'enroll-completed' ) { 190 | 191 | fprint.enrollStop( this._deviceHandle, () => { 192 | 193 | this._state = undefined; 194 | 195 | this._settings.users = this._settings.users || []; 196 | this._settings.users.push({ 197 | id : userId, 198 | data : data, 199 | fingerprint : fingerprint 200 | }); 201 | this._setSettings(); 202 | 203 | this.emit('user-add', userId, data); 204 | 205 | callback( null, this.getUser( userId, false ) ); 206 | 207 | this._startIdentify(); 208 | }); 209 | } else { 210 | 211 | } 212 | 213 | }); 214 | 215 | }); 216 | 217 | 218 | } 219 | 220 | updateUser( userId, newUserData ) { 221 | 222 | let user = this.getUser( userId ); 223 | if( user instanceof Error ) return user; 224 | 225 | user.data = newUserData; 226 | this._setSettings(); 227 | 228 | this.emit('user-update', userId, newUserData); 229 | 230 | return this.getUser( userId, false ); 231 | 232 | } 233 | 234 | deleteUser( userId ) { 235 | 236 | this._settings.users = this._settings.users || []; 237 | this._settings.users = _.without( this._settings.users, this.getUser( userId ) ); 238 | this._setSettings(); 239 | 240 | this.emit('user-delete', userId); 241 | 242 | this._stopIdentify(() => { 243 | this._startIdentify(); 244 | }) 245 | 246 | } 247 | 248 | } 249 | 250 | module.exports = FingerprintReader; -------------------------------------------------------------------------------- /node-fprint-server.paw: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 134481920 7 | 650506DD-5272-4E95-B18B-3E1B8011A432 8 | 116 9 | 10 | 11 | 12 | NSPersistenceFrameworkVersion 13 | 752 14 | NSStoreModelVersionHashes 15 | 16 | LMCookieJar 17 | 18 | Fttmf2L4PrGvKUF496+nqgVVGek45TjOe7sUMtjNg8I= 19 | 20 | LMEnvironment 21 | 22 | uzBoVFcO4YvR9/3ej4AJ1UOOsA/u5DKY2aemusoIseU= 23 | 24 | LMEnvironmentDomain 25 | 26 | yM1GPGHdquS8IWLtuczlNoqKhIhD9FW6IReSfFffJgs= 27 | 28 | LMEnvironmentVariable 29 | 30 | P8e0lYd5JZKRabS/eXVSOJ4oitilz67xtv+pLqW1Jqg= 31 | 32 | LMEnvironmentVariableValue 33 | 34 | my5hNPJ51oDCSa8EgdNxWAnRcDLcERUGjtuXnzhSxQ0= 35 | 36 | LMKeyValue 37 | 38 | bIXXbyYF2xAv2MXg8JTVFsslmMKuvsfnR86QdUcFkdM= 39 | 40 | LMRequest 41 | 42 | vcehD1+Y0zJEqCmgqlTWLscXXTAat5YQbsUJSVqwTq0= 43 | 44 | LMRequestGroup 45 | 46 | MnZKiG/nHZ2bvbULv2vfzH+0tXd5w+NuuMPlNJjBAMw= 47 | 48 | LMRequestTreeItem 49 | 50 | Ae5GlAcsXxZOGKCPQgemYbXQ3+KUtHlsprrqBD+V81o= 51 | 52 | 53 | NSStoreModelVersionHashesVersion 54 | 3 55 | NSStoreModelVersionIdentifiers 56 | 57 | LMDocumentVersion4 58 | 59 | 60 | 61 | 62 | 63 | 64 | CB4A32D2-95F9-4D9F-9BEE-7D7B246AC6DA 65 | 1 66 | Default Jar 67 | 68 | 69 | 70 | 0 71 | 72 | 1 73 | 74 | 75 | 76 | 77 | 78 | 79 | http://192.168.0.111:4444 80 | 81 | 82 | 83 | 84 | BBCF0D5A-29C3-424A-A27D-1D2C5274A8EA 85 | 0 86 | Default Domain 87 | 88 | 89 | 90 | 91 | AAB8B08E-56DB-4369-9334-5C9F3FA661A1 92 | 0 93 | host 94 | 95 | 96 | 97 | 98 | C6BF3969-76D7-4D3D-A52D-4064A1AA76E2 99 | 0 100 | Default Environment 101 | 102 | 103 | 104 | 105 | ADB056DD-04D0-46BA-A899-049E641D7DFD 106 | [{"data":{"environmentVariable":"AAB8B08E-56DB-4369-9334-5C9F3FA661A1"},"identifier":"com.luckymarmot.EnvironmentVariableDynamicValue"},"\\/api\\/user"] 107 | 1 108 | 1 109 | 0 110 | 0 111 | POST 112 | 0 113 | [{"data":{"json":"{\\"name\\":\\"Henk\\"}"},"identifier":"com.luckymarmot.JSONDynamicValue"}] 114 | 2 115 | Add user 116 | 117 | 118 | 119 | 120 | 121 | 0E2DC242-1076-4DE3-991C-26798A486956 122 | [{"data":{"environmentVariable":"AAB8B08E-56DB-4369-9334-5C9F3FA661A1"},"identifier":"com.luckymarmot.EnvironmentVariableDynamicValue"},"\\/api\\/user"] 123 | 1 124 | 1 125 | 0 126 | 0 127 | GET 128 | 0 129 | 0 130 | Get users 131 | 132 | 133 | 134 | 135 | 136 | 137 | 0 138 | 139 | 1 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 0 148 | 149 | 1 150 | 151 | 152 | 153 | 154 | 155 | 156 | EC1AD2A9-4F94-4609-9691-FC92E27D3AB8 157 | [{"data":{"environmentVariable":"AAB8B08E-56DB-4369-9334-5C9F3FA661A1"},"identifier":"com.luckymarmot.EnvironmentVariableDynamicValue"},"\\/api\\/user\\/",{"data":{"request":"0E2DC242-1076-4DE3-991C-26798A486956","keyPath":["message[0].id"],"format":0},"identifier":"com.luckymarmot.ResponseBodyPathDynamicValue"}] 158 | 1 159 | 1 160 | 0 161 | 0 162 | GET 163 | 0 164 | 1 165 | Get user 166 | 167 | 168 | 169 | 170 | 171 | 8569D107-AF40-4147-8DBF-3667EA6916F0 172 | [{"data":{"environmentVariable":"AAB8B08E-56DB-4369-9334-5C9F3FA661A1"},"identifier":"com.luckymarmot.EnvironmentVariableDynamicValue"},"\\/api\\/user\\/",{"data":{"request":"ADB056DD-04D0-46BA-A899-049E641D7DFD","keyPath":["message.id"],"format":0},"identifier":"com.luckymarmot.ResponseBodyPathDynamicValue"}] 173 | 1 174 | 1 175 | 0 176 | 0 177 | DELETE 178 | 0 179 | [{"data":{"json":"{\\"name\\":\\"Henk 2\\"}"},"identifier":"com.luckymarmot.JSONDynamicValue"}] 180 | 4 181 | Delete user 182 | 183 | 184 | 185 | 186 | 187 | 188 | 0 189 | 190 | 1 191 | 192 | 193 | 194 | 195 | 196 | 197 | AAF05911-ED3C-4C21-8B1C-D81250D6E7DC 198 | [{"data":{"environmentVariable":"AAB8B08E-56DB-4369-9334-5C9F3FA661A1"},"identifier":"com.luckymarmot.EnvironmentVariableDynamicValue"},"\\/api\\/user\\/",{"data":{"request":"ADB056DD-04D0-46BA-A899-049E641D7DFD","keyPath":["message.id"],"format":0},"identifier":"com.luckymarmot.ResponseBodyPathDynamicValue"}] 199 | 1 200 | 1 201 | 0 202 | 0 203 | PUT 204 | 0 205 | [{"data":{"json":"{\\"name\\":\\"Henk (updated)\\"}"},"identifier":"com.luckymarmot.JSONDynamicValue"}] 206 | 3 207 | Update user 208 | 209 | 210 | 211 | 212 | 213 | 214 | 0 215 | 216 | 1 217 | 218 | 219 | 220 | 221 | 222 | --------------------------------------------------------------------------------