github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/server/api/index.js (about) 1 /** 2 * Copyright 2017 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * ---------------------------------------------------------------------------- 16 */ 17 'use strict' 18 19 const _ = require('lodash') 20 const express = require('express') 21 const bodyParser = require('body-parser') 22 23 const auth = require('./auth') 24 const users = require('./users') 25 const { BadRequest, Unauthorized } = require('./errors') 26 const agents = require('./agents') 27 const records = require('./records') 28 const recordTypes = require('./record_types') 29 const blockchain = require('../blockchain/') 30 const batcher = require('../blockchain/batcher') 31 const config = require('../system/config') 32 33 const router = express.Router() 34 35 // Passes a request to a function, then sends back the promised result as JSON. 36 // Will catch errors and send on to any error handling middleware. 37 const handlePromisedResponse = func => (req, res, next) => { 38 func(req) 39 .then(filterQueryParams(req.query)) 40 .then(result => res.json(result)) 41 .catch(err => next(err)) 42 } 43 44 // Handler suitable for all GET requests. Passes the endpoint function 45 // a merged copy of the parameters for a request, handling promised results 46 const handle = func => handlePromisedResponse(req => { 47 return func(_.assign({}, req.query, req.params, req.internal)) 48 }) 49 50 // Handler suitable for POST/PATCH request, passes along the request's body 51 // in addition to its other parameters. 52 const handleBody = func => handlePromisedResponse(req => { 53 return func(req.body, _.assign({}, req.query, req.params, req.internal)) 54 }) 55 56 const filterQueryParams = ({ fields, omit }) => result => { 57 const filterParams = obj => fields ? _.pick(obj, fields.split(',')) 58 : omit ? _.omit(obj, omit.split(',')) 59 : obj 60 61 return Array.isArray(result) ? _.map(result, filterParams) : filterParams(result) 62 } 63 64 // Parses the endpoints from an Express router 65 const getEndpoints = router => { 66 return _.chain(router.stack) 67 .filter(layer => layer.route) 68 .map(({ route }) => { 69 return _.chain(route.stack) 70 .reduceRight((layers, layer) => { 71 if (layer.name === 'restrict') { 72 _.nth(layers, -1).restricted = true 73 } else { 74 layers.push({ 75 path: route.path, 76 method: layer.method.toUpperCase(), 77 restricted: false 78 }) 79 } 80 return layers 81 }, []) 82 .reverse() 83 .value() 84 }) 85 .flatten() 86 .value() 87 } 88 89 /* 90 * Custom Middleware 91 */ 92 93 // Logs basic request information to the console 94 const logRequest = (req, res, next) => { 95 console.log(`Received ${req.method} request for ${req.url} from ${req.ip}`) 96 next() 97 } 98 99 // Adds an object to the request for storing internally generated parameters 100 const initInternalParams = (req, res, next) => { 101 req.internal = {} 102 next() 103 } 104 105 // Middleware for parsing the wait query parameter 106 const waitParser = (req, res, next) => { 107 const DEFAULT_WAIT = Math.floor(config.DEFAULT_SUBMIT_WAIT / 1000) 108 const parsed = req.query.wait === '' ? DEFAULT_WAIT : Number(req.query.wait) 109 req.query.wait = _.isNaN(parsed) ? null : parsed 110 next() 111 } 112 113 // Check the Authorization header if present. 114 // Saves the encoded public key to the request object. 115 const authHandler = (req, res, next) => { 116 req.internal.authedKey = null 117 const token = req.headers.authorization 118 if (!token) return next() 119 120 auth.verifyToken(token) 121 .then(publicKey => { 122 req.internal.authedKey = publicKey 123 next() 124 }) 125 .catch(() => next()) 126 } 127 128 // Route-specific middleware, throws error if not authorized 129 const restrict = (req, res, next) => { 130 if (req.internal.authedKey) return next() 131 next(new Unauthorized('This route requires a valid Authorization header')) 132 } 133 134 // Send back a simple JSON error with an HTTP status code 135 const errorHandler = (err, req, res, next) => { 136 if (err) { 137 res.status(err.status || 500).json({ error: err.message }) 138 } else { 139 next() 140 } 141 } 142 143 /* 144 * Route and Middleware Setup 145 */ 146 147 router.use(bodyParser.json({ type: 'application/json' })) 148 router.use(bodyParser.raw({ type: 'application/octet-stream' })) 149 150 router.use(logRequest) 151 router.use(initInternalParams) 152 router.use(waitParser) 153 router.use(authHandler) 154 155 router.get('/agents', handle(agents.list)) 156 router.get('/agents/:publicKey', handle(agents.fetch)) 157 158 router.post('/authorization', handleBody(auth.authorize)) 159 160 router.get('/info', handle(() => { 161 return Promise.resolve() 162 .then(() => ({ 163 pubkey: batcher.getPublicKey(), 164 mapsApiKey: config.MAPS_API_KEY, 165 endpoints: endpointInfo 166 })) 167 })) 168 169 router.post('/info/mapsApiKey', handleBody(body => { 170 return Promise.resolve() 171 .then(() => { 172 if (config.MAPS_API_KEY) { 173 throw new BadRequest('Google Maps API key already set') 174 } 175 config.set('MAPS_API_KEY', body.mapsApiKey) 176 return `Google Maps API key set to "${body.mapsApiKey}"` 177 }) 178 })) 179 180 router.get('/records', handle(records.listRecords)) 181 router.get('/records/:recordId', handle(records.fetchRecord)) 182 router.get('/records/:recordId/property/:propertyName', handle(records.fetchProperty)) 183 router.get('/records/:recordId/:propertyName', handle(records.fetchProperty)) 184 185 router.get('/record-types', handle(recordTypes.list)) 186 router.get('/record-types/:typeName', handle(recordTypes.fetch)) 187 188 router.post('/transactions', handleBody(blockchain.submit)) 189 190 router.route('/users') 191 .post(handleBody(users.create)) 192 .patch(restrict, handleBody(users.update)) 193 194 // This route is redundant, but matches RESTful expectations 195 router.patch('/users/:publicKey', restrict, handleBody((body, params) => { 196 if (params.publicKey !== params.authedKey) { 197 throw new Unauthorized('You may only modify your own user account!') 198 } 199 return users.update(body, params) 200 })) 201 202 router.use(errorHandler) 203 const endpointInfo = getEndpoints(router) 204 205 module.exports = router