github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/kat/src/clients/database.ts (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import { config } from '../lib/config';
    16  import { ClientEventType, IClientEventListener, IDatabaseProvider, IDBAssetDefinition, IDBAssetInstance, IDBBatch, IDBBlockchainData, IDBMember, IDBPaymentDefinition, IDBPaymentInstance, IStoredSubscriptions } from '../lib/interfaces';
    17  import * as utils from '../lib/utils';
    18  import MongoDBProvider from './db-providers/mongodb';
    19  import NEDBProvider from './db-providers/nedb';
    20  const log = utils.getLogger('handlers/asset-trade.ts');
    21  
    22  let databaseProvider: IDatabaseProvider;
    23  
    24  export const init = async () => {
    25    if (config.mongodb !== undefined) {
    26      databaseProvider = new MongoDBProvider();
    27    } else {
    28      databaseProvider = new NEDBProvider();
    29    }
    30    await databaseProvider.init();
    31  };
    32  
    33  let listeners: IClientEventListener[] = [];
    34  
    35  // COLLECTION AGNOSTIC QUERIES
    36  
    37  export const createCollection = (collectionName: string, indexes: { fields: string[], unique?: boolean }[]) => {
    38    return databaseProvider.createCollection(collectionName, indexes);
    39  };
    40  
    41  // MEMBER QUERIES
    42  
    43  export const retrieveMemberByAddress = (address: string): Promise<IDBMember | null> => {
    44    return databaseProvider.findOne<IDBMember>('members', { address });
    45  };
    46  
    47  export const retrieveMembers = (query: object, skip: number, limit: number): Promise<IDBMember[]> => {
    48    return databaseProvider.find<IDBMember>('members', query, { name: 1 }, skip, limit);
    49  };
    50  
    51  export const upsertMember = async (member: IDBMember) => {
    52    await databaseProvider.updateOne('members', { address: member.address }, { $set: member }, true);
    53    emitEvent('member-registered', member);
    54  };
    55  
    56  // ASSET DEFINITION QUERIES
    57  
    58  export const retrieveAssetDefinitions = (query: object, skip: number, limit: number): Promise<IDBAssetDefinition[]> => {
    59    return databaseProvider.find<IDBAssetDefinition>('asset-definitions', query, { name: 1 }, skip, limit)
    60  };
    61  
    62  export const countAssetDefinitions = (query: object): Promise<number> => {
    63    return databaseProvider.count('asset-definitions', query);
    64  };
    65  
    66  export const retrieveAssetDefinitionByID = (assetDefinitionID: string): Promise<IDBAssetDefinition | null> => {
    67    return databaseProvider.findOne<IDBAssetDefinition>('asset-definitions', { assetDefinitionID });
    68  };
    69  
    70  export const retrieveAssetDefinitionByName = (name: string): Promise<IDBAssetDefinition | null> => {
    71    return databaseProvider.findOne<IDBAssetDefinition>('asset-definitions', { name });
    72  };
    73  
    74  export const upsertAssetDefinition = async (assetDefinition: IDBAssetDefinition) => {
    75    await databaseProvider.updateOne('asset-definitions', { assetDefinitionID: assetDefinition.assetDefinitionID }, { $set: assetDefinition }, true);
    76    if (assetDefinition.submitted !== undefined) {
    77      emitEvent('asset-definition-submitted', assetDefinition);
    78    } else if (assetDefinition.transactionHash !== undefined) {
    79      emitEvent('asset-definition-created', assetDefinition);
    80    }
    81  };
    82  
    83  export const markAssetDefinitionAsConflict = async (assetDefinitionID: string, timestamp: number) => {
    84    await databaseProvider.updateOne('asset-definitions', { assetDefinitionID }, { $set: { timestamp, conflict: true } }, false);
    85    emitEvent('asset-definition-name-conflict', { assetDefinitionID })
    86  };
    87  
    88  // PAYMENT DEFINITION QUERIES
    89  
    90  export const retrievePaymentDefinitions = (query: object, skip: number, limit: number): Promise<IDBPaymentDefinition[]> => {
    91    return databaseProvider.find<IDBPaymentDefinition>('payment-definitions', query, { name: 1 }, skip, limit);
    92  };
    93  
    94  export const countPaymentDefinitions = (query: object): Promise<number> => {
    95    return databaseProvider.count('payment-definitions', query);
    96  };
    97  
    98  export const retrievePaymentDefinitionByID = (paymentDefinitionID: string): Promise<IDBPaymentDefinition | null> => {
    99    return databaseProvider.findOne<IDBPaymentDefinition>('payment-definitions', { paymentDefinitionID });
   100  };
   101  
   102  export const retrievePaymentDefinitionByName = (name: string): Promise<IDBPaymentDefinition | null> => {
   103    return databaseProvider.findOne<IDBPaymentDefinition>('payment-definitions', { name });
   104  };
   105  
   106  export const upsertPaymentDefinition = async (paymentDefinition: IDBPaymentDefinition) => {
   107    await databaseProvider.updateOne('payment-definitions', { paymentDefinitionID: paymentDefinition.paymentDefinitionID }, { $set: paymentDefinition }, true)
   108    if (paymentDefinition.submitted !== undefined) {
   109      emitEvent('payment-definition-submitted', paymentDefinition);
   110    } else if (paymentDefinition.transactionHash !== undefined) {
   111      emitEvent('payment-definition-created', paymentDefinition);
   112    }
   113  };
   114  
   115  export const markPaymentDefinitionAsConflict = async (paymentDefinitionID: string, timestamp: number) => {
   116    await databaseProvider.updateOne('payment-definitions', { paymentDefinitionID }, { $set: { conflict: true, timestamp } }, false);
   117    emitEvent('payment-definition-name-conflict', { paymentDefinitionID })
   118  };
   119  
   120  // ASSET INSTANCE QUERIES
   121  
   122  export const retrieveAssetInstances = (assetDefinitionID: string, query: object, sort: object, skip: number, limit: number): Promise<IDBAssetInstance[]> => {
   123    return databaseProvider.find<IDBAssetInstance>(`asset-instance-${assetDefinitionID}`, query, sort, skip, limit);
   124  };
   125  
   126  export const countAssetInstances = (assetDefinitionID: string, query: object): Promise<number> => {
   127    return databaseProvider.count(`asset-instance-${assetDefinitionID}`, query);
   128  };
   129  
   130  export const retrieveAssetInstanceByID = (assetDefinitionID: string, assetInstanceID: string): Promise<IDBAssetInstance | null> => {
   131    return databaseProvider.findOne<IDBAssetInstance>(`asset-instance-${assetDefinitionID}`, { assetInstanceID });
   132  };
   133  
   134  export const retrieveAssetInstanceByDefinitionIDAndContentHash = (assetDefinitionID: string, contentHash: string): Promise<IDBAssetInstance | null> => {
   135    return databaseProvider.findOne<IDBAssetInstance>(`asset-instance-${assetDefinitionID}`, { contentHash });
   136  };
   137  
   138  export const upsertAssetInstance = async (assetInstance: IDBAssetInstance) => {
   139    await databaseProvider.updateOne(`asset-instance-${assetInstance.assetDefinitionID}`, { assetInstanceID: assetInstance.assetInstanceID }, { $set: assetInstance }, true);
   140    if (assetInstance.submitted !== undefined) {
   141      emitEvent('asset-instance-submitted', assetInstance);
   142    } else if (assetInstance.transactionHash !== undefined) {
   143      emitEvent('asset-instance-created', assetInstance);
   144    }
   145  };
   146  
   147  export const setAssetInstanceReceipt = async (assetDefinitionID: string, assetInstanceID: string, receipt: string) => {
   148    await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID }, { $set: { receipt } }, true);
   149  };
   150  
   151  export const setAssetInstancePrivateContent = async (assetDefinitionID: string, assetInstanceID: string, content: object | undefined, filename: string | undefined) => {
   152    await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID }, { $set: { content, filename } }, true);
   153    log.info(`Emitting event for private-asset-instance-content-stored`);
   154    emitEvent('private-asset-instance-content-stored', { assetDefinitionID, assetInstanceID, content, filename });
   155  };
   156  
   157  export const markAssetInstanceAsConflict = async (assetDefinitionID: string, assetInstanceID: string, timestamp: number) => {
   158    await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID }, { $set: { conflict: true, timestamp } }, false);
   159    emitEvent('asset-instance-content-conflict', { assetDefinitionID, assetInstanceID });
   160  };
   161  
   162  export const setSubmittedAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string, submitted: number, batchID?: string) => {
   163    await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID },
   164      {
   165        $set: {
   166          [`properties.${author}.${key}.value`]: value,
   167          [`properties.${author}.${key}.submitted`]: submitted,
   168          [`properties.${author}.${key}.batchID`]: batchID,
   169        }
   170      }, false);
   171    emitEvent('asset-instance-property-submitted', { assetDefinitionID, assetInstanceID, key, value, submitted, batchID });
   172  };
   173  
   174  export const setAssetInstancePropertyReceipt = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, receipt: string) => {
   175    await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID },
   176      {
   177        $set: {
   178          [`properties.${author}.${key}.receipt`]: receipt
   179        }
   180      }, false);
   181  };
   182  
   183  export const setConfirmedAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string, timestamp: number, { blockNumber, transactionHash }: IDBBlockchainData) => {
   184    await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID },
   185      {
   186        $set: {
   187          [`properties.${author}.${key}.value`]: value,
   188          [`properties.${author}.${key}.history.${timestamp}`]: { value, timestamp, blockNumber, transactionHash }
   189        }
   190      }, false);
   191    emitEvent('asset-instance-property-set', { assetDefinitionID, assetInstanceID, author, key, value, timestamp, blockNumber, transactionHash });
   192  };
   193  
   194  // PAYMENT INSTANCE QUERIES
   195  
   196  export const retrievePaymentInstances = (query: object, sort: object, skip: number, limit: number): Promise<IDBPaymentInstance[]> => {
   197    return databaseProvider.find<IDBPaymentInstance>('payment-instances', query, sort, skip, limit);
   198  };
   199  
   200  export const countPaymentInstances = (query: object): Promise<number> => {
   201    return databaseProvider.count('payment-instances', query);
   202  };
   203  
   204  export const retrievePaymentInstanceByID = (paymentInstanceID: string): Promise<IDBPaymentInstance | null> => {
   205    return databaseProvider.findOne<IDBPaymentInstance>('payment-instances', { paymentInstanceID });
   206  };
   207  
   208  export const upsertPaymentInstance = async (paymentInstance: IDBPaymentInstance) => {
   209    await databaseProvider.updateOne('payment-instances', { paymentInstanceID: paymentInstance.paymentInstanceID }, { $set: paymentInstance }, true);
   210    if (paymentInstance.submitted !== undefined) {
   211      emitEvent('payment-instance-submitted', paymentInstance);
   212    } else {
   213      emitEvent('payment-instance-created', paymentInstance);
   214    }
   215  };
   216  
   217  
   218  // BATCH QUERIES
   219  
   220  export const retrieveBatches = (query: object, skip: number, limit: number, sort: {[f: string]: number} = {}): Promise<IDBBatch[]> => {
   221    return databaseProvider.find<IDBBatch>('batches', query, sort, skip, limit);
   222  };
   223  
   224  export const retrieveBatchByID = (batchID: string): Promise<IDBBatch | null> => {
   225    return databaseProvider.findOne<IDBBatch>('batches', { batchID });
   226  };
   227  
   228  export const retrieveBatchByHash = (batchHash: string): Promise<IDBBatch | null> => {
   229    return databaseProvider.findOne<IDBBatch>('batches', { batchHash });
   230  };
   231  
   232  export const upsertBatch = async (batch: IDBBatch) => {
   233    await databaseProvider.updateOne('batches', { batchID: batch.batchID }, { $set: batch }, true);
   234  };
   235  
   236  // SUBSCRIPTION MANAGEMENT
   237  
   238  export const retrieveSubscriptions = (): Promise<IStoredSubscriptions | null> => {
   239    return databaseProvider.findOne<IStoredSubscriptions>('state', { key: 'subscriptions' });
   240  };
   241  
   242  export const upsertSubscriptions = (subscriptions: IStoredSubscriptions): Promise<void> => {
   243    return databaseProvider.updateOne('state', { key: 'subscriptions' }, { $set: subscriptions }, true);
   244  };
   245  
   246  // EVENT HANDLING
   247  
   248  export const addListener = (listener: IClientEventListener) => {
   249    listeners.push(listener);
   250  };
   251  
   252  export const removeListener = (listener: IClientEventListener) => {
   253    listeners = listeners.filter(entry => entry != listener);
   254  };
   255  
   256  const emitEvent = (eventType: ClientEventType, content: object) => {
   257    for (const listener of listeners) {
   258      listener(eventType, content);
   259    }
   260  };
   261  
   262  export const shutDown = () => {
   263    databaseProvider.shutDown();
   264  };