1
0
mirror of https://github.com/oceanprotocol-archive/squid-js.git synced 2024-02-02 15:31:51 +01:00

Merge pull request #13 from oceanprotocol/feature/ddd

Feature/Domain Driven Design
This commit is contained in:
Sebastian Gerske 2018-09-20 15:20:42 +02:00 committed by GitHub
commit 77c4914d4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 383 additions and 46 deletions

View File

@ -36,21 +36,20 @@ Start by adding the package to your dependencies:
npm i @oceanprotocol/squid
```
The package exposes `OceanAgent` and `OceanKeeper` which you can import in your code like so:
The package exposes `Ocean` and `Logger` which you can import in your code like this:
```js
// ES6
import { OceanAgent, OceanKeeper } from '@oceanprotocol/squid'
import { Ocean, Logger } from '@oceanprotocol/squid'
// ES2015
const { OceanAgent, OceanKeeper } = require('@oceanprotocol/squid')
const { Ocean, Logger } = require('@oceanprotocol/squid')
```
You can then connect to a running [Keeper](https://github.com/oceanprotocol/keeper-contracts) & [Provider](https://github.com/oceanprotocol/provider) instance, e.g.:
```js
const oceanKeeper = new OceanKeeper('http://localhost:8545', 'development')
const oceanAgent = new OceanAgent('http://localhost:5000/api/v1/provider')
const ocean = await new Ocean({nodeUri: 'http://localhost:8545', network: 'development', providerUri: 'http://localhost:5000'})
```
## Development

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "@oceanprotocol/squid",
"version": "0.0.4",
"version": "0.0.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,12 +1,12 @@
{
"name": "@oceanprotocol/squid",
"version": "0.0.4",
"version": "0.0.6",
"description": "JavaScript client library for Ocean Protocol",
"main": "dist/squid.js",
"scripts": {
"test": "eslint ./src",
"start": "babel src --watch --out-dir dist",
"build": "babel src --out-dir dist",
"start": "babel ./src/ --watch --out-dir ./dist/",
"build": "babel ./src/ --out-dir ./dist/",
"release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive",
"release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive",
"release-major": "./node_modules/release-it/bin/release-it.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive",

View File

@ -1,19 +0,0 @@
import TruffleContract from 'truffle-contract'
const contracts = []
export default class ContractLoader {
static async _doLoad(what, where, provider) {
// console.log("Loading", what, "from", where)
/* eslint-disable-next-line */
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`)
const contract = TruffleContract(artifact)
contract.setProvider(provider)
contracts[what] = await contract.at(artifact.address)
return contracts[what]
}
static load(what, where, provider) {
return contracts[what] || ContractLoader._doLoad(what, where, provider)
}
}

View File

@ -1,9 +1,11 @@
/* global fetch */
/* eslint-disable no-console */
import Logger from '../utils/logger'
export default class OceanAgent {
constructor(connectionUrl) {
this.assetsUrl = connectionUrl + '/assets'
Logger.warn('OceanAgent is deprecated use the Ocean object from squid instead')
}
getAssetsMetadata() {
@ -20,17 +22,17 @@ export default class OceanAgent {
headers: { 'Content-type': 'application/json' }
})
.then(response => {
console.log('Success:', response)
Logger.log('Success:', response)
if (response.ok) {
console.log('Success:', response)
Logger.log('Success:', response)
return true
}
console.log('Failed: ', response.status, response.statusText)
Logger.log('Failed: ', response.status, response.statusText)
return false
// throw new Error(response.statusText ? response.statusText : `publish asset failed with status ${response.status}`)
})
.catch(error => {
console.log(`Publish asset to ocean database could not be completed: ${error.message()}`)
Logger.log(`Publish asset to ocean database could not be completed: ${error.message()}`)
return false
})
}

View File

