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