github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/server/db/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 r = require('rethinkdb') 20 const _ = require('lodash') 21 const jsSchema = require('js-schema') 22 const config = require('../system/config') 23 24 const HOST = config.DB_HOST 25 const PORT = config.DB_PORT 26 const NAME = config.DB_NAME 27 const RETRY_WAIT = config.RETRY_WAIT 28 const AWAIT_TABLE = 'blocks' 29 30 // Connection to db for query methods, run connect before querying 31 let connection = null 32 33 const promisedTimeout = (fn, wait) => { 34 return new Promise(resolve => setTimeout(resolve, wait)).then(fn); 35 } 36 37 const awaitDatabase = () => { 38 return r.tableList().run(connection) 39 .then(tableNames => { 40 if (!tableNames.includes(AWAIT_TABLE)) { 41 throw new Error() 42 } 43 console.log('Successfully connected to database:', NAME) 44 }) 45 .catch(() => { 46 console.warn('Database not initialized:', NAME) 47 console.warn(`Retrying database in ${RETRY_WAIT / 1000} seconds...`) 48 return promisedTimeout(awaitDatabase, RETRY_WAIT) 49 }) 50 } 51 52 const connect = () => { 53 return r.connect({host: HOST, port: PORT, db: NAME}) 54 .then(conn => { 55 connection = conn 56 return awaitDatabase() 57 }) 58 .catch(err => { 59 if (err instanceof r.Error.ReqlDriverError) { 60 console.warn('Unable to connect to RethinkDB') 61 console.warn(`Retrying in ${RETRY_WAIT / 1000} seconds...`) 62 return promisedTimeout(connect, RETRY_WAIT) 63 } 64 throw err 65 }) 66 } 67 68 const runQuery = query => { 69 return query 70 .run(connection) 71 .catch(err => { 72 console.error(err.message) 73 throw new Error(err.message) 74 }) 75 } 76 77 const queryWithCurrentBlock = query => { 78 return runQuery( 79 r.table('blocks') 80 .orderBy(r.desc('blockNum')) 81 .nth(0)('blockNum') 82 .do(query) 83 ) 84 } 85 86 // Runs a specified query against a database table 87 const queryTable = (table, query, removeCursor = true) => { 88 return query(r.table(table)) 89 .run(connection) 90 .then(cursor => removeCursor ? cursor.toArray() : cursor) 91 .catch(err => { 92 console.error(`Unable to query "${table}" table!`) 93 console.error(err.message) 94 throw new Error(err.message) 95 }) 96 } 97 98 // Use for queries that modify a table, turns error messages into errors 99 const modifyTable = (table, query) => { 100 return queryTable(table, query, false) 101 .then(results => { 102 if (!results) { 103 throw new Error(`Unknown error while attempting to modify "${table}"`) 104 } 105 if (results.errors > 0) { 106 throw new Error(results.first_error) 107 } 108 return results 109 }) 110 } 111 112 // Inserts a document into a table, throwing an error on failure 113 // Accepts an optional validator function, which should have an errors method 114 const insertTable = (table, doc) => { 115 return modifyTable(table, t => t.insert(doc)) 116 .then(results => { 117 if (results.inserted === 0) { 118 throw new Error(`Unknown Error: Unable to insert to ${table}`) 119 } 120 return results 121 }) 122 } 123 124 const updateTable = (table, primary, changes) => { 125 return modifyTable(table, t => { 126 return t.get(primary).update(changes, {returnChanges: true}) 127 }) 128 .then(results => { 129 if (results.replaced === 0 && results.unchanged === 0) { 130 throw new Error(`Unknown Error: Unable to update ${primary}`) 131 } 132 return results 133 }) 134 } 135 136 // Validates a db input based on a schema as promised 137 const validate = (input, schema) => { 138 return Promise.resolve() 139 .then(() => { 140 const validator = jsSchema(schema) 141 if (validator(input)) return input 142 143 const errors = validator.errors(input) 144 if (!errors) throw new Error('Invalid Input: one or more keys forbidden') 145 146 const [ key, message ] = _.entries(errors)[0] 147 throw new Error(`Invalid Input: "${key}" - ${message}`) 148 }) 149 } 150 151 module.exports = { 152 connect, 153 runQuery, 154 queryWithCurrentBlock, 155 queryTable, 156 modifyTable, 157 insertTable, 158 updateTable, 159 validate 160 }