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  }