@ -1,7 +1,6 @@
/* eslint-disable no-console */
import Web3 from 'web3'
import ContractLoader from './contractLoader'
import ContractLoader from '../keeper/contractLoader'
import Logger from '../utils/logger'
const DEFAULT_GAS = 300000
@ -11,12 +10,14 @@ export default class OceanKeeper {
this.web3 = new Web3(web3Provider)
this.defaultGas = DEFAULT_GAS
this.network = network || 'development'
Logger.warn('OceanKeeper is deprecated use the Ocean object from squid instead')
}
async initContracts() {
this.oceanToken = await ContractLoader.load('OceanToken', this.network, this.web3.currentProvider)
this.oceanMarket = await ContractLoader.load('OceanMarket', this.network, this.web3.currentProvider)
this.oceanAuth = await ContractLoader.load('OceanAuth', this.network, this.web3.currentProvider)
this.oceanToken = await ContractLoader.load('OceanToken', this.network, this.web3)
this.oceanMarket = await ContractLoader.load('OceanMarket', this.network, this.web3)
this.oceanAuth = await ContractLoader.load('OceanAuth', this.network, this.web3)
return {
oceanToken: this.oceanToken,
@ -94,7 +95,7 @@ export default class OceanKeeper {
paid: await this.verifyOrderPayment(event.args._id).then((received) => received),
key: null
}))
console.debug('got orders: ', orders)
Logger.debug('got orders: ', orders)
return orders
}
@ -110,7 +111,7 @@ export default class OceanKeeper {
price,
{ from: publisherAddress, gas: this.defaultGas }
)
console.log('registered: ', result)
Logger.log('registered: ', result)
return assetId
}
@ -172,7 +173,7 @@ export default class OceanKeeper {
event.watch((error, result) => { // eslint-disable-line security/detect-non-literal-fs-filename
event.stopWatching()
if (error) {
console.log(`Error in keeper ${eventName} event: `, error)
Logger.log(`Error in keeper ${eventName} event: `, error)
}
callback(result, error)
})

25
src/keeper/auth.js Normal file
View File

@ -0,0 +1,25 @@
import ContractLoader from './contractLoader'
import KeeperBase from './keeper-base'
export default class OceanAuth extends KeeperBase {
constructor(web3, network) {
super(web3, network)
return (async () => {
this.contract = await ContractLoader.load('OceanAuth', this._network, this._web3)
return this
})()
}
cancelAccessRequest(orderId, senderAddress) {
return this.contract.cancelAccessRequest(orderId, { from: senderAddress })
}
getOrderStatus(orderId) {
return this.contract.statusOfAccessRequest(orderId)
}
getEncryptedAccessToken(orderId, senderAddress) {
return this.contract.getEncryptedAccessToken(orderId, { from: senderAddress })
}
}

View File

@ -0,0 +1,23 @@
import TruffleContract from 'truffle-contract'
import Logger from '../utils/logger'
const contracts = []
export default class ContractLoader {
static async _doLoad(what, where, web3) {
Logger.log('Loading', what, 'from', where)
/* eslint-disable-next-line security/detect-non-literal-require */
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${what}.${where}`)
Logger.log('Loaded artifact', artifact)
const contract = TruffleContract(artifact)
Logger.log('Getting instance of', what, 'from', where, 'at', artifact.address)
contract.setProvider(web3.currentProvider)
contracts[what] = await contract.at(artifact.address)
return contracts[what]
}
static async load(what, where, provider) {
return contracts[what] || ContractLoader._doLoad(what, where, provider)
}
}

View File

@ -0,0 +1,7 @@
export default class KeeperBase {
constructor(web3, network) {
this._web3 = web3
this._network = network
this.contract = null
}
}

53
src/keeper/market.js Normal file
View File

@ -0,0 +1,53 @@
import ContractLoader from './contractLoader'
import KeeperBase from './keeper-base'
import Logger from '../utils/logger'
export default class OceanMarket extends KeeperBase {
constructor(web3, network) {
super(web3, network)
return (async () => {
this.contract = await ContractLoader.load('OceanMarket', this._network, this._web3)
return this
})()
}
// call functions (costs no gas)
checkAsset(assetId) {
return this.contract.checkAsset(assetId)
}
verifyOrderPayment(orderId) {
return this.contract.verifyPaymentReceived(orderId)
}
getAssetPrice(assetId) {
return this.contract.getAssetPrice(assetId)
.then((price) => price.toNumber())
}
// Transactions with gas cost
requestTokens(amount, address) {
return this.contract.requestTokens(amount, { from: address })
}
async registerAsset(name, description, price, publisherAddress) {
const assetId = await this.contract.generateId(name + description)
const result = await this.contract.register(
assetId,
price,
{ from: publisherAddress, gas: this.defaultGas }
)
Logger.log('registered: ', result)
return assetId
}
async payAsset(assetId, order, publisherAddress, senderAddress) {
let assetPrice = await this.contract.getAssetPrice(assetId).then((price) => price.toNumber())
this.contract.sendPayment(order.id, publisherAddress, assetPrice, order.timeout, {
from: senderAddress,
gas: 2000000
})
}
}

30
src/keeper/token.js Normal file
View File

@ -0,0 +1,30 @@
import ContractLoader from './contractLoader'
import KeeperBase from './keeper-base'
import Logger from '../utils/logger'
export default class OceanToken extends KeeperBase {
constructor(web3, network) {
super(web3, network)
return (async () => {
this.contract = await ContractLoader.load('OceanToken', this._network, this._web3)
return this
})()
}
getTokenBalance(accountAddress) {
return this.contract.balanceOf.call(accountAddress)
}
async getEthBalance(account) {
return new Promise((resolve, reject) => {
Logger.log('getting balance for', account)
this._web3.eth.getBalance(account, 'latest', (err, balance) => {
if (err) return reject(err)
Logger.log('balance', balance)
resolve(balance)
})
})
}
}

37
src/metadata.js Normal file
View File

@ -0,0 +1,37 @@
/* global fetch */
import Logger from './utils/logger'
export default class MetaData {
constructor(providerUri) {
this.assetsUrl = providerUri + '/assets'
}
getAssetsMetadata() {
return fetch(this.assetsUrl + '/metadata', { method: 'GET' })
.then(res => res.json())
.then(data => JSON.parse(data))
}
publishDataAsset(asset) {
return fetch(this.assetsUrl + '/metadata',
{
method: 'POST',
body: JSON.stringify(asset),
headers: { 'Content-type': 'application/json' }
})
.then(response => {
Logger.log('Success:', response)
if (response.ok) {
Logger.log('Success:', response)
return true
}
Logger.log('Failed: ', response.status, response.statusText)
return false
// throw new Error(response.statusText ? response.statusText : `publish asset failed with status ${response.status}`)
})
.catch(error => {
Logger.log(`Publish asset to ocean database could not be completed: ${error.message()}`)
return false
})
}
}

136
src/ocean.js Normal file
View File

@ -0,0 +1,136 @@
import Web3 from 'web3'
import OceanMarket from './keeper/market'
import OceanAuth from './keeper/auth'
import OceanToken from './keeper/token'
import Logger from './utils/logger'
import Web3Helper from './utils/Web3Helper'
import MetaData from './metadata'
const DEFAULT_GAS = 300000
export default class Ocean {
constructor(config) {
const web3Provider = config.web3Provider || new Web3.providers.HttpProvider(config.nodeUri)
this._web3 = new Web3(web3Provider)
this._defaultGas = config.gas || DEFAULT_GAS
this._network = config.network || 'development'
this._providerUri = config.providerUri || null
this.helper = new Web3Helper(this._web3)
this.metadata = new MetaData(this._providerUri)
return (async () => {
this.market = await new OceanMarket(this._web3, this._network)
this.auth = await new OceanAuth(this._web3, this._network)
this.token = await new OceanToken(this._web3, this._network)
return this
})()
}
async getAccounts() {
return Promise.all(this.helper.getAccounts().map(async (account) => {
// await ocean.market.requestTokens(account, 1000)
return {
name: account,
balance: {
ocn: await this.token.getTokenBalance(account),
eth: await this.token.getEthBalance(account)
}
}
}))
}
async getOrdersByConsumer(consumerAddress) {
let accessConsentEvent = this.auth.contract.AccessConsentRequested({ _consumer: consumerAddress }, {
fromBlock: 0,
toBlock: 'latest'
})
let _resolve = null
let _reject = null
const promise = new Promise((resolve, reject) => {
_resolve = resolve
_reject = reject
})
const getEvents = () => {
accessConsentEvent.get((error, logs) => {
if (error) {
_reject(error)
throw new Error(error)
} else {
_resolve(logs)
}
})
return promise
}
const events = await getEvents().then((events) => events)
// let orders = await this.buildOrdersFromEvents(events, consumerAddress).then((result) => result)
let orders = events
.filter(obj => (obj.args._consumer === consumerAddress))
.map(async (event) => ({
...event.args,
timeout: event.args._timeout.toNumber(),
status: await this.getOrderStatus(event.args._id).then((status) => status.toNumber()),
paid: await this.verifyOrderPayment(event.args._id).then((received) => received),
key: null
}))
Logger.debug('got orders: ', orders)
return orders
}
purchaseAsset(
assetId, publisherId, price, privateKey, publicKey, timeout, senderAddress,
initialRequestEventHandler, accessCommittedEventHandler, tokenPublishedEventHandler) {
const { token, market, auth } = this
// Allow market contract to transfer funds on the consumer's behalf
token.approve(market.address, price, { from: senderAddress, gas: 2000000 })
// Submit the access request
auth.initiateAccessRequest(
assetId, publisherId, publicKey,
timeout, { from: senderAddress, gas: 1000000 }
)
const resourceFilter = { _resourceId: assetId, _consumer: senderAddress }
const initRequestEvent = auth.contract.AccessConsentRequested(resourceFilter)
let order = {}
this._listenOnce(
initRequestEvent,
'AccessConsentRequested',
(result, error) => {
order = initialRequestEventHandler(result, error)
const requestIdFilter = { _id: order.id }
const accessCommittedEvent = auth.contract.AccessRequestCommitted(requestIdFilter)
const tokenPublishedEvent = auth.contract.EncryptedTokenPublished(requestIdFilter)
this._listenOnce(
accessCommittedEvent,
'AccessRequestCommitted',
(result, error) => {
accessCommittedEventHandler(result, order, error)
}
)
this._listenOnce(
tokenPublishedEvent,
'EncryptedTokenPublished',
(result, error) => {
tokenPublishedEventHandler(result, order, error)
}
)
})
return order
}
// Helper functions (private)
_listenOnce(event, eventName, callback) {
// eslint-disable-next-line security/detect-non-literal-fs-filename
event.watch((error, result) => {
event.stopWatching()
if (error) {
Logger.log(`Error in keeper ${eventName} event: `, error)
}
callback(result, error)
})
}
}

View File

@ -1,7 +1,11 @@
import OceanAgent from './ocean-agent'
import OceanKeeper from './ocean-keeper'
import OceanAgent from './deprecated/ocean-agent'
import OceanKeeper from './deprecated/ocean-keeper'
import Ocean from './ocean'
import Logger from './utils/logger'
export {
OceanAgent,
OceanKeeper
Ocean,
OceanAgent, // deprecated
OceanKeeper, // deprecated
Logger
}

17
src/utils/Web3Helper.js Normal file
View File

@ -0,0 +1,17 @@
import Logger from './logger'
export default class Web3Helper {
constructor(web3) {
this._web3 = web3
}
getAccounts() {
Logger.log(this._web3)
return this._web3.eth.accounts
}
// web3 wrappers
sign(accountAddress, message) {
return this._web3.eth.sign(accountAddress, message)
}
}

22
src/utils/logger.js Normal file
View File

@ -0,0 +1,22 @@
export default class Logger {
static dispatch(verb, ...args) {
/* eslint-disable-next-line no-console */
console[verb](...args)
}
static log(...args) {
Logger.dispatch('log', ...args)
}
static debug(...args) {
Logger.dispatch('debug', ...args)
}
static warn(...args) {
Logger.dispatch('warn', ...args)
}
static error(...args) {
Logger.dispatch('error', ...args)
}
}