├── .babelrc ├── .ci ├── travis-before-install.sh ├── travis-before-script.sh ├── travis-install.sh └── travis_script.sh ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── compose ├── Dockerfile └── tendermint │ └── tmdata │ └── config.toml ├── coverage.lcov ├── dist ├── browser │ ├── bigchaindb-orm.amd.min.js │ ├── bigchaindb-orm.amd.min.js.map │ ├── bigchaindb-orm.cjs.min.js │ ├── bigchaindb-orm.cjs.min.js.map │ ├── bigchaindb-orm.cjs2.min.js │ ├── bigchaindb-orm.cjs2.min.js.map │ ├── bigchaindb-orm.umd.min.js │ ├── bigchaindb-orm.umd.min.js.map │ ├── bigchaindb-orm.window.min.js │ └── bigchaindb-orm.window.min.js.map └── node │ ├── connection.js │ ├── index.js │ └── ormobject.js ├── docker-compose.yml ├── media ├── repo-banner.sketch └── repo-banner@2x.png ├── package.json ├── plugins └── add-vendors-plugin.js ├── src ├── connection.js ├── index.js └── ormobject.js ├── test └── test_orm.js ├── webpack.common.js ├── webpack.config.js ├── webpack.development.js ├── webpack.parts.js └── webpack.production.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env" 4 | ], 5 | "plugins": [ 6 | "transform-export-extensions", 7 | "transform-object-assign", 8 | "transform-object-rest-spread", 9 | ["transform-runtime", { "polyfill": false, "regenerator": true }] 10 | ], 11 | "sourceMaps": true 12 | } -------------------------------------------------------------------------------- /.ci/travis-before-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo apt-get update 4 | sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce 5 | 6 | sudo rm /usr/local/bin/docker-compose 7 | curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose 8 | chmod +x docker-compose 9 | sudo mv docker-compose /usr/local/bin 10 | -------------------------------------------------------------------------------- /.ci/travis-before-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -x 4 | 5 | docker-compose up -d bigchaindb 6 | 7 | npm install 8 | gem install cowsay 9 | npm install -g codecov -------------------------------------------------------------------------------- /.ci/travis-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -x 4 | 5 | docker-compose build --no-cache bigchaindb 6 | -------------------------------------------------------------------------------- /.ci/travis_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -x 4 | 5 | docker-compose run --rm js-driver-orm npm run test 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "ascribe" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | package-lock.json 6 | 7 | # testing 8 | /coverage 9 | 10 | # production 11 | /build 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | .nyc_output -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigchaindb/js-driver-orm/73ed441e210b823c7c6442fcc0728ca75726e2f6/.npmignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: node_js 7 | 8 | node_js: 9 | - 7 10 | - 8 11 | - 9 12 | - 10 13 | 14 | cache: 15 | directories: 16 | - node_modules 17 | 18 | env: 19 | global: 20 | - DOCKER_COMPOSE_VERSION=1.19.0 21 | 22 | before_install: 23 | - .ci/travis-before-install.sh 24 | 25 | install: 26 | - .ci/travis-install.sh 27 | 28 | before_script: 29 | - .ci/travis-before-script.sh 30 | - gem install cowsay 31 | - npm install -g codecov 32 | 33 | script: npm run test 34 | 35 | notifications: 36 | email: false 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [![js-bigchaindb-driver](media/repo-banner@2x.png)](https://www.bigchaindb.com) 2 | 3 | > A CRAB-based ORM for BigchainDB. 4 | 5 | [![npm](https://img.shields.io/npm/v/bigchaindb-orm.svg)](https://www.npmjs.com/package/bigchaindb-orm) 6 | [![codecov](https://codecov.io/gh/bigchaindb/js-driver-orm/branch/master/graph/badge.svg)](https://codecov.io/gh/bigchaindb/js-driver-orm) 7 | [![js ascribe](https://img.shields.io/badge/js-ascribe-39BA91.svg)](https://github.com/ascribe/javascript) 8 | [![Build Status](https://travis-ci.org/bigchaindb/js-driver-orm.svg?branch=master)](https://travis-ci.org/bigchaindb/js-driver-orm) 9 | [![Greenkeeper badge](https://badges.greenkeeper.io/bigchaindb/js-driver-orm.svg)](https://greenkeeper.io/) 10 | 11 | CRAB is the CRUD model in databases applied to blockchains: 12 | 13 | | Database | Blockchain | 14 | | ---------- | ------------ | 15 | | **C**reate | **C**reate | 16 | | **R**ead | **R**etrieve | 17 | | **U**pdate | **A**ppend | 18 | | **D**elete | **B**urn | 19 | 20 | ## Breaking changes 21 | 22 | - **Version 3.x ** of js-driver-orm changes namespacing of data storage and retrieval. Using new version with old data will brake it! 23 | 24 | ## Table of Contents 25 | 26 | - [Setup](#setup) 27 | - [Usage](#usage) 28 | - [Examples](#examples) 29 | - [Create an asset](#example-create-an-asset) 30 | - [Retrieve assets](#example-retrieve-assets) 31 | - [Append a transaction](#example-append-a-transaction) 32 | - [Burn an asset](#example-burn-an-asset) 33 | - [License](#license) 34 | 35 | ## Setup 36 | 37 | ```bash 38 | $ npm install bigchaindb-orm 39 | ``` 40 | 41 | ## Usage 42 | 43 | ```javascript 44 | // import bigchaindb-orm 45 | import Orm from 'bigchaindb-orm' 46 | // connect to BigchainDB 47 | const bdbOrm = new Orm( 48 | "https://test.bigchaindb.com/api/v1/", 49 | { 50 | app_id: "Get one from testnet.bigchaindb.com", 51 | app_key: "Get one from testnet.bigchaindb.com" 52 | } 53 | ) 54 | // define(,) 55 | // : represents the name of model you want to store 56 | // : any information you want to pass about the model (can be string or object) 57 | // note: cannot be changed once set! 58 | bdbOrm.define("myModel", "https://schema.org/v1/myModel") 59 | // create a public and private key for Alice 60 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 61 | ``` 62 | 63 | ## Examples 64 | 65 | All examples need bdbOrm initialized as described in usage 66 | 67 | ### Example: Create an asset 68 | 69 | ```javascript 70 | // from the defined models in our bdbOrm we create an asset with Alice as owner 71 | bdbOrm.models.myModel 72 | .create({ 73 | keypair: aliceKeypair, 74 | data: { key: 'dataValue' } 75 | }) 76 | .then(asset => { 77 | /* 78 | asset is an object with all our data and functions 79 | asset.id equals the id of the asset 80 | asset.data is data of the last (unspent) transaction 81 | asset.transactionHistory gives the full raw transaction history 82 | Note: Raw transaction history has different object structure then 83 | asset. You can find specific data change in metadata property. 84 | */ 85 | console.log(asset.id) 86 | }) 87 | ``` 88 | 89 | ### Example: Retrieve assets 90 | 91 | ```javascript 92 | // get all objects with retrieve() 93 | // or get a specific object with retrieve(object.id) 94 | bdbOrm.models.myModel 95 | .retrieve() 96 | .then(assets => { 97 | // assets is an array of myModel 98 | console.log(assets.map(asset => asset.id)) 99 | }) 100 | ``` 101 | 102 | ### Example: Append a transaction 103 | > "Update" (Database) => "Append" (Blockchain) 104 | 105 | ```javascript 106 | // create an asset with Alice as owner 107 | bdbOrm.models.myModel 108 | .create({ 109 | keypair: aliceKeypair, 110 | data: { key: 'dataValue' } 111 | }) 112 | .then(asset => { 113 | // lets append update the data of our asset 114 | // since we use a blockchain, we can only append 115 | return asset.append({ 116 | toPublicKey: aliceKeypair.publicKey, 117 | keypair: aliceKeypair, 118 | data: { key: 'updatedValue' } 119 | }) 120 | }) 121 | .then(updatedAsset => { 122 | // updatedAsset contains the last (unspent) state 123 | // of our asset so any actions 124 | // need to be done to updatedAsset 125 | console.log(updatedAsset.data) 126 | }) 127 | ``` 128 | 129 | ### Example: Burn an asset 130 | > "Delete" (Database) => "Burn" (Blockchain) 131 | 132 | :exclamation:Burning assets does not delete them! It moves control of asset from users keypair to unfulfillable one.:exclamation: 133 | 134 | ```javascript 135 | // create an asset with Alice as owner 136 | bdbOrm.models.myModel 137 | .create({ 138 | keypair: aliceKeypair, 139 | data: { key: 'dataValue' } 140 | }) 141 | .then(asset => { 142 | // lets burn the asset by assigning to a random keypair 143 | // since will not store the private key it's infeasible to redeem the asset 144 | return asset.burn({ 145 | keypair: aliceKeypair 146 | }) 147 | }) 148 | .then(burnedAsset => { 149 | // asset is now tagged as "burned" 150 | console.log(burnedAsset.data) 151 | }) 152 | ``` 153 | 154 | ## License 155 | 156 | ``` 157 | Copyright 2018 BigchainDB GmbH 158 | 159 | Licensed under the Apache License, Version 2.0 (the "License"); 160 | you may not use this file except in compliance with the License. 161 | You may obtain a copy of the License at 162 | 163 | http://www.apache.org/licenses/LICENSE-2.0 164 | 165 | Unless required by applicable law or agreed to in writing, software 166 | distributed under the License is distributed on an "AS IS" BASIS, 167 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 168 | See the License for the specific language governing permissions and 169 | limitations under the License. 170 | ``` 171 | -------------------------------------------------------------------------------- /compose/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | RUN apt-get update && apt-get install -y vim 4 | 5 | RUN mkdir -p /usr/src/app 6 | WORKDIR /usr/src/app 7 | 8 | RUN pip install --upgrade pip ipdb ipython 9 | 10 | COPY . /usr/src/app/ 11 | 12 | RUN pip install git+https://github.com/bigchaindb/bigchaindb.git 13 | -------------------------------------------------------------------------------- /compose/tendermint/tmdata/config.toml: -------------------------------------------------------------------------------- 1 | # This is a TOML config file. 2 | # For more information, see https://github.com/toml-lang/toml 3 | 4 | proxy_app = "tcp://bigchaindb:46658" 5 | moniker = "anonymous" 6 | fast_sync = true 7 | db_backend = "leveldb" 8 | log_level = "state:debug,*:error" 9 | 10 | [consensus] 11 | create_empty_blocks = false 12 | 13 | [rpc] 14 | laddr = "tcp://0.0.0.0:46657" 15 | 16 | [p2p] 17 | laddr = "tcp://0.0.0.0:46656" 18 | -------------------------------------------------------------------------------- /coverage.lcov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigchaindb/js-driver-orm/73ed441e210b823c7c6442fcc0728ca75726e2f6/coverage.lcov -------------------------------------------------------------------------------- /dist/node/connection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _typeof2 = require('babel-runtime/helpers/typeof'); 8 | 9 | var _typeof3 = _interopRequireDefault(_typeof2); 10 | 11 | var _extends2 = require('babel-runtime/helpers/extends'); 12 | 13 | var _extends3 = _interopRequireDefault(_extends2); 14 | 15 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 16 | 17 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 18 | 19 | var _createClass2 = require('babel-runtime/helpers/createClass'); 20 | 21 | var _createClass3 = _interopRequireDefault(_createClass2); 22 | 23 | var _bigchaindbDriver = require('bigchaindb-driver'); 24 | 25 | var driver = _interopRequireWildcard(_bigchaindbDriver); 26 | 27 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 28 | 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 30 | 31 | // eslint-disable-line import/no-namespace 32 | 33 | var Connection = function () { 34 | function Connection(path) { 35 | var headers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 36 | (0, _classCallCheck3.default)(this, Connection); 37 | 38 | this.path = path; 39 | this.headers = (0, _extends3.default)({}, headers); 40 | this.conn = new driver.Connection(path, headers); 41 | } 42 | 43 | (0, _createClass3.default)(Connection, [{ 44 | key: 'getAssetId', 45 | value: function getAssetId(tx) { 46 | // eslint-disable-line class-methods-use-this 47 | return tx.operation === 'CREATE' ? tx.id : tx.asset.id; 48 | } 49 | }, { 50 | key: 'getTransaction', 51 | value: function getTransaction(transactionId) { 52 | return this.conn.getTransaction(transactionId); 53 | } 54 | }, { 55 | key: 'listTransactions', 56 | value: function listTransactions(assetId, operation) { 57 | return this.conn.listTransactions(assetId, operation); 58 | } 59 | }, { 60 | key: 'searchAssets', 61 | value: function searchAssets(text) { 62 | return this.conn.searchAssets(text); 63 | } 64 | }, { 65 | key: 'createTransaction', 66 | value: function createTransaction(publicKey, privateKey, payload, metadata) { 67 | try { 68 | // Create a transation 69 | var tx = driver.Transaction.makeCreateTransaction(payload, metadata, [driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(publicKey))], publicKey); 70 | 71 | // sign/fulfill the transaction 72 | var txSigned = driver.Transaction.signTransaction(tx, privateKey); 73 | return this.conn.postTransactionCommit(txSigned).then(function () { 74 | return txSigned; 75 | }); 76 | } catch (error) { 77 | return Promise.reject(error); 78 | } 79 | } 80 | }, { 81 | key: 'transferTransaction', 82 | value: function transferTransaction(tx, fromPublicKey, fromPrivateKey, toPublicKey, metadata) { 83 | try { 84 | var txTransfer = driver.Transaction.makeTransferTransaction([{ 'tx': tx, 'output_index': 0 }], [driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(toPublicKey))], metadata); 85 | var txTransferSigned = driver.Transaction.signTransaction(txTransfer, fromPrivateKey); 86 | // send it off to BigchainDB 87 | return this.conn.postTransactionCommit(txTransferSigned).then(function () { 88 | return txTransferSigned; 89 | }); 90 | } catch (error) { 91 | return Promise.reject(error); 92 | } 93 | } 94 | }, { 95 | key: 'getSortedTransactions', 96 | value: function getSortedTransactions(assetId) { 97 | return this.conn.listTransactions(assetId).then(function (txList) { 98 | if (txList.length <= 1) { 99 | return txList; 100 | } 101 | var inputTransactions = []; 102 | txList.forEach(function (tx) { 103 | return tx.inputs.forEach(function (input) { 104 | if (input.fulfills) { 105 | inputTransactions.push(input.fulfills.transaction_id); 106 | } 107 | }); 108 | }); 109 | var unspents = txList.filter(function (tx) { 110 | return inputTransactions.indexOf(tx.id) === -1; 111 | }); 112 | if (unspents.length) { 113 | var _ret = function () { 114 | var tipTransaction = unspents[0]; 115 | var tipTransactionId = tipTransaction.inputs[0].fulfills.transaction_id; 116 | var sortedTxList = []; 117 | while (true) { 118 | // eslint-disable-line no-constant-condition 119 | sortedTxList.push(tipTransaction); 120 | try { 121 | tipTransactionId = tipTransaction.inputs[0].fulfills.transaction_id; 122 | } catch (e) { 123 | break; 124 | } 125 | if (!tipTransactionId) { 126 | break; 127 | } 128 | tipTransaction = txList.filter(function (tx) { 129 | return (// eslint-disable-line no-loop-func, prefer-destructuring 130 | tx.id === tipTransactionId 131 | ); 132 | })[0]; 133 | } 134 | return { 135 | v: sortedTxList.reverse() 136 | }; 137 | }(); 138 | 139 | if ((typeof _ret === 'undefined' ? 'undefined' : (0, _typeof3.default)(_ret)) === "object") return _ret.v; 140 | } else { 141 | console.error('something went wrong while sorting transactions', txList, inputTransactions); 142 | } 143 | return txList; 144 | }); 145 | } 146 | }]); 147 | return Connection; 148 | }(); 149 | 150 | exports.default = Connection; -------------------------------------------------------------------------------- /dist/node/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 8 | 9 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 10 | 11 | var _createClass2 = require('babel-runtime/helpers/createClass'); 12 | 13 | var _createClass3 = _interopRequireDefault(_createClass2); 14 | 15 | var _bigchaindbDriver = require('bigchaindb-driver'); 16 | 17 | var driver = _interopRequireWildcard(_bigchaindbDriver); 18 | 19 | var _connection = require('./connection'); 20 | 21 | var _connection2 = _interopRequireDefault(_connection); 22 | 23 | var _ormobject = require('./ormobject'); 24 | 25 | var _ormobject2 = _interopRequireDefault(_ormobject); 26 | 27 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 28 | 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 30 | 31 | var Orm = function () { 32 | function Orm(connectionUrl, headers) { 33 | (0, _classCallCheck3.default)(this, Orm); 34 | 35 | this.connection = new _connection2.default(connectionUrl, headers); 36 | if (headers && headers.app_id !== undefined && headers.app_id !== '') { 37 | this.appId = headers.app_id; 38 | } else { 39 | this.appId = 'global'; 40 | } 41 | this.models = []; 42 | this.driver = driver; 43 | } 44 | 45 | (0, _createClass3.default)(Orm, [{ 46 | key: 'define', 47 | value: function define(modelName, modelSchema) { 48 | this.models[modelName] = new _ormobject2.default(modelName, modelSchema, this.connection, this.appId); 49 | } 50 | }]); 51 | return Orm; 52 | }(); // eslint-disable-next-line import/no-namespace 53 | 54 | 55 | exports.default = Orm; -------------------------------------------------------------------------------- /dist/node/ormobject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); 8 | 9 | var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); 10 | 11 | var _extends2 = require('babel-runtime/helpers/extends'); 12 | 13 | var _extends3 = _interopRequireDefault(_extends2); 14 | 15 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 16 | 17 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 18 | 19 | var _createClass2 = require('babel-runtime/helpers/createClass'); 20 | 21 | var _createClass3 = _interopRequireDefault(_createClass2); 22 | 23 | var _v = require('uuid/v4'); 24 | 25 | var _v2 = _interopRequireDefault(_v); 26 | 27 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 28 | 29 | // The likelihood to generate a vanity address that is 11 times "Burn" is extremely low: 30 | // - https://en.bitcoin.it/wiki/Vanitygen#Use_of_vanitygen_to_try_to_attack_addresses 31 | var BURN_ADDRESS = 'BurnBurnBurnBurnBurnBurnBurnBurnBurnBurnBurn'; 32 | 33 | var OrmObject = function () { 34 | function OrmObject(modelName, modelSchema, connection) { 35 | var appId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'global'; 36 | var transactionList = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; 37 | (0, _classCallCheck3.default)(this, OrmObject); 38 | 39 | this._name = modelName; 40 | this._schema = modelSchema; 41 | this._connection = connection; 42 | this._appId = appId; 43 | if (transactionList.length) { 44 | this.transactionHistory = transactionList; 45 | this.id = transactionList[0].asset.data.id; 46 | this.data = _extends3.default.apply(undefined, [{}].concat((0, _toConsumableArray3.default)(transactionList.map(function (tx) { 47 | return tx.metadata; 48 | })))); 49 | } 50 | } 51 | 52 | (0, _createClass3.default)(OrmObject, [{ 53 | key: 'retrieve', 54 | value: function retrieve() { 55 | var _this = this; 56 | 57 | var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; 58 | 59 | var query = void 0; 60 | if (input !== undefined && input !== '') { 61 | query = '"' + input + '"'; 62 | } else { 63 | query = '"id:' + this._appId + ':' + this._name + ':"'; 64 | } 65 | return this._connection.searchAssets(query).then(function (assets) { 66 | return Promise.all(assets.map(function (asset) { 67 | return _this._connection.getSortedTransactions(asset.id).then(function (txList) { 68 | return new OrmObject(_this._name, _this._schema, _this._connection, _this._appId, txList); 69 | }); 70 | })); 71 | }); 72 | } 73 | }, { 74 | key: 'create', 75 | value: function create(inputs) { 76 | var _this2 = this; 77 | 78 | var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : (0, _v2.default)(); 79 | 80 | if (inputs === undefined) { 81 | console.error('inputs missing'); 82 | } 83 | var assetPayload = { 84 | 'schema': this._schema, 85 | 'id': 'id:' + this._appId + ':' + this._name + ':' + id 86 | }; 87 | return this._connection.createTransaction(inputs.keypair.publicKey, inputs.keypair.privateKey, assetPayload, inputs.data).then(function (tx) { 88 | return Promise.resolve(_this2._connection.getSortedTransactions(tx.id).then(function (txList) { 89 | return new OrmObject(_this2._name, _this2._schema, _this2._connection, _this2._appId, txList); 90 | })); 91 | }); 92 | } 93 | }, { 94 | key: 'append', 95 | value: function append(inputs) { 96 | var _this3 = this; 97 | 98 | if (inputs === undefined) { 99 | console.error('inputs missing'); 100 | } 101 | return this._connection.transferTransaction(this.transactionHistory[this.transactionHistory.length - 1], inputs.keypair.publicKey, inputs.keypair.privateKey, inputs.toPublicKey, inputs.data).then(function () { 102 | return Promise.resolve(_this3._connection.getSortedTransactions(_this3.transactionHistory[0].id).then(function (txList) { 103 | return new OrmObject(_this3._name, _this3._schema, _this3._connection, _this3._appId, txList); 104 | })); 105 | }); 106 | } 107 | }, { 108 | key: 'burn', 109 | value: function burn(inputs) { 110 | var _this4 = this; 111 | 112 | if (inputs === undefined) { 113 | console.error('inputs missing'); 114 | } 115 | 116 | return this._connection.transferTransaction(this.transactionHistory[this.transactionHistory.length - 1], inputs.keypair.publicKey, inputs.keypair.privateKey, BURN_ADDRESS, { status: 'BURNED' }).then(function () { 117 | return Promise.resolve(_this4._connection.getSortedTransactions(_this4.transactionHistory[0].id).then(function (txList) { 118 | return new OrmObject(_this4._name, _this4._schema, _this4._connection, _this4._appId, txList); 119 | })); 120 | }); 121 | } 122 | }]); 123 | return OrmObject; 124 | }(); 125 | 126 | exports.default = OrmObject; -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | mongodb: 5 | image: mongo:3.6 6 | ports: 7 | - "27017" 8 | command: mongod 9 | bigchaindb: 10 | depends_on: 11 | - mongodb 12 | - tendermint 13 | image: bigchaindb/bigchaindb:master 14 | environment: 15 | BIGCHAINDB_DATABASE_HOST: mongodb 16 | BIGCHAINDB_DATABASE_PORT: 27017 17 | BIGCHAINDB_SERVER_BIND: 0.0.0.0:9984 18 | BIGCHAINDB_WSSERVER_HOST: 0.0.0.0 19 | BIGCHAINDB_TENDERMINT_HOST: tendermint 20 | BIGCHAINDB_TENDERMINT_PORT: 26657 21 | ports: 22 | - "9984:9984" 23 | - "9985:9985" 24 | - "26658" 25 | healthcheck: 26 | test: ["CMD", "bash", "-c", "curl http://bigchaindb:9984 && curl http://tendermint:26657/abci_query"] 27 | interval: 3s 28 | timeout: 5s 29 | retries: 3 30 | command: -l DEBUG start 31 | tendermint: 32 | image: tendermint/tendermint:0.22.8 33 | # volumes: 34 | # - ./tmdata:/tendermint 35 | entrypoint: '' 36 | ports: 37 | - "26656" 38 | - "26657" 39 | command: sh -c "tendermint init && tendermint node --consensus.create_empty_blocks=false --proxy_app=tcp://bigchaindb:26658" 40 | -------------------------------------------------------------------------------- /media/repo-banner.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigchaindb/js-driver-orm/73ed441e210b823c7c6442fcc0728ca75726e2f6/media/repo-banner.sketch -------------------------------------------------------------------------------- /media/repo-banner@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigchaindb/js-driver-orm/73ed441e210b823c7c6442fcc0728ca75726e2f6/media/repo-banner@2x.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bigchaindb-orm", 3 | "version": "3.0.3", 4 | "description": "A CRAB-based ORM for BigchainDB", 5 | "homepage": "https://www.bigchaindb.com/", 6 | "bugs": "https://github.com/bigchaindb/js-driver-orm/issues", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/bigchaindb/js-driver-orm.git" 10 | }, 11 | "license": "Apache-2.0", 12 | "author": "BigchainDB", 13 | "main": "./dist/node/index.js", 14 | "browser": "./dist/browser/bigchaindb-orm.cjs2.min.js", 15 | "dependencies": { 16 | "bigchaindb-driver": "^4.1.0", 17 | "eslint-config-standard": "^12.0.0", 18 | "uuid": "^3.3.2" 19 | }, 20 | "devDependencies": { 21 | "ava": "^0.25.0", 22 | "babel-cli": "^6.26.0", 23 | "babel-eslint": "^8.2.6", 24 | "babel-loader": "^7.1.5", 25 | "babel-plugin-add-module-exports": "^0.3.1", 26 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 27 | "babel-plugin-transform-export-extensions": "^6.22.0", 28 | "babel-plugin-transform-object-assign": "^6.22.0", 29 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 30 | "babel-plugin-transform-runtime": "^6.23.0", 31 | "babel-preset-env": "^1.6.1", 32 | "babel-preset-es2015-no-commonjs": "^0.0.2", 33 | "babel-runtime": "^6.26.0", 34 | "cowsay": "^1.3.1", 35 | "cross-env": "^5.2.0", 36 | "eslint": "^5.0.1", 37 | "eslint-config-ascribe": "^3.0.5", 38 | "eslint-plugin-import": "^2.13.0", 39 | "eslint-plugin-node": "^7.0.1", 40 | "eslint-plugin-promise": "^4.0.1", 41 | "eslint-plugin-standard": "^4.0.0", 42 | "nyc": "^12.0.1", 43 | "release-it": "^7.4.8", 44 | "rimraf": "^2.6.2", 45 | "sinon": "^5.0.1", 46 | "webpack": "^4.16.3", 47 | "webpack-cli": "^3.1.0", 48 | "webpack-merge": "^4.1.4" 49 | }, 50 | "scripts": { 51 | "lint": "eslint ./{src,test}/*.js", 52 | "build": "npm run clean && npm run build:cjs && npm run build:dist", 53 | "build:bundle": "cross-env NODE_ENV=production webpack", 54 | "build:cjs": "cross-env BABEL_ENV=cjs babel ./src -d dist/node", 55 | "build:dist": "cross-env NODE_ENV=production webpack --mode production", 56 | "clean": "rimraf dist/bundle dist/node", 57 | "test": "npm run lint && nyc ava test/ && npm run thanks && npm run report-coverage", 58 | "thanks": "cowsay Hi, thanks for your interest in BigchainDB. We appreciate your contribution!", 59 | "prepublishOnly": "npm run build", 60 | "release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive", 61 | "release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive", 62 | "release-major": "./node_modules/release-it/bin/release-it.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive", 63 | "report-coverage": "nyc report --reporter=lcov > coverage.lcov && codecov" 64 | }, 65 | "keywords": [ 66 | "bigchaindb", 67 | "driver", 68 | "blockchain", 69 | "decentralized", 70 | "orm" 71 | ], 72 | "ava": { 73 | "files": [ 74 | "test/*.js" 75 | ], 76 | "source": [ 77 | "**/*.{js,jsx}", 78 | "!node_modules/**/*", 79 | "!dist/**/*" 80 | ], 81 | "failFast": true, 82 | "failWithoutAssertions": false, 83 | "tap": true, 84 | "powerAssert": false, 85 | "require": [ 86 | "babel-register", 87 | "babel-polyfill" 88 | ], 89 | "babel": "inherit" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /plugins/add-vendors-plugin.js: -------------------------------------------------------------------------------- 1 | // Copyright BigchainDB GmbH and BigchainDB contributors 2 | // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) 3 | // Code is Apache-2.0 and docs are CC-BY-4.0 4 | 5 | const { ConcatSource } = require('webpack-sources') 6 | 7 | module.exports = class AddVendorsPlugin { 8 | constructor(base) { 9 | this.base = base 10 | } 11 | 12 | apply(compiler) { 13 | compiler.hooks.emit.tapAsync( 14 | `AddVendorsPlugin ${this.base}`, 15 | (compilation, callback) => { 16 | const main = compilation.assets[`main.${this.base}`] 17 | const mainMap = compilation.assets[`main.${this.base}.map`] 18 | const vendor = compilation.assets[`vendors.${this.base}`] 19 | 20 | if (main && vendor) { 21 | const compiledAsset = new ConcatSource(main.children[0]) 22 | compiledAsset.add(vendor) 23 | compiledAsset.add(main.children[1]) 24 | compilation.assets = {} 25 | compilation.assets[this.base] = compiledAsset 26 | } else if (main && mainMap) { 27 | compilation.assets = {} 28 | compilation.assets[this.base] = main 29 | compilation.assets[`${this.base}.map`] = mainMap 30 | } 31 | callback() 32 | } 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/connection.js: -------------------------------------------------------------------------------- 1 | import * as driver from 'bigchaindb-driver' // eslint-disable-line import/no-namespace 2 | 3 | export default class Connection { 4 | constructor(path, headers = {}) { 5 | this.path = path 6 | this.headers = Object.assign({}, headers) 7 | this.conn = new driver.Connection(path, headers) 8 | } 9 | 10 | getAssetId(tx) { // eslint-disable-line class-methods-use-this 11 | return tx.operation === 'CREATE' ? tx.id : tx.asset.id 12 | } 13 | 14 | getTransaction(transactionId) { 15 | return this.conn.getTransaction(transactionId) 16 | } 17 | 18 | listTransactions(assetId, operation) { 19 | return this.conn.listTransactions(assetId, operation) 20 | } 21 | 22 | searchAssets(text) { 23 | return this.conn.searchAssets(text) 24 | } 25 | 26 | createTransaction(publicKey, privateKey, payload, metadata) { 27 | try { 28 | // Create a transation 29 | const tx = driver.Transaction.makeCreateTransaction( 30 | payload, 31 | metadata, 32 | [ 33 | driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(publicKey)) 34 | ], 35 | publicKey 36 | ) 37 | 38 | // sign/fulfill the transaction 39 | const txSigned = driver.Transaction.signTransaction(tx, privateKey) 40 | return this.conn.postTransactionCommit(txSigned).then(() => txSigned) 41 | } catch (error) { 42 | return Promise.reject(error) 43 | } 44 | } 45 | 46 | transferTransaction(tx, fromPublicKey, fromPrivateKey, toPublicKey, metadata) { 47 | try { 48 | const txTransfer = driver.Transaction.makeTransferTransaction( 49 | [{ 'tx': tx, 'output_index': 0 }], 50 | [driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(toPublicKey))], 51 | metadata, 52 | ) 53 | const txTransferSigned = driver.Transaction.signTransaction(txTransfer, fromPrivateKey) 54 | // send it off to BigchainDB 55 | return this.conn.postTransactionCommit(txTransferSigned).then(() => txTransferSigned) 56 | } catch (error) { 57 | return Promise.reject(error) 58 | } 59 | } 60 | 61 | getSortedTransactions(assetId) { 62 | return this.conn.listTransactions(assetId) 63 | .then((txList) => { 64 | if (txList.length <= 1) { 65 | return txList 66 | } 67 | const inputTransactions = [] 68 | txList.forEach((tx) => 69 | tx.inputs.forEach(input => { 70 | if (input.fulfills) { 71 | inputTransactions.push(input.fulfills.transaction_id) 72 | } 73 | })) 74 | const unspents = txList.filter((tx) => inputTransactions.indexOf(tx.id) === -1) 75 | if (unspents.length) { 76 | let tipTransaction = unspents[0] 77 | let tipTransactionId = tipTransaction.inputs[0].fulfills.transaction_id 78 | const sortedTxList = [] 79 | while (true) { // eslint-disable-line no-constant-condition 80 | sortedTxList.push(tipTransaction) 81 | try { 82 | tipTransactionId = tipTransaction.inputs[0].fulfills.transaction_id 83 | } catch (e) { 84 | break 85 | } 86 | if (!tipTransactionId) { 87 | break 88 | } 89 | tipTransaction = txList.filter((tx) => // eslint-disable-line no-loop-func, prefer-destructuring 90 | tx.id === tipTransactionId)[0] 91 | } 92 | return sortedTxList.reverse() 93 | } else { 94 | console.error( 95 | 'something went wrong while sorting transactions', 96 | txList, inputTransactions 97 | ) 98 | } 99 | return txList 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-namespace 2 | import * as driver from 'bigchaindb-driver' 3 | import Connection from './connection' 4 | import OrmObject from './ormobject' 5 | 6 | export default class Orm { 7 | constructor(connectionUrl, headers) { 8 | this.connection = new Connection(connectionUrl, headers) 9 | if (headers && headers.app_id !== undefined && headers.app_id !== '') { 10 | this.appId = headers.app_id 11 | } else { 12 | this.appId = 'global' 13 | } 14 | this.models = [] 15 | this.driver = driver 16 | } 17 | define(modelName, modelSchema) { 18 | this.models[modelName] = new OrmObject( 19 | modelName, 20 | modelSchema, 21 | this.connection, 22 | this.appId 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ormobject.js: -------------------------------------------------------------------------------- 1 | import uuid from 'uuid/v4' 2 | 3 | // The likelihood to generate a vanity address that is 11 times "Burn" is extremely low: 4 | // - https://en.bitcoin.it/wiki/Vanitygen#Use_of_vanitygen_to_try_to_attack_addresses 5 | const BURN_ADDRESS = 'BurnBurnBurnBurnBurnBurnBurnBurnBurnBurnBurn' 6 | 7 | export default class OrmObject { 8 | constructor(modelName, modelSchema, connection, appId = 'global', transactionList = []) { 9 | this._name = modelName 10 | this._schema = modelSchema 11 | this._connection = connection 12 | this._appId = appId 13 | if (transactionList.length) { 14 | this.transactionHistory = transactionList 15 | this.id = transactionList[0].asset.data.id 16 | this.data = Object.assign({}, ...transactionList.map(tx => (tx.metadata))) 17 | } 18 | } 19 | 20 | retrieve(input = undefined) { 21 | let query 22 | if (input !== undefined && input !== '') { 23 | query = `"${input}"` 24 | } else { 25 | query = `"id:${this._appId}:${this._name}:"` 26 | } 27 | return this._connection.searchAssets(query) 28 | .then(assets => 29 | Promise.all(assets.map(asset => 30 | this._connection.getSortedTransactions(asset.id) 31 | .then(txList => 32 | new OrmObject( 33 | this._name, 34 | this._schema, 35 | this._connection, 36 | this._appId, 37 | txList 38 | ))))) 39 | } 40 | 41 | create(inputs, id = uuid()) { 42 | if (inputs === undefined) { 43 | console.error('inputs missing') 44 | } 45 | const assetPayload = { 46 | 'schema': this._schema, 47 | 'id': `id:${this._appId}:${this._name}:${id}` 48 | } 49 | return this._connection 50 | .createTransaction( 51 | inputs.keypair.publicKey, 52 | inputs.keypair.privateKey, 53 | assetPayload, 54 | inputs.data 55 | ) 56 | .then(tx => Promise.resolve(this._connection.getSortedTransactions(tx.id).then((txList) => 57 | new OrmObject( 58 | this._name, 59 | this._schema, 60 | this._connection, 61 | this._appId, 62 | txList 63 | )))) 64 | } 65 | 66 | append(inputs) { 67 | if (inputs === undefined) { 68 | console.error('inputs missing') 69 | } 70 | return this._connection 71 | .transferTransaction( 72 | this.transactionHistory[this.transactionHistory.length - 1], 73 | inputs.keypair.publicKey, 74 | inputs.keypair.privateKey, 75 | inputs.toPublicKey, 76 | inputs.data 77 | ) 78 | .then(() => 79 | Promise.resolve(this._connection.getSortedTransactions(this.transactionHistory[0].id) 80 | .then((txList) => 81 | new OrmObject( 82 | this._name, 83 | this._schema, 84 | this._connection, 85 | this._appId, 86 | txList 87 | )))) 88 | } 89 | 90 | burn(inputs) { 91 | if (inputs === undefined) { 92 | console.error('inputs missing') 93 | } 94 | 95 | return this._connection 96 | .transferTransaction( 97 | this.transactionHistory[this.transactionHistory.length - 1], 98 | inputs.keypair.publicKey, 99 | inputs.keypair.privateKey, 100 | BURN_ADDRESS, 101 | { status: 'BURNED' } 102 | ) 103 | .then(() => 104 | Promise.resolve(this._connection.getSortedTransactions(this.transactionHistory[0].id) 105 | .then((txList) => 106 | new OrmObject( 107 | this._name, 108 | this._schema, 109 | this._connection, 110 | this._appId, 111 | txList 112 | )))) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /test/test_orm.js: -------------------------------------------------------------------------------- 1 | import sinon from 'sinon' 2 | import test from 'ava' 3 | 4 | import Orm from '../src/index' 5 | import Connection from '../src/connection' 6 | 7 | test('Create asset with data', t => { 8 | const expected = { key: 'dataValue' } 9 | 10 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { 11 | app_id: '', 12 | app_key: '' 13 | }) 14 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 15 | // create a public and private key for Alice 16 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 17 | return bdbOrm.models.myModel 18 | .create({ 19 | keypair: aliceKeypair, 20 | data: expected 21 | }) 22 | .then(res => t.deepEqual(res.data, expected)) 23 | }) 24 | 25 | test('Create asset with user provided id', t => { 26 | const expected = { key: 'dataValue' } 27 | const id = 'My-Unique-ID' 28 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { 29 | app_id: '', 30 | app_key: '' 31 | }) 32 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 33 | // create a public and private key for Alice 34 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 35 | return bdbOrm.models.myModel 36 | .create({ 37 | keypair: aliceKeypair, 38 | data: expected 39 | }, id) 40 | .then(res => t.deepEqual(res.data, expected)) 41 | }) 42 | 43 | test('Retrieve asset', t => { 44 | const expected = { key: 'dataValue' } 45 | 46 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { 47 | app_id: '', 48 | app_key: '' 49 | }) 50 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 51 | // create a public and private key for Alice 52 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 53 | return bdbOrm.models.myModel 54 | .create({ 55 | keypair: aliceKeypair, 56 | data: expected 57 | }) 58 | .then(asset => bdbOrm.models.myModel.retrieve(asset.id)) 59 | .then(res => t.deepEqual(res[0].data, expected)) 60 | }) 61 | 62 | test('Retrieve multiple asset', async (t) => { 63 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { 64 | app_id: '', 65 | app_key: '' 66 | }) 67 | bdbOrm.define('myNewModel', 'https://schema.org/v1/myNewModel') 68 | // create a public and private key for Alice 69 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 70 | const expected = await bdbOrm.models.myNewModel.retrieve().then(res => res.length) + 1 71 | 72 | return bdbOrm.models.myNewModel 73 | .create({ 74 | keypair: aliceKeypair, 75 | data: { key: 'dataValue' } 76 | }) 77 | .then(() => bdbOrm.models.myNewModel.retrieve() 78 | .then(res => t.deepEqual(res.length, expected))) 79 | }) 80 | 81 | test('Append asset', t => { 82 | const expected = { 83 | key: 'dataValue', 84 | keyToUpdate: 'updatedDataValue', 85 | newKey: 'newDataValue' 86 | } 87 | 88 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { 89 | app_id: '', 90 | app_key: '' 91 | }) 92 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 93 | // create a public and private key for Alice 94 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 95 | return bdbOrm.models.myModel 96 | .create({ 97 | keypair: aliceKeypair, 98 | data: { key: 'dataValue', keyToUpdate: 'dataUpdatableValue' } 99 | }) 100 | .then(asset => asset.append({ 101 | toPublicKey: aliceKeypair.publicKey, 102 | keypair: aliceKeypair, 103 | data: { keyToUpdate: 'updatedDataValue', newKey: 'newDataValue' } 104 | })) 105 | .then(res => { 106 | t.deepEqual(res.data, expected) 107 | t.deepEqual(res.transactionHistory.length, 2) 108 | }) 109 | }) 110 | 111 | test('Burn asset', t => { 112 | const expected = { key: 'dataValue', status: 'BURNED' } 113 | 114 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { 115 | app_id: '', 116 | app_key: '' 117 | }) 118 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 119 | // create a public and private key for Alice 120 | const aliceKeypair = new bdbOrm.driver.Ed25519Keypair() 121 | return bdbOrm.models.myModel 122 | .create({ 123 | keypair: aliceKeypair, 124 | data: { key: 'dataValue' } 125 | }) 126 | .then(asset => asset.burn({ 127 | keypair: aliceKeypair 128 | })) 129 | .then(res => { 130 | t.deepEqual(res.data, expected) 131 | t.deepEqual(res.transactionHistory.length, 2) 132 | t.not(res.transactionHistory[res.transactionHistory.length - 1] 133 | .outputs[0].public_keys[0], aliceKeypair.publicKey) 134 | }) 135 | }) 136 | 137 | test('Orm stores notices the headers appId', t => { 138 | const bdbOrm = new Orm('http://localhost:9984/api/v1/', { app_id: 'AppID' }) 139 | 140 | t.is(bdbOrm.appId, 'AppID') 141 | }) 142 | 143 | test.serial('Orm console logs an error if inputs is undefined calling create', t => { 144 | const bdbOrm = new Orm('http://localhost:9984/api/v1/') 145 | 146 | t.context.consoleError = console.error 147 | console.error = sinon.spy() 148 | 149 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 150 | 151 | try { 152 | bdbOrm.models.myModel.create() 153 | } catch (e) { 154 | // noop 155 | } 156 | 157 | t.true(console.error.calledOnce) 158 | console.error = t.context.consoleError 159 | }) 160 | 161 | test.serial('Orm console logs an error if inputs is undefined calling append', t => { 162 | const bdbOrm = new Orm('http://localhost:9984/api/v1/') 163 | 164 | t.context.consoleError = console.error 165 | console.error = sinon.spy() 166 | 167 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 168 | 169 | try { 170 | bdbOrm.models.myModel.append() 171 | } catch (e) { 172 | // noop 173 | } 174 | 175 | t.true(console.error.calledOnce) 176 | console.error = t.context.consoleError 177 | }) 178 | 179 | test.serial('Orm console logs an error if inputs is undefined calling burn', t => { 180 | const bdbOrm = new Orm('http://localhost:9984/api/v1/') 181 | 182 | t.context.consoleError = console.error 183 | console.error = sinon.spy() 184 | 185 | bdbOrm.define('myModel', 'https://schema.org/v1/myModel') 186 | 187 | try { 188 | bdbOrm.models.myModel.burn() 189 | } catch (e) { 190 | // noop 191 | } 192 | 193 | t.true(console.error.calledOnce) 194 | console.error = t.context.consoleError 195 | }) 196 | 197 | test('Connection returns transaction id as assetId when transaction is a CREATE', t => { 198 | const tx = { asset: { id: 'a-fake-asset-id' }, operation: 'CREATE', id: 'a-fake-tx-id' } 199 | const conn = new Connection('/') 200 | 201 | t.is(conn.getAssetId(tx), tx.id) 202 | }) 203 | 204 | test('Connection returns asset id as assetId when transaction is not a CREATE', t => { 205 | const tx = { asset: { id: 'a-fake-asset-id' }, operation: 'OTHER', id: 'a-fake-tx-id' } 206 | const conn = new Connection('/') 207 | 208 | t.is(conn.getAssetId(tx), tx.asset.id) 209 | }) 210 | 211 | test('Connection proxies getTransaction to bigchaindb-driver', t => { 212 | const conn = new Connection('/') 213 | conn.conn = { getTransaction(id) { return id === 'a-fake-id' } } 214 | 215 | t.true(conn.getTransaction('a-fake-id')) 216 | }) 217 | 218 | test('Connection proxies listTransactions to bigchaindb-driver', t => { 219 | const conn = new Connection('/') 220 | conn.conn = { listTransactions(id, op) { return id === 'a-fake-id' && op === 'CREATE' } } 221 | 222 | t.true(conn.listTransactions('a-fake-id', 'CREATE')) 223 | }) 224 | 225 | test('Connection#createTransaction rejects the promise returned on error', async t => { 226 | const conn = new Connection('/') 227 | 228 | await t.throws(conn.createTransaction(1, 2, 3, 4)) 229 | }) 230 | 231 | test('Connection#transferTransaction rejects the promise returned on error', async t => { 232 | const conn = new Connection('/') 233 | 234 | await t.throws(conn.transferTransaction(1, 2, 3, 4, 5)) 235 | }) 236 | 237 | test.serial( 238 | 'Connection#getSortedTransactions logs an error if none of the transactions are unspents', 239 | async t => { 240 | const conn = new Connection('/') 241 | const txList = { filter: sinon.stub(), forEach: sinon.stub(), length: 2 } 242 | 243 | t.context.consoleError = console.error 244 | console.error = sinon.spy() 245 | 246 | conn.conn = {} 247 | conn.conn.listTransactions = sinon.stub() 248 | conn.conn.listTransactions.returns(new Promise((resolve) => { resolve(txList) })) 249 | txList.forEach.returns(true) 250 | txList.filter.returns([]) 251 | 252 | await conn.getSortedTransactions() 253 | 254 | t.true(console.error.calledOnce) 255 | console.error = t.context.consoleError 256 | } 257 | ) 258 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | // Copyright BigchainDB GmbH and BigchainDB contributors 2 | // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) 3 | // Code is Apache-2.0 and docs are CC-BY-4.0 4 | 5 | /* eslint-disable strict, no-console, object-shorthand */ 6 | 7 | 'use strict' 8 | 9 | const { paths } = require('./webpack.parts.js') 10 | 11 | module.exports = { 12 | entry: paths.entry, 13 | mode: 'none', 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'babel-loader', 21 | }, 22 | } 23 | ] 24 | }, 25 | optimization: { 26 | minimize: true, 27 | noEmitOnErrors: true 28 | }, 29 | resolve: { 30 | extensions: ['.js'], 31 | modules: ['node_modules'], 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Copyright BigchainDB GmbH and BigchainDB contributors 2 | // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) 3 | // Code is Apache-2.0 and docs are CC-BY-4.0 4 | 5 | /* eslint-disable strict, no-console, object-shorthand */ 6 | 7 | 'use strict' 8 | 9 | const PRODUCTION = process.env.NODE_ENV === 'production' 10 | 11 | const common = require('./webpack.common.js') 12 | 13 | const { outputs } = require('./webpack.parts.js') 14 | 15 | // '[libraryTarget]': [file extension] 16 | const OUTPUT_MAPPING = { 17 | 'amd': 'amd', 18 | 'commonjs': 'cjs', 19 | 'commonjs2': 'cjs2', 20 | 'umd': 'umd', 21 | 'window': 'window', 22 | } 23 | 24 | const OVERRIDES = { 25 | // optimization: { 26 | // minimize: false 27 | // } 28 | } 29 | 30 | if (PRODUCTION) { 31 | module.exports = outputs(common, 'production', OUTPUT_MAPPING, OVERRIDES) 32 | } else { 33 | module.exports = outputs(common, 'development', OUTPUT_MAPPING, OVERRIDES) 34 | } -------------------------------------------------------------------------------- /webpack.development.js: -------------------------------------------------------------------------------- 1 | // Copyright BigchainDB GmbH and BigchainDB contributors 2 | // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) 3 | // Code is Apache-2.0 and docs are CC-BY-4.0 4 | 5 | /* eslint-disable strict, no-console, object-shorthand */ 6 | 7 | 'use strict' 8 | 9 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 10 | 11 | module.exports = { 12 | devtool: 'inline-source-map', 13 | optimization: { 14 | minimizer: [ 15 | new UglifyJsPlugin({ 16 | test: /vendor/, 17 | sourceMap: false, 18 | }), 19 | new UglifyJsPlugin({ 20 | test: /^((?!(vendor)).)*.js$/, 21 | sourceMap: true, 22 | }) 23 | ], 24 | splitChunks: { 25 | cacheGroups: { 26 | commons: { 27 | test: /[\\/]node_modules[\\/]/, 28 | name: 'vendors', 29 | chunks: 'all' 30 | } 31 | } 32 | }, 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /webpack.parts.js: -------------------------------------------------------------------------------- 1 | // Copyright BigchainDB GmbH and BigchainDB contributors 2 | // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) 3 | // Code is Apache-2.0 and docs are CC-BY-4.0 4 | 5 | /* eslint-disable strict, no-console, object-shorthand */ 6 | 7 | 'use strict' 8 | 9 | const path = require('path') 10 | const merge = require('webpack-merge') 11 | 12 | const development = require('./webpack.development.js') 13 | const production = require('./webpack.production.js') 14 | 15 | const AddVendorsPlugin = require('./plugins/add-vendors-plugin') 16 | 17 | const paths = { 18 | entry: path.resolve(__dirname, './src/index.js'), 19 | bundle: path.resolve(__dirname, 'dist/browser'), 20 | } 21 | 22 | const outputs = (base, env, mapping, overrides) => { 23 | const collection = [] 24 | const library = 'bigchaindb-orm' 25 | const windowLibrary = 'BigchainDB-Orm' 26 | 27 | let environment = development 28 | let ext = 'js' 29 | 30 | if (env === 'production') { 31 | environment = production 32 | ext = `min.${ext}` 33 | } 34 | 35 | Object.entries(mapping).forEach(([target, extension]) => { 36 | const filename = `[name].${library}.${extension}.${ext}` 37 | 38 | const compiled = { 39 | output: { 40 | filename: filename, 41 | library: target === 'window' ? windowLibrary : library, 42 | libraryTarget: target, 43 | path: paths.bundle 44 | }, 45 | plugins: [ 46 | new AddVendorsPlugin(`${library}.${extension}.${ext}`) 47 | ] 48 | } 49 | 50 | collection.push(merge(base, environment, compiled, overrides)) 51 | }) 52 | 53 | return collection 54 | } 55 | 56 | module.exports = { 57 | outputs, 58 | paths 59 | } 60 | -------------------------------------------------------------------------------- /webpack.production.js: -------------------------------------------------------------------------------- 1 | // Copyright BigchainDB GmbH and BigchainDB contributors 2 | // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) 3 | // Code is Apache-2.0 and docs are CC-BY-4.0 4 | 5 | /* eslint-disable strict, no-console, object-shorthand */ 6 | 7 | 'use strict' 8 | 9 | module.exports = { 10 | devtool: 'source-map', 11 | } 12 | --------------------------------------------------------------------------------