github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/server/db/records.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 21 const db = require('./') 22 23 /* Helpers */ 24 25 const getAttribute = attr => obj => obj(attr) 26 const getRecordId = getAttribute('recordId') 27 const getRecordType = getAttribute('recordType') 28 const getProperties = getAttribute('properties') 29 const getName = getAttribute('name') 30 const getFinal = getAttribute('final') 31 const getPublicKey = getAttribute('publicKey') 32 const getDataType = getAttribute('dataType') 33 const getReporters = getAttribute('reporters') 34 const getAuthorization = getAttribute('authorized') 35 const getReportedValues = getAttribute('reportedValues') 36 const getStatus = getAttribute('status') 37 38 const getAssociatedAgentId = role => record => record(role).nth(-1)('agentId') 39 const getOwnerId = getAssociatedAgentId('owners') 40 const getCustodianId = getAssociatedAgentId('custodians') 41 42 const getAssociatedAgents = 43 role => record => record(role).orderBy(r.desc('timestamp')) 44 const getOwners = getAssociatedAgents('owners') 45 const getCustodians = getAssociatedAgents('custodians') 46 47 const hasAttribute = getAttr => attr => obj => r.eq(attr, getAttr(obj)) 48 const hasName = hasAttribute(getName) 49 const hasRecordId = hasAttribute(getRecordId) 50 const hasPublicKey = hasAttribute(getPublicKey) 51 const hasStatus = hasAttribute(getStatus) 52 53 const hasBlock = block => obj => { 54 return r.and( 55 obj('startBlockNum').le(block), 56 obj('endBlockNum').gt(block) 57 ) 58 } 59 60 const getTable = (tableName, block) => { 61 return r.table(tableName).filter(hasBlock(block)) 62 } 63 64 const getProposals = recordId => receivingAgent => block => { 65 return getTable('proposals', block) 66 .filter(hasRecordId(recordId)) 67 .filter(hasStatus('OPEN')) 68 .pluck('receivingAgent', 'issuingAgent', 'role', 'properties') 69 .coerceTo('array') 70 } 71 72 const findRecord = recordId => block => { 73 return getTable('records', block) 74 .filter(hasRecordId(recordId)) 75 .nth(0) 76 } 77 78 const findProperty = recordId => block => propertyName => { 79 return getTable('properties', block) 80 .filter(hasRecordId(recordId)) 81 .filter(hasName(propertyName)) 82 .nth(0) 83 } 84 85 const getReporter = publicKey => block => { 86 return getTable('agents', block) 87 .filter(hasPublicKey(publicKey)) 88 .pluck('name', 'publicKey') 89 .coerceTo('array') 90 .do(results => { 91 return r.branch( 92 results.isEmpty(), 93 { name: 'BAD DATA', publicKey: 'BAD DATA' }, 94 results(0)) 95 }) 96 } 97 98 const findReportedValues = 99 recordId => propertyName => dataType => reporterKeys => block => { 100 return getTable('propertyPages', block) 101 .filter(hasRecordId(recordId)) 102 .filter(hasName(propertyName)) 103 .concatMap(getReportedValues) 104 .map(getUpdate(dataType)(reporterKeys)(block)) 105 .orderBy(r.desc('timestamp')) 106 .coerceTo('array') 107 } 108 109 const getValue = dataType => value => { 110 return r.branch( 111 r.eq(dataType, 'BOOLEAN'), value('booleanValue'), 112 r.eq(dataType, 'NUMBER'), value('numberValue'), 113 r.eq(dataType, 'STRING'), value('stringValue'), 114 r.eq(dataType, 'BYTES'), value('bytesValue'), 115 r.eq(dataType, 'LOCATION'), value('locationValue'), 116 r.eq(dataType, 'ENUM'), value('enumValue'), 117 r.eq(dataType, 'STRUCT'), value('structValue'), 118 value('bytesValue') // if dataType is unknown, use bytesValue 119 ) 120 } 121 122 const getUpdate = dataType => reporterKeys => block => value => { 123 return r.expr({ 124 'value': getValue(dataType)(value), 125 'timestamp': value('timestamp'), 126 'reporter': getReporter(reporterKeys.map(getPublicKey).nth(value('reporterIndex')))(block) 127 }) 128 } 129 130 const getTypeProperties = record => block => { 131 return getTable('recordTypes', block) 132 .filter(hasName(getRecordType(record))) 133 .map(getProperties) 134 .map(getName) 135 .nth(0) 136 .map(findProperty(getRecordId(record))(block)) 137 .coerceTo('array') 138 } 139 140 const getPropertyValues = recordId => block => property => { 141 return getReporters(property).do(reporterKeys => { 142 return getDataType(property).do(dataType => { 143 return r.expr({ 144 'name': getName(property), 145 'dataType': dataType, 146 fixed: property('fixed'), 147 numberExponent: property('numberExponent'), 148 unit: property('unit'), 149 'reporterKeys': reporterKeys, 150 'values': findReportedValues(recordId)(getName(property))(dataType)(reporterKeys)(block) 151 }) 152 }) 153 }) 154 } 155 156 const getCurrentValue = propertyValue => { 157 return r.branch( 158 propertyValue('values').count().eq(0), 159 null, 160 propertyValue('values').nth(0) 161 ) 162 } 163 164 const makePropertiesEntry = propertyValues => { 165 return propertyValues 166 .map(entry => { 167 return r.object( 168 getName(entry), 169 entry('values').pluck('value', 'timestamp') 170 ) 171 }) 172 .reduce((left, right) => left.merge(right)) 173 .default({}) 174 } 175 176 const getAuthorizedReporterKeys = propertyValue => { 177 return propertyValue('reporterKeys') 178 .filter(getAuthorization) 179 .map(getPublicKey) 180 .coerceTo('array') 181 } 182 183 /* Queries */ 184 185 const fetchPropertyQuery = (recordId, name) => block => { 186 return findProperty(recordId)(block)(name).do(property => { 187 return getPropertyValues(recordId)(block)(property).do(propertyValues => { 188 return r.expr({ 189 'name': name, 190 'recordId': recordId, 191 'reporters': getAuthorizedReporterKeys(propertyValues), 192 'dataType': propertyValues('dataType'), 193 'value': getCurrentValue(propertyValues), 194 'updates': propertyValues('values') 195 }) 196 }) 197 }) 198 } 199 200 const _loadRecord = (block, authedKey) => (record) => { 201 let recordId = getRecordId(record) 202 return getTypeProperties(record)(block) 203 .map(getPropertyValues(recordId)(block)).do(propertyValues => { 204 return r.expr({ 205 'recordId': getRecordId(record), 206 'owner': getOwnerId(record), 207 'custodian': getCustodianId(record), 208 'final': getFinal(record), 209 'properties': propertyValues 210 .map(propertyValue => r.expr({ 211 'name': getName(propertyValue), 212 'type': getDataType(propertyValue), 213 'value': getCurrentValue(propertyValue).do( 214 value => r.branch( 215 value, 216 value('value'), 217 value 218 ) 219 ), 220 'reporters': getAuthorizedReporterKeys(propertyValue), 221 }).merge(r.branch( 222 getDataType(propertyValue).eq('NUMBER'), 223 { numberExponent: propertyValue('numberExponent') }, 224 {} 225 )).merge(r.branch( 226 propertyValue('fixed'), 227 { fixed: propertyValue('fixed') }, 228 {} 229 )).merge(r.branch( 230 propertyValue('unit').ne(''), 231 { unit: propertyValue('unit') }, 232 {} 233 ))), 234 'updates': r.expr({ 235 'owners': getOwners(record), 236 'custodians': getCustodians(record), 237 'properties': makePropertiesEntry(propertyValues) 238 }), 239 'proposals': getProposals(recordId)(authedKey)(block) 240 }) 241 }) 242 } 243 244 const fetchRecordQuery = (recordId, authedKey) => block => { 245 return findRecord(recordId)(block).do(_loadRecord(block, authedKey)) 246 } 247 248 const listRecordsQuery = (authedKey, filterQuery) => block => { 249 return getTable('records', block) 250 .filter(filterQuery) 251 .map(_loadRecord(block, authedKey)) 252 .coerceTo('array') 253 } 254 255 /* Exported functions */ 256 257 const fetchProperty = (recordId, propertyName) => { 258 return db.queryWithCurrentBlock(fetchPropertyQuery(recordId, propertyName)) 259 } 260 261 const fetchRecord = (recordId, authedKey) => { 262 return db.queryWithCurrentBlock(fetchRecordQuery(recordId, authedKey)) 263 } 264 265 const listRecords = (authedKey, filterQuery) => { 266 return db.queryWithCurrentBlock(listRecordsQuery(authedKey, filterQuery)) 267 } 268 269 module.exports = { 270 fetchProperty, 271 fetchRecord, 272 listRecords 273 }