mirror of
https://github.com/oceanprotocol-archive/squid-js.git
synced 2024-02-02 15:31:51 +01:00
Merge pull request #31 from oceanprotocol/feature/secret_store
Squid 1.0
This commit is contained in:
commit
e5a285f17a
5
.bumpversion.cfg
Normal file
5
.bumpversion.cfg
Normal file
|
@ -0,0 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 0.1.7
|
||||
|
||||
[bumpversion:file:package.json]
|
||||
|
11
.travis.yml
11
.travis.yml
|
@ -30,4 +30,13 @@ after_script:
|
|||
- greenkeeper-lockfile-upload
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
email: false
|
||||
|
||||
deploy:
|
||||
- provider: npm
|
||||
email: "devops@oceanprotocol.com"
|
||||
api_key: ${NPM_TOKEN}
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
Start by adding the package to your dependencies:
|
||||
|
||||
```bash
|
||||
npm i @oceanprotocol/squid
|
||||
npm i @oceanprotocol/squid --save
|
||||
```
|
||||
|
||||
The package exposes `Ocean` and `Logger` which you can import in your code like this:
|
||||
|
@ -46,10 +46,13 @@ import { Ocean, Logger } from '@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.:
|
||||
You can then connect to a running [Keeper](https://github.com/oceanprotocol/keeper-contracts) & [Aquarius](https://github.com/oceanprotocol/aquarius) instance, e.g.:
|
||||
|
||||
```js
|
||||
const ocean = await new Ocean({nodeUri: 'http://localhost:8545', network: 'development', providerUri: 'http://localhost:5000'})
|
||||
const ocean = await new Ocean({
|
||||
nodeUri: 'http://localhost:8545',
|
||||
aquariusUri: 'http://localhost:5000'
|
||||
})
|
||||
```
|
||||
|
||||
## Development
|
||||
|
|
36
bumpversion.sh
Executable file
36
bumpversion.sh
Executable file
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
usage(){
|
||||
echo "Usage: $0 {major|minor|patch} [--tag]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ! [ -x "$(command -v bumpversion)" ]; then
|
||||
echo 'Error: bumpversion is not installed.' >&2
|
||||
exit 1
|
||||
elif ! git diff-index --quiet HEAD -- >/dev/null 2>&1; then
|
||||
echo 'There are local changes in your the git repository. Please commit or stash them before bumping version.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Illegal number of parameters"
|
||||
usage
|
||||
elif [[ $1 != 'major' && $1 != 'minor' && $1 != 'patch' ]]; then
|
||||
echo 'First argument must be {major|minor|patch}'
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ $2 == '--tag' ]]; then
|
||||
if git branch --contains $(git rev-parse --verify HEAD) | grep -E 'master'; then
|
||||
bumpversion --tag --commit $1
|
||||
else
|
||||
echo "Only master tags can be tagged"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
bumpversion $1
|
||||
fi
|
700
package-lock.json
generated
700
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
|
@ -1,17 +1,19 @@
|
|||
{
|
||||
"name": "@oceanprotocol/squid",
|
||||
"version": "0.1.0-beta.17",
|
||||
"version": "0.1.7",
|
||||
"description": "JavaScript client library for Ocean Protocol",
|
||||
"main": "dist/squid.js",
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"test:watch": "mocha -w --watch-extensions js,ts",
|
||||
"test:watch": "mocha -w --watch-extensions js,ts,json",
|
||||
"test:cover": "nyc mocha",
|
||||
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||
"start": "npm link @oceanprotocol/keeper-contracts && npm run build:watch",
|
||||
"build": "npm run lint && tsc && npm run doc",
|
||||
"clean": "rm -rf ./dist/ ./doc/ ./coverage ./.nyc_output",
|
||||
"lint": "tslint -c tslint.json 'src/**/*.ts' 'test/**/*.ts'",
|
||||
"start": "npm link @oceanprotocol/keeper-contracts @oceanprotocol/secret-store-client && npm run build:watch",
|
||||
"build": "npm run clean && npm run lint && tsc && npm run doc",
|
||||
"build:watch": "tsc -w",
|
||||
"doc": "typedoc --mode modules --out ./doc/ src/",
|
||||
"doc": "typedoc --mode modules --out ./doc/ ./src/",
|
||||
"run": "ts-node",
|
||||
"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",
|
||||
|
@ -49,20 +51,23 @@
|
|||
"node": ">=8 <10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oceanprotocol/keeper-contracts": "^0.4.1",
|
||||
"bignumber.js": "^7.2.1",
|
||||
"eth-crypto": "^1.2.4",
|
||||
"@oceanprotocol/keeper-contracts": "0.3.19",
|
||||
"@oceanprotocol/secret-store-client": "0.0.7",
|
||||
"bignumber.js": "^8.0.1",
|
||||
"eth-crypto": "^1.2.5",
|
||||
"eth-ecies": "^1.0.3",
|
||||
"ethereumjs-util": "^6.0.0",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"node-fetch": "^2.2.0",
|
||||
"node-fetch": "^2.2.1",
|
||||
"uuid": "^3.3.2",
|
||||
"web3": "1.0.0-beta.36",
|
||||
"web3-utils": "1.0.0-beta.36"
|
||||
"web3-utils": "1.0.0-beta.36",
|
||||
"whatwg-url": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.6",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/node": "^8.10.36",
|
||||
"@types/node": "^8.10.37",
|
||||
"chai": "^4.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"nyc": "^13.1.0",
|
||||
|
@ -70,6 +75,6 @@
|
|||
"ts-node": "^7.0.1",
|
||||
"tslint": "^5.11.0",
|
||||
"typedoc": "^0.13.0",
|
||||
"typescript": "^3.1.3"
|
||||
"typescript": "^3.1.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import {URL} from "whatwg-url"
|
||||
import DDO from "../ddo/DDO"
|
||||
import Config from "../models/Config"
|
||||
import Logger from "../utils/Logger"
|
||||
import AquariusConnectorProvider from "./AquariusConnectorProvider"
|
||||
import SearchQuery from "./query/SearchQuery"
|
||||
|
||||
export default class Aquarius {
|
||||
|
||||
|
@ -13,36 +16,37 @@ export default class Aquarius {
|
|||
|
||||
public async getAccessUrl(accessToken: any, payload: any): Promise<string> {
|
||||
|
||||
const accessUrl = await AquariusConnectorProvider.getConnector().post(
|
||||
`${accessToken.service_endpoint}/${accessToken.resource_id}`,
|
||||
payload)
|
||||
.then((response: any) => {
|
||||
const accessUrl: string = await AquariusConnectorProvider.getConnector()
|
||||
.post(`${accessToken.service_endpoint}/${accessToken.resource_id}`, payload)
|
||||
.then((response: any): string => {
|
||||
if (response.ok) {
|
||||
return response.text()
|
||||
}
|
||||
Logger.log("Failed: ", response.status, response.statusText)
|
||||
Logger.error("Failed: ", response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
.then((consumptionUrl: string) => {
|
||||
Logger.log("Success accessing consume endpoint: ", consumptionUrl)
|
||||
.then((consumptionUrl: string): string => {
|
||||
Logger.error("Success accessing consume endpoint: ", consumptionUrl)
|
||||
return consumptionUrl
|
||||
})
|
||||
.catch((error) => {
|
||||
Logger.error("Error fetching the data asset consumption url: ", error)
|
||||
return null
|
||||
})
|
||||
|
||||
return accessUrl
|
||||
}
|
||||
|
||||
public async queryMetadata(query): Promise<any[]> {
|
||||
public async queryMetadata(query: SearchQuery): Promise<any[]> {
|
||||
|
||||
const result = await AquariusConnectorProvider.getConnector().post(
|
||||
this.url + "/api/v1/aquarius/assets/ddo/query",
|
||||
JSON.stringify(query))
|
||||
const result = await AquariusConnectorProvider.getConnector()
|
||||
.post(this.url + "/api/v1/aquarius/assets/ddo/query", JSON.stringify(query))
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
Logger.log("Failed: ", response.status, response.statusText)
|
||||
Logger.error("queryMetadata failed:", response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
.catch((error) => {
|
||||
Logger.error("Error fetching querying metadata: ", error)
|
||||
|
@ -51,16 +55,21 @@ export default class Aquarius {
|
|||
return result
|
||||
}
|
||||
|
||||
public async queryMetadataByText(query): Promise<any[]> {
|
||||
public async queryMetadataByText(query: SearchQuery): Promise<any[]> {
|
||||
|
||||
const result = await AquariusConnectorProvider.getConnector().get(
|
||||
this.url + "/api/v1/aquarius/assets/ddo/query",
|
||||
JSON.stringify(query))
|
||||
const fullUrl = new URL(this.url + "/api/v1/aquarius/assets/ddo/query")
|
||||
fullUrl.searchParams.append("text", query.text)
|
||||
fullUrl.searchParams.append("sort", JSON.stringify(query.sort))
|
||||
fullUrl.searchParams.append("offset", query.offset.toString())
|
||||
fullUrl.searchParams.append("page", query.page.toString())
|
||||
const result = await AquariusConnectorProvider.getConnector()
|
||||
.get(fullUrl)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
Logger.log("Failed: ", response.status, response.statusText)
|
||||
Logger.log("queryMetadataByText failed:", response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
.catch((error) => {
|
||||
Logger.error("Error fetching querying metadata: ", error)
|
||||
|
@ -68,4 +77,44 @@ export default class Aquarius {
|
|||
|
||||
return result
|
||||
}
|
||||
|
||||
public async storeDDO(ddo: DDO): Promise<DDO> {
|
||||
const fullUrl = this.url + `/api/v1/aquarius/assets/ddo`
|
||||
const result: DDO = await AquariusConnectorProvider.getConnector()
|
||||
.post(fullUrl, DDO.serialize(ddo))
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
Logger.error("storeDDO failed:", response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
.catch((error) => {
|
||||
Logger.error("Error fetching querying metadata: ", error)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public async retrieveDDO(did: string): Promise<DDO> {
|
||||
const fullUrl = this.url + `/api/v1/aquarius/assets/ddo/${did}`
|
||||
const result = await AquariusConnectorProvider.getConnector()
|
||||
.get(fullUrl)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
Logger.log("retrieveDDO failed:", response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
.catch((error) => {
|
||||
Logger.error("Error fetching querying metadata: ", error)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public getServiceEndpoint(did) {
|
||||
return `${this.url}/api/v1/provider/assets/metadata/${did}`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import fetch from "node-fetch"
|
||||
import { URL } from "url"
|
||||
|
||||
export default class AquariusConnector {
|
||||
|
||||
public post(url, payload) {
|
||||
return fetch(url, {
|
||||
public async post(url, payload): Promise<any> {
|
||||
return this.fetch(url, {
|
||||
method: "POST",
|
||||
body: payload,
|
||||
headers: {
|
||||
|
@ -13,17 +12,26 @@ export default class AquariusConnector {
|
|||
})
|
||||
}
|
||||
|
||||
public get(url, payload) {
|
||||
const fullUrl = new URL(url)
|
||||
for (const key of Object.keys(payload)) {
|
||||
fullUrl.searchParams.append(key, payload[key])
|
||||
}
|
||||
return fetch(fullUrl, {
|
||||
public async get(url): Promise<any> {
|
||||
return this.fetch(url, {
|
||||
method: "GET",
|
||||
body: null,
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
public async put(url, payload): Promise<any> {
|
||||
return this.fetch(url, {
|
||||
method: "PUT",
|
||||
body: payload,
|
||||
headers: {
|
||||
"Content-type": "application/json",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
private async fetch(url, opts): Promise<any> {
|
||||
return fetch(url, opts)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
import Logger from "../utils/Logger"
|
||||
import AquariusConnector from "./AquariusConnector"
|
||||
|
||||
export default class AquariusConnectorProvider {
|
||||
|
||||
public static setConnector(connector: AquariusConnector) {
|
||||
|
||||
Logger.log("setting", typeof connector.constructor.name)
|
||||
|
||||
AquariusConnectorProvider.connector = connector
|
||||
}
|
||||
|
||||
public static getConnector() {
|
||||
public static getConnector(): AquariusConnector {
|
||||
|
||||
if (!AquariusConnectorProvider.connector) {
|
||||
AquariusConnectorProvider.connector = new AquariusConnector()
|
||||
}
|
||||
Logger.log("getting", typeof AquariusConnectorProvider.connector.constructor.name)
|
||||
return AquariusConnectorProvider.connector
|
||||
}
|
||||
|
||||
|
|
3
src/aquarius/query/Query.ts
Normal file
3
src/aquarius/query/Query.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default class Query {
|
||||
public value: number = 1
|
||||
}
|
14
src/aquarius/query/SearchQuery.ts
Normal file
14
src/aquarius/query/SearchQuery.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Query from "./Query"
|
||||
import Sort from "./Sort"
|
||||
|
||||
export default class SearchQuery {
|
||||
public offset: number = 100
|
||||
public page: number = 0
|
||||
public query: Query = {
|
||||
value: 1,
|
||||
} as Query
|
||||
public sort: Sort = {
|
||||
value: 1,
|
||||
} as Sort
|
||||
public text: string = "Office"
|
||||
}
|
3
src/aquarius/query/Sort.ts
Normal file
3
src/aquarius/query/Sort.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default class Sort {
|
||||
public value: number = 1
|
||||
}
|
15
src/ddo/AdditionalInformation.ts
Normal file
15
src/ddo/AdditionalInformation.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import StructuredMarkup from "./StructuredMarkup"
|
||||
|
||||
export default class AdditionalInformation {
|
||||
public updateFrecuency: string = "yearly"
|
||||
public structuredMarkup: StructuredMarkup[] = [
|
||||
{
|
||||
uri: "http://skos.um.es/unescothes/C01194/jsonld",
|
||||
mediaType: "application/ld+json",
|
||||
} as StructuredMarkup,
|
||||
{
|
||||
uri: "http://skos.um.es/unescothes/C01194/turtle",
|
||||
mediaType: "text/turtle",
|
||||
}as StructuredMarkup,
|
||||
]
|
||||
}
|
4
src/ddo/Authentication.ts
Normal file
4
src/ddo/Authentication.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default class Authentication {
|
||||
public type: string = "RsaSignatureAuthentication2018"
|
||||
public publicKey: string = "did:op:123456789abcdefghi#keys-1"
|
||||
}
|
8
src/ddo/Condition.ts
Normal file
8
src/ddo/Condition.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import Parameter from "./Parameter"
|
||||
|
||||
export default class Condition {
|
||||
public name: string = "lockPayment"
|
||||
public timeout: number = 0
|
||||
public conditionKey: string
|
||||
public parameters: Parameter[]
|
||||
}
|
5
src/ddo/Curation.ts
Normal file
5
src/ddo/Curation.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default class Curation {
|
||||
public rating: number = 0.93
|
||||
public numVotes: number = 123
|
||||
public schema: string = "Binary Votting"
|
||||
}
|
37
src/ddo/DDO.ts
Normal file
37
src/ddo/DDO.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import Authentication from "./Authentication"
|
||||
import PublicKey from "./PublicKey"
|
||||
import Service from "./Service"
|
||||
|
||||
export default class DDO {
|
||||
|
||||
public static serialize(ddo: DDO): string {
|
||||
return JSON.stringify(ddo, null, 2)
|
||||
}
|
||||
|
||||
public static deserialize(ddoString: string): DDO {
|
||||
const ddo = JSON.parse(ddoString)
|
||||
|
||||
return ddo as DDO
|
||||
}
|
||||
|
||||
public "@context": string = "https://w3id.org/future-method/v1"
|
||||
public id: string
|
||||
public publicKey: PublicKey[]
|
||||
public authentication: Authentication[]
|
||||
public service: Service[]
|
||||
|
||||
// @ts-ignore
|
||||
private assa: string
|
||||
|
||||
public constructor(ddo?: {
|
||||
id?: string,
|
||||
publicKey?: PublicKey[],
|
||||
authentication?: Authentication[],
|
||||
service?: Service[],
|
||||
}) {
|
||||
this.id = ddo ? ddo.id ? ddo.id : null : null
|
||||
this.publicKey = ddo ? ddo.publicKey ? ddo.publicKey : [] : []
|
||||
this.authentication = ddo ? ddo.authentication ? ddo.authentication : [] : []
|
||||
this.service = ddo ? ddo.service ? ddo.service : [] : []
|
||||
}
|
||||
}
|
54
src/ddo/MetaData.ts
Normal file
54
src/ddo/MetaData.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import AdditionalInformation from "./AdditionalInformation"
|
||||
import Curation from "./Curation"
|
||||
import MetaDataBase from "./MetaDataBase"
|
||||
import StructuredMarkup from "./StructuredMarkup"
|
||||
|
||||
export default class MetaData {
|
||||
|
||||
public base: MetaDataBase = {
|
||||
name: "UK Weather information 2011",
|
||||
type: "dataset",
|
||||
description: "Weather information of UK including temperature and humidity",
|
||||
size: "3.1gb",
|
||||
dateCreated: "2012-02-01T10:55:11+00:00",
|
||||
author: "Met Office",
|
||||
license: "CC-BY",
|
||||
copyrightHolder: "Met Office",
|
||||
encoding: "UTF-8",
|
||||
compression: "zip",
|
||||
contentType: "text/csv",
|
||||
// tslint:disable-next-line
|
||||
workExample: "stationId,latitude,longitude,datetime,temperature,humidity423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
|
||||
contentUrls: [
|
||||
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
|
||||
],
|
||||
links: [
|
||||
{sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"},
|
||||
{sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"},
|
||||
{fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/"},
|
||||
],
|
||||
inLanguage: "en",
|
||||
tags: "weather, uk, 2011, temperature, humidity",
|
||||
price: 10,
|
||||
} as MetaDataBase
|
||||
|
||||
public curation: Curation = {
|
||||
rating: 0.93,
|
||||
numVotes: 123,
|
||||
schema: "Binary Votting",
|
||||
} as Curation
|
||||
|
||||
public additionalInformation: AdditionalInformation = {
|
||||
updateFrecuency: "yearly",
|
||||
structuredMarkup: [
|
||||
{
|
||||
uri: "http://skos.um.es/unescothes/C01194/jsonld",
|
||||
mediaType: "application/ld+json",
|
||||
} as StructuredMarkup,
|
||||
{
|
||||
uri: "http://skos.um.es/unescothes/C01194/turtle",
|
||||
mediaType: "text/turtle",
|
||||
} as StructuredMarkup,
|
||||
],
|
||||
} as AdditionalInformation
|
||||
}
|
31
src/ddo/MetaDataBase.ts
Normal file
31
src/ddo/MetaDataBase.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
export default class MetaDataBase {
|
||||
public name: string = "UK Weather information 2011"
|
||||
public type: string = "dataset"
|
||||
public description: string = "Weather information of UK including temperature and humidity"
|
||||
public size: string = "3.1gb"
|
||||
public dateCreated: string = "2012-10-10T17:00:000Z"
|
||||
public author: string = "Met Office"
|
||||
public license: string = "CC-BY"
|
||||
public copyrightHolder: string = "Met Office"
|
||||
public encoding: string = "UTF-8"
|
||||
public compression: string = "zip"
|
||||
public contentType: string = "text/csv"
|
||||
public workExample: string = "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68"
|
||||
public contentUrls: string[] = [
|
||||
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
|
||||
]
|
||||
public links: any[] = [
|
||||
{
|
||||
sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/",
|
||||
},
|
||||
{
|
||||
sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/",
|
||||
},
|
||||
{
|
||||
fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/",
|
||||
},
|
||||
]
|
||||
public inLanguage: string = "en"
|
||||
public tags: string = "weather, uk, 2011, temperature, humidity"
|
||||
public price: number = 10
|
||||
}
|
5
src/ddo/Parameter.ts
Normal file
5
src/ddo/Parameter.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default class Parameter {
|
||||
public name: string
|
||||
public type: string
|
||||
public value: any
|
||||
}
|
7
src/ddo/PublicKey.ts
Normal file
7
src/ddo/PublicKey.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default class PublicKey {
|
||||
public id: string = "did:op:123456789abcdefghi#keys-1"
|
||||
public type: string = "RsaVerificationKey2018"
|
||||
public owner: string = "did:op:123456789abcdefghi"
|
||||
public publicKeyPem?: string = "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
|
||||
public publicKeyBase58?: string = "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
|
||||
}
|
13
src/ddo/Service.ts
Normal file
13
src/ddo/Service.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Condition from "./Condition"
|
||||
import MetaData from "./MetaData"
|
||||
|
||||
export default class Service {
|
||||
public type: string = "OpenIdConnectVersion1.0Service"
|
||||
public serviceDefinitionId?: string
|
||||
public templateId?: string
|
||||
public serviceEndpoint: string = "https://openid.example.com/"
|
||||
public purchaseEndpoint?: string
|
||||
public description?: string = "My public social inbox"
|
||||
public metadata?: MetaData = {} as MetaData
|
||||
public conditions?: Condition[] = []
|
||||
}
|
4
src/ddo/StructuredMarkup.ts
Normal file
4
src/ddo/StructuredMarkup.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default class StructuredMarkup {
|
||||
public uri: string = "http://skos.um.es/unescothes/C01194/jsonld"
|
||||
public mediaType: string = "application/ld+json"
|
||||
}
|
20
src/examples/Register.ts
Normal file
20
src/examples/Register.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import DDO from "../ddo/DDO"
|
||||
import MetaData from "../ddo/MetaData"
|
||||
import {Account, Logger, Ocean} from "../squid"
|
||||
|
||||
(async () => {
|
||||
const ocean: Ocean = await Ocean.getInstance({
|
||||
nodeUri: "http://localhost:8545",
|
||||
aquariusUri: "http://localhost:5000",
|
||||
parityUri: "http://localhost:9545",
|
||||
secretStoreUri: "https://secret-store.dev-ocean.com",
|
||||
threshold: 2,
|
||||
password: "unittest",
|
||||
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
|
||||
})
|
||||
|
||||
const publisher: Account = (await ocean.getAccounts())[0]
|
||||
|
||||
const ddo: DDO = await ocean.registerAsset(new MetaData(), publisher)
|
||||
Logger.log(ddo.id)
|
||||
})()
|
|
@ -3,63 +3,28 @@ import Logger from "../utils/Logger"
|
|||
import Keeper from "./Keeper"
|
||||
import Web3Provider from "./Web3Provider"
|
||||
|
||||
const contracts: Map<string, Contract> = new Map<string, Contract>()
|
||||
|
||||
export default class ContractHandler {
|
||||
|
||||
public static async get(what: string): Contract {
|
||||
const where = (await (await Keeper.getInstance()).getNetworkName()).toLowerCase()
|
||||
try {
|
||||
return contracts.get(what) || await ContractHandler.load(what, where)
|
||||
return ContractHandler.contracts.get(what) || await ContractHandler.load(what, where)
|
||||
} catch (err) {
|
||||
Logger.error("Failed to load", what, "from", where, err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
public static async deployContracts() {
|
||||
Logger.log("Trying to deploy contracts")
|
||||
|
||||
const web3 = Web3Provider.getWeb3()
|
||||
|
||||
const deployerAddress = (await web3.eth.getAccounts())[0]
|
||||
|
||||
// deploy libs
|
||||
/* not part of trilobite
|
||||
const dll = await ContractHandler.deployContract("DLL", deployerAddress)
|
||||
const attributeStore = await ContractHandler.deployContract("AttributeStore", deployerAddress)
|
||||
*/
|
||||
// deploy contracts
|
||||
const token = await ContractHandler.deployContract("OceanToken", deployerAddress)
|
||||
/* not part of trilobite
|
||||
const plcrVoting = await ContractHandler.deployContract("PLCRVoting", deployerAddress, {
|
||||
args: [token.options.address],
|
||||
tokens: [
|
||||
{
|
||||
name: "DLL", address: dll.options.address,
|
||||
}, {
|
||||
name: "AttributeStore", address: attributeStore.options.address,
|
||||
},
|
||||
],
|
||||
})
|
||||
/* not part of trilobite
|
||||
const registry = await ContractHandler.deployContract("OceanRegistry", deployerAddress, {
|
||||
args: [token.options.address, plcrVoting.options.address],
|
||||
})
|
||||
*/
|
||||
const market = await ContractHandler.deployContract("OceanMarket", deployerAddress, {
|
||||
args: [token.options.address],
|
||||
})
|
||||
/* not part of trilobite
|
||||
const dispute = await ContractHandler.deployContract("OceanDispute", deployerAddress, {
|
||||
args: [market.options.address, registry.options.address, plcrVoting.options.address],
|
||||
})
|
||||
*/
|
||||
await ContractHandler.deployContract("OceanAuth", deployerAddress, {
|
||||
args: [market.options.address],
|
||||
})
|
||||
public static set(name: string, contractInstance: Contract) {
|
||||
ContractHandler.contracts.set(name, contractInstance)
|
||||
}
|
||||
|
||||
public static has(name: string): boolean {
|
||||
return ContractHandler.contracts.has(name)
|
||||
}
|
||||
|
||||
private static contracts: Map<string, Contract> = new Map<string, Contract>()
|
||||
|
||||
private static async load(what: string, where: string): Promise<Contract> {
|
||||
const web3 = Web3Provider.getWeb3()
|
||||
// Logger.log("Loading", what, "from", where)
|
||||
|
@ -73,56 +38,7 @@ export default class ContractHandler {
|
|||
// Logger.log("Getting instance of", what, "from", where, "at", artifact.address)
|
||||
const contract = new web3.eth.Contract(artifact.abi, artifact.address)
|
||||
Logger.log("Loaded", what, "from", where)
|
||||
contracts.set(what, contract)
|
||||
return contracts.get(what)
|
||||
}
|
||||
|
||||
// todo: reactivate for tethys
|
||||
private static replaceTokens(bytecode: string, tokens: any[]) {
|
||||
|
||||
for (const token of tokens) {
|
||||
|
||||
bytecode = bytecode.replace(
|
||||
new RegExp(`_+${token.name}_+`, "g"),
|
||||
token.address.replace("0x", ""))
|
||||
}
|
||||
// Logger.log(bytecode)
|
||||
|
||||
return bytecode.toString()
|
||||
}
|
||||
|
||||
private static async deployContract(name: string, from: string, params?): Promise<Contract> {
|
||||
|
||||
// dont redeploy if there is already something loaded
|
||||
if (contracts.has(name)) {
|
||||
return contracts.get(name)
|
||||
}
|
||||
|
||||
const web3 = Web3Provider.getWeb3()
|
||||
|
||||
let contractInstance: Contract
|
||||
try {
|
||||
Logger.log("Deploying", name)
|
||||
|
||||
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${name}.development.json`)
|
||||
const tempContract = new web3.eth.Contract(artifact.abi, artifact.address)
|
||||
contractInstance = await tempContract.deploy({
|
||||
data: params && params.tokens ?
|
||||
ContractHandler.replaceTokens(artifact.bytecode.toString(), params.tokens) :
|
||||
artifact.bytecode,
|
||||
arguments: params && params.args ? params.args : null,
|
||||
}).send({
|
||||
from,
|
||||
gas: 3000000,
|
||||
gasPrice: 10000000000,
|
||||
})
|
||||
contracts.set(name, contractInstance)
|
||||
// Logger.log("Deployed", name, "at", contractInstance.options.address);
|
||||
} catch (err) {
|
||||
Logger.error("Deployment failed for", name, "with params", JSON.stringify(params, null, 2), err.message)
|
||||
throw err
|
||||
}
|
||||
|
||||
return contractInstance
|
||||
ContractHandler.contracts.set(what, contract)
|
||||
return ContractHandler.contracts.get(what)
|
||||
}
|
||||
}
|
||||
|
|
18
src/keeper/ContractReflector.ts
Normal file
18
src/keeper/ContractReflector.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import MethodReflection from "../models/MethodReflection"
|
||||
import GenericContract from "./contracts/GenericContract"
|
||||
|
||||
export default class ContractReflector {
|
||||
|
||||
public static async reflectContractMethod(pathToMethod: string): Promise<MethodReflection> {
|
||||
const parts: string[] = pathToMethod.split(".")
|
||||
|
||||
const contract = await GenericContract.getInstance(parts[0])
|
||||
return {
|
||||
contractName: parts[0],
|
||||
methodName: parts[1],
|
||||
address: contract.getAddress(),
|
||||
signature: contract.getSignatureOfMethod(parts[1]),
|
||||
inputs: contract.getInputsOfMethod(parts[1]),
|
||||
} as MethodReflection
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
import OceanAuth from "./Auth"
|
||||
import OceanMarket from "./Market"
|
||||
import OceanToken from "./Token"
|
||||
import OceanAuth from "./contracts/Auth"
|
||||
import AccessConditions from "./contracts/conditions/AccessConditions"
|
||||
import DIDRegistry from "./contracts/DIDRegistry"
|
||||
import OceanMarket from "./contracts/Market"
|
||||
import ServiceAgreement from "./contracts/ServiceAgreement"
|
||||
import OceanToken from "./contracts/Token"
|
||||
import Web3Provider from "./Web3Provider"
|
||||
|
||||
export default class Keeper {
|
||||
|
@ -13,6 +16,9 @@ export default class Keeper {
|
|||
Keeper.instance.market = await OceanMarket.getInstance()
|
||||
Keeper.instance.auth = await OceanAuth.getInstance()
|
||||
Keeper.instance.token = await OceanToken.getInstance()
|
||||
Keeper.instance.serviceAgreement = await ServiceAgreement.getInstance()
|
||||
Keeper.instance.accessConditions = await AccessConditions.getInstance()
|
||||
Keeper.instance.didRegistry = await DIDRegistry.getInstance()
|
||||
}
|
||||
return Keeper.instance
|
||||
}
|
||||
|
@ -22,6 +28,9 @@ export default class Keeper {
|
|||
public token: OceanToken
|
||||
public market: OceanMarket
|
||||
public auth: OceanAuth
|
||||
public serviceAgreement: ServiceAgreement
|
||||
public accessConditions: AccessConditions
|
||||
public didRegistry: DIDRegistry
|
||||
|
||||
public async getNetworkName(): Promise<string> {
|
||||
return Web3Provider.getWeb3().eth.net.getId()
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import * as Web3 from "web3"
|
||||
import ConfigProvider from "../ConfigProvider"
|
||||
import Logger from "../utils/Logger"
|
||||
|
||||
Logger.log("using web3", Web3.version)
|
||||
|
||||
export default class Web3Provider {
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import {Receipt} from "web3-utils"
|
||||
import AccessStatus from "../models/AccessStatus"
|
||||
import Asset from "../ocean/Asset"
|
||||
import Order from "../ocean/Order"
|
||||
import AccessStatus from "../../models/AccessStatus"
|
||||
import ContractBase from "./ContractBase"
|
||||
|
||||
export default class OceanAuth extends ContractBase {
|
||||
|
@ -25,23 +23,12 @@ export default class OceanAuth extends ContractBase {
|
|||
return this.call("getEncryptedAccessToken", [orderId], consumerAddress)
|
||||
}
|
||||
|
||||
public async initiateAccessRequest(asset: Asset, publicKey: string,
|
||||
timeout: number, buyerAddress: string): Promise<Receipt> {
|
||||
const args = [asset.getId(), asset.publisher.getId(), publicKey, timeout]
|
||||
return this.sendTransaction("initiateAccessRequest", buyerAddress, args)
|
||||
}
|
||||
|
||||
public async commitAccessRequest(order: Order, publisherAddress: string) {
|
||||
const args = [order.getId(), true, 9999999999, "discovery", "read", "slaLink", "slaType"]
|
||||
return this.sendTransaction("commitAccessRequest", publisherAddress, args)
|
||||
}
|
||||
|
||||
public async getTempPubKey(orderId: string) {
|
||||
return this.call("getTempPubKey", [orderId])
|
||||
}
|
||||
|
||||
public async deliverAccessToken(orderId: string, accessToken: string, publisherAddress: string) {
|
||||
return this.sendTransaction("deliverAccessToken", publisherAddress, [orderId, accessToken])
|
||||
return this.send("deliverAccessToken", publisherAddress, [orderId, accessToken])
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import Event from "web3"
|
||||
import Contract from "web3-eth-contract"
|
||||
import Logger from "../utils/Logger"
|
||||
import ContractHandler from "./ContractHandler"
|
||||
import {Receipt} from "web3-utils"
|
||||
import Logger from "../../utils/Logger"
|
||||
import ContractHandler from "../ContractHandler"
|
||||
|
||||
export default abstract class ContractBase {
|
||||
|
||||
|
@ -32,7 +33,7 @@ export default abstract class ContractBase {
|
|||
|
||||
public async getEventData(eventName: any, options: any): Promise<Event[]> {
|
||||
if (!this.contract.events[eventName]) {
|
||||
throw new Error(`Event ${eventName} not found on contract ${this.contractName}`)
|
||||
throw new Error(`Event "${eventName}" not found on contract "${this.contractName}"`)
|
||||
}
|
||||
return this.contract.getPastEvents(eventName, options)
|
||||
}
|
||||
|
@ -41,13 +42,23 @@ export default abstract class ContractBase {
|
|||
return this.contract.options.address
|
||||
}
|
||||
|
||||
public getSignatureOfMethod(methodName: string): string {
|
||||
const foundMethod = this.searchMethod(methodName)
|
||||
return foundMethod.signature
|
||||
}
|
||||
|
||||
public getInputsOfMethod(methodName: string): any[] {
|
||||
const foundMethod = this.searchMethod(methodName)
|
||||
return foundMethod.inputs
|
||||
}
|
||||
|
||||
protected async init() {
|
||||
this.contract = await ContractHandler.get(this.contractName)
|
||||
}
|
||||
|
||||
protected async sendTransaction(name: string, from: string, args: any[]) {
|
||||
protected async send(name: string, from: string, args: any[]): Promise<Receipt> {
|
||||
if (!this.contract.methods[name]) {
|
||||
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
|
||||
throw new Error(`Method "${name}" is not part of contract "${this.contractName}"`)
|
||||
}
|
||||
try {
|
||||
const tx = this.contract.methods[name](...args)
|
||||
|
@ -60,13 +71,13 @@ export default abstract class ContractBase {
|
|||
})
|
||||
} catch (err) {
|
||||
const argString = JSON.stringify(args, null, 2)
|
||||
Logger.error(`Sending transaction ${name} on contract ${this.contractName} failed.`)
|
||||
Logger.error(`Sending transaction "${name}" on contract "${this.contractName}" failed.`)
|
||||
Logger.error(`Args: ${argString} From: ${from}`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
protected async call(name: string, args: any[], from?: string) {
|
||||
protected async call(name: string, args: any[], from?: string): Promise<any> {
|
||||
if (!this.contract.methods[name]) {
|
||||
throw new Error(`Method ${name} is not part of contract ${this.contractName}`)
|
||||
}
|
||||
|
@ -74,9 +85,20 @@ export default abstract class ContractBase {
|
|||
const method = this.contract.methods[name](...args)
|
||||
return method.call(from ? {from} : null)
|
||||
} catch (err) {
|
||||
Logger.error(`Calling method ${name} on contract ${this.contractName} failed. Args: ${args}`, err)
|
||||
Logger.error(`Calling method "${name}" on contract "${this.contractName}" failed. Args: ${args}`, err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
private searchMethod(methodName): any {
|
||||
const foundMethod = this.contract.options.jsonInterface.find((method) => {
|
||||
if (method.name === methodName) {
|
||||
return method
|
||||
}
|
||||
})
|
||||
if (!foundMethod) {
|
||||
throw new Error(`Method "${methodName}" is not part of contract "${this.contractName}"`)
|
||||
}
|
||||
return foundMethod
|
||||
}
|
||||
}
|
38
src/keeper/contracts/DIDRegistry.ts
Normal file
38
src/keeper/contracts/DIDRegistry.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import {Receipt} from "web3-utils"
|
||||
import ValueType from "../../models/ValueType"
|
||||
import Web3Provider from "../Web3Provider"
|
||||
import ContractBase from "./ContractBase"
|
||||
|
||||
export default class DIDRegistry extends ContractBase {
|
||||
|
||||
public static async getInstance(): Promise<DIDRegistry> {
|
||||
const didRegistry: DIDRegistry = new DIDRegistry("DIDRegistry")
|
||||
await didRegistry.init()
|
||||
return didRegistry
|
||||
}
|
||||
|
||||
public async registerAttribute(did: string, type: ValueType, key: string,
|
||||
value: string, ownerAddress: string): Promise<Receipt> {
|
||||
|
||||
return this.send("registerAttribute",
|
||||
ownerAddress, ["0x" + did, type, Web3Provider.getWeb3().utils.fromAscii(key), value],
|
||||
)
|
||||
}
|
||||
|
||||
public async getOwner(did: string): Promise<string> {
|
||||
|
||||
return this.call("getOwner",
|
||||
["0x" + did],
|
||||
)
|
||||
}
|
||||
|
||||
public async getUpdateAt(did: string): Promise<number> {
|
||||
|
||||
const blockNum = await this.call("getUpdateAt",
|
||||
["0x" + did],
|
||||
)
|
||||
|
||||
return parseInt(blockNum, 10)
|
||||
}
|
||||
|
||||
}
|
14
src/keeper/contracts/GenericContract.ts
Normal file
14
src/keeper/contracts/GenericContract.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import ContractBase from "./ContractBase"
|
||||
|
||||
export default class GenericContract extends ContractBase {
|
||||
|
||||
public static async getInstance(contractName: string): Promise<ContractBase> {
|
||||
const contract: GenericContract = new GenericContract(contractName)
|
||||
await contract.init()
|
||||
return contract
|
||||
}
|
||||
|
||||
private constructor(contractName: string) {
|
||||
super(contractName)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import BigNumber from "bignumber.js"
|
||||
import {Receipt} from "web3-utils"
|
||||
import Order from "../ocean/Order"
|
||||
import ContractBase from "./ContractBase"
|
||||
|
||||
export default class OceanMarket extends ContractBase {
|
||||
|
@ -13,7 +12,7 @@ export default class OceanMarket extends ContractBase {
|
|||
|
||||
// call functions (costs no gas)
|
||||
public async isAssetActive(assetId: string): Promise<boolean> {
|
||||
return this.call("checkAsset", [assetId])
|
||||
return this.call("checkAsset", ["0x" + assetId])
|
||||
}
|
||||
|
||||
public async verifyOrderPayment(orderId: string): Promise<boolean> {
|
||||
|
@ -21,12 +20,12 @@ export default class OceanMarket extends ContractBase {
|
|||
}
|
||||
|
||||
public async getAssetPrice(assetId: string): Promise<number> {
|
||||
return this.call("getAssetPrice", [assetId])
|
||||
return this.call("getAssetPrice", ["0x" + assetId])
|
||||
.then((price: string) => new BigNumber(price).toNumber())
|
||||
}
|
||||
|
||||
public async requestTokens(amount: number, receiverAddress: string): Promise<Receipt> {
|
||||
return this.sendTransaction("requestTokens", receiverAddress, [amount])
|
||||
return this.send("requestTokens", receiverAddress, [amount])
|
||||
}
|
||||
|
||||
public async generateId(input: string): Promise<string> {
|
||||
|
@ -34,14 +33,6 @@ export default class OceanMarket extends ContractBase {
|
|||
}
|
||||
|
||||
public async register(assetId: string, price: number, publisherAddress: string): Promise<Receipt> {
|
||||
return this.sendTransaction("register", publisherAddress, [assetId, price])
|
||||
}
|
||||
|
||||
public async payOrder(order: Order, publisherAddress: string,
|
||||
price: number, consumerAddress: string,
|
||||
timeout: number): Promise<Receipt> {
|
||||
return this.sendTransaction("sendPayment", consumerAddress, [
|
||||
order.getId(), publisherAddress, price, timeout,
|
||||
])
|
||||
return this.send("register", publisherAddress, ["0x" + assetId, price])
|
||||
}
|
||||
}
|
48
src/keeper/contracts/ServiceAgreement.ts
Normal file
48
src/keeper/contracts/ServiceAgreement.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import {Receipt} from "web3-utils"
|
||||
import MethodReflection from "../../models/MethodReflection"
|
||||
import ContractBase from "./ContractBase"
|
||||
|
||||
export default class ServiceAgreement extends ContractBase {
|
||||
|
||||
public static async getInstance(): Promise<ServiceAgreement> {
|
||||
const serviceAgreement: ServiceAgreement = new ServiceAgreement("ServiceAgreement")
|
||||
await serviceAgreement.init()
|
||||
return serviceAgreement
|
||||
}
|
||||
|
||||
public async setupAgreementTemplate(templateId: string, methodReflections: MethodReflection[],
|
||||
dependencyMatrix: number[], name: any, ownerAddress: string)
|
||||
: Promise<Receipt> {
|
||||
|
||||
return this.send("setupAgreementTemplate", ownerAddress, [
|
||||
templateId, methodReflections.map((r) => r.address),
|
||||
methodReflections.map((r) => r.signature), dependencyMatrix, name, [0], 0,
|
||||
])
|
||||
}
|
||||
|
||||
public async getTemplateStatus(templateId: string) {
|
||||
|
||||
return this.call("getTemplateStatus", [templateId])
|
||||
}
|
||||
|
||||
public async getTemplateOwner(templateId: string) {
|
||||
|
||||
return this.call("getTemplateOwner", [templateId])
|
||||
}
|
||||
|
||||
public async getAgreementStatus(serviceDefinitionId: string) {
|
||||
|
||||
return this.call("getAgreementStatus", [serviceDefinitionId])
|
||||
}
|
||||
|
||||
public async executeAgreement(serviceAgreementTemplateId: string, serviceAgreementSignatureHash: string,
|
||||
consumerAddress: string, valueHashes: string[], timeoutValues: number[],
|
||||
serviceAgreementId: string, did: string, publisherAddress: string):
|
||||
Promise<Receipt> {
|
||||
|
||||
return this.send("executeAgreement", publisherAddress, [
|
||||
serviceAgreementTemplateId, serviceAgreementSignatureHash, consumerAddress, valueHashes,
|
||||
timeoutValues, "0x" + serviceAgreementId, "0x" + did.replace("did:op:", ""),
|
||||
])
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ export default class OceanToken extends ContractBase {
|
|||
}
|
||||
|
||||
public async approve(marketAddress: string, price: number, buyerAddress: string): Promise<Receipt> {
|
||||
return this.sendTransaction("approve", buyerAddress, [marketAddress, price])
|
||||
return this.send("approve", buyerAddress, [marketAddress, price])
|
||||
}
|
||||
|
||||
public async balanceOf(address: string): Promise<number> {
|
18
src/keeper/contracts/conditions/AccessConditions.ts
Normal file
18
src/keeper/contracts/conditions/AccessConditions.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {Receipt} from "web3-utils"
|
||||
import ContractBase from "../ContractBase"
|
||||
|
||||
export default class AccessConditions extends ContractBase {
|
||||
|
||||
public static async getInstance(): Promise<AccessConditions> {
|
||||
const accessConditions: AccessConditions = new AccessConditions("AccessConditions")
|
||||
await accessConditions.init()
|
||||
return accessConditions
|
||||
}
|
||||
|
||||
public async grantAccess(serviceAgreementId: any, assetId: any, documentKeyId: any, publisherAddress: string)
|
||||
: Promise<Receipt> {
|
||||
return this.send("grantAccess", publisherAddress, [
|
||||
serviceAgreementId, "0x" + assetId, "0x" + documentKeyId,
|
||||
])
|
||||
}
|
||||
}
|
10
src/keeper/contracts/conditions/PaymentConditions.ts
Normal file
10
src/keeper/contracts/conditions/PaymentConditions.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import ContractBase from "../ContractBase"
|
||||
|
||||
export default class PaymentConditions extends ContractBase {
|
||||
|
||||
public static async getInstance(): Promise<PaymentConditions> {
|
||||
const paymentConditions: PaymentConditions = new PaymentConditions("PaymentConditions")
|
||||
await paymentConditions.init()
|
||||
return paymentConditions
|
||||
}
|
||||
}
|
|
@ -1,5 +1,23 @@
|
|||
export default class Config {
|
||||
/* Aquarius Config */
|
||||
// the url to the aquarius
|
||||
public aquariusUri: string
|
||||
public nodeUri: string
|
||||
public web3Provider: any
|
||||
|
||||
/* Keeper Config */
|
||||
// the uri to the node we want to connect to, not need if web3Provider is set
|
||||
public nodeUri?: string
|
||||
// from outside eg. metamask
|
||||
public web3Provider?: any
|
||||
|
||||
/* Secret Store Config */
|
||||
// the uri of the secret store to connect to
|
||||
public secretStoreUri: string
|
||||
// the uri of the parity node to connect to
|
||||
public parityUri: string
|
||||
// the password of the account in the local parity node to sign the serverKeyId
|
||||
public password: string
|
||||
// the address of the account in the local parity node to sign the serverKeyId
|
||||
public address: string
|
||||
// the number of nodes in the secret store that have to agree on changes
|
||||
public threshold: number
|
||||
}
|
||||
|
|
7
src/models/MethodReflection.ts
Normal file
7
src/models/MethodReflection.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default class MethodReflection {
|
||||
public contractName: string
|
||||
public methodName: string
|
||||
public address: string
|
||||
public signature: string
|
||||
public inputs: any[]
|
||||
}
|
4
src/models/ValuePair.ts
Normal file
4
src/models/ValuePair.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default class ValuePair {
|
||||
public type: string
|
||||
public value: any
|
||||
}
|
8
src/models/ValueType.ts
Normal file
8
src/models/ValueType.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
enum ValueType {
|
||||
DID, // DID string e.g. 'did:op:xxx'
|
||||
DIDRef, // hash of DID same as in parameter (bytes32 _did) in text 0x0123abc.. or 0123abc..
|
||||
URL, // URL string e.g. 'http(s)://xx'
|
||||
DDO, // DDO string in JSON e.g. '{ "id": "did:op:xxx"...
|
||||
}
|
||||
|
||||
export default ValueType
|
|
@ -1,56 +0,0 @@
|
|||
import * as EthCrypto from "eth-crypto"
|
||||
import * as EthjsUtil from "ethereumjs-util"
|
||||
import Keeper from "../keeper/Keeper"
|
||||
import Logger from "../utils/Logger"
|
||||
import Account from "./Account"
|
||||
import OceanBase from "./OceanBase"
|
||||
import Order from "./Order"
|
||||
|
||||
export default class Asset extends OceanBase {
|
||||
|
||||
constructor(public name: string,
|
||||
public description: string,
|
||||
public price: number,
|
||||
public publisher: Account) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async purchase(consumer: Account, timeout: number): Promise<Order> {
|
||||
const {token, market, auth} = await Keeper.getInstance()
|
||||
|
||||
const key = EthCrypto.createIdentity()
|
||||
const publicKey = EthjsUtil.privateToPublic(key.privateKey).toString("hex")
|
||||
const price = await market.getAssetPrice(this.getId())
|
||||
const isValid = await market.isAssetActive(this.getId())
|
||||
|
||||
Logger.log("The asset:", this.getId(), "is it valid?", isValid, "it's price is:", price)
|
||||
|
||||
if (!isValid) {
|
||||
throw new Error("The Asset is not valid!")
|
||||
}
|
||||
try {
|
||||
const marketAddr = market.getAddress()
|
||||
// Allow market contract to transfer funds on the consumer"s behalf
|
||||
await token.approve(marketAddr, price, consumer.getId())
|
||||
Logger.log(`${price} tokens approved on market with id: ${marketAddr}`)
|
||||
} catch (err) {
|
||||
Logger.error("token.approve failed", err)
|
||||
}
|
||||
let order: Order
|
||||
try {
|
||||
// Submit the access request
|
||||
const initiateAccessRequestReceipt = await auth.initiateAccessRequest(this,
|
||||
publicKey, timeout, consumer.getId())
|
||||
|
||||
const {returnValues} = initiateAccessRequestReceipt.events.AccessConsentRequested
|
||||
Logger.log(`Keeper AccessConsentRequested event received on asset: ${this.getId()}`)
|
||||
order = new Order(this, returnValues._timeout, returnValues._pubKey, key)
|
||||
order.setId(returnValues._id)
|
||||
} catch (err) {
|
||||
Logger.error("auth.initiateAccessRequest failed", err)
|
||||
}
|
||||
|
||||
return order
|
||||
}
|
||||
|
||||
}
|
12
src/ocean/IdGenerator.ts
Normal file
12
src/ocean/IdGenerator.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import * as v4 from "uuid/v4"
|
||||
|
||||
export default class IdGenerator {
|
||||
public static generateId(): string {
|
||||
const id = `${v4()}${v4()}`
|
||||
return id.replace(/-/g, "")
|
||||
}
|
||||
|
||||
public static generatePrefixedId() {
|
||||
return "0x" + this.generateId()
|
||||
}
|
||||
}
|
|
@ -1,29 +1,45 @@
|
|||
import Aquarius from "../aquarius/Aquarius"
|
||||
import AquariusProvider from "../aquarius/AquariusProvider"
|
||||
import SearchQuery from "../aquarius/query/SearchQuery"
|
||||
import ConfigProvider from "../ConfigProvider"
|
||||
import DDOCondition from "../ddo/Condition"
|
||||
import DDO from "../ddo/DDO"
|
||||
import MetaData from "../ddo/MetaData"
|
||||
import Parameter from "../ddo/Parameter"
|
||||
import Service from "../ddo/Service"
|
||||
import Keeper from "../keeper/Keeper"
|
||||
import Web3Provider from "../keeper/Web3Provider"
|
||||
import Config from "../models/Config"
|
||||
import ValuePair from "../models/ValuePair"
|
||||
import ValueType from "../models/ValueType"
|
||||
import Logger from "../utils/Logger"
|
||||
import Account from "./Account"
|
||||
import Asset from "./Asset"
|
||||
import Order from "./Order"
|
||||
import IdGenerator from "./IdGenerator"
|
||||
import Condition from "./ServiceAgreements/Condition"
|
||||
import ServiceAgreement from "./ServiceAgreements/ServiceAgreement"
|
||||
import ServiceAgreementTemplate from "./ServiceAgreements/ServiceAgreementTemplate"
|
||||
import Access from "./ServiceAgreements/Templates/Access"
|
||||
|
||||
export default class Ocean {
|
||||
|
||||
public static async getInstance(config) {
|
||||
public static async getInstance(config: Config) {
|
||||
|
||||
if (!Ocean.instance) {
|
||||
ConfigProvider.setConfig(config)
|
||||
Ocean.instance = new Ocean(await Keeper.getInstance())
|
||||
Ocean.instance = new Ocean()
|
||||
Ocean.instance.keeper = await Keeper.getInstance()
|
||||
Ocean.instance.aquarius = await AquariusProvider.getAquarius()
|
||||
}
|
||||
|
||||
return Ocean.instance
|
||||
}
|
||||
|
||||
private static instance = null
|
||||
private keeper: Keeper
|
||||
|
||||
private constructor(keeper: Keeper) {
|
||||
this.keeper = keeper
|
||||
private keeper: Keeper
|
||||
private aquarius: Aquarius
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public async getAccounts(): Promise<Account[]> {
|
||||
|
@ -34,62 +50,102 @@ export default class Ocean {
|
|||
return ethAccounts.map((address: string) => new Account(address))
|
||||
}
|
||||
|
||||
public async register(asset: Asset): Promise<string> {
|
||||
const {market} = this.keeper
|
||||
public async registerAsset(metadata: MetaData, publisher: Account): Promise<DDO> {
|
||||
|
||||
// generate an id
|
||||
const assetId = await market.generateId(asset.name + asset.description)
|
||||
Logger.log(`Registering: ${assetId} with price ${asset.price} for ${asset.publisher.getId()}`)
|
||||
asset.setId(assetId)
|
||||
const isAssetActive = await market.isAssetActive(assetId)
|
||||
// register asset in the market
|
||||
if (!isAssetActive) {
|
||||
const result = await market.register(asset.getId(), asset.price, asset.publisher.getId())
|
||||
Logger.log("Registered:", assetId, "in block", result.blockNumber)
|
||||
} else {
|
||||
throw new Error("Asset already registered")
|
||||
}
|
||||
return assetId
|
||||
}
|
||||
const {didRegistry} = this.keeper
|
||||
|
||||
public async getOrdersByAccount(consumer: Account): Promise<Order[]> {
|
||||
const {auth} = this.keeper
|
||||
const id: string = IdGenerator.generateId()
|
||||
const did: string = `did:op:${id}`
|
||||
const serviceDefinitionId: string = IdGenerator.generatePrefixedId()
|
||||
|
||||
Logger.log("Getting orders")
|
||||
metadata.base.contentUrls = metadata.base.contentUrls.map((contentUrl) => {
|
||||
|
||||
const accessConsentRequestedData = await auth.getEventData(
|
||||
"AccessConsentRequested", {
|
||||
filter: {
|
||||
_consumer: consumer.getId(),
|
||||
},
|
||||
fromBlock: 0,
|
||||
toBlock: "latest",
|
||||
})
|
||||
// todo encrypt url in secret store
|
||||
Logger.log(contentUrl)
|
||||
return "0x00000"
|
||||
})
|
||||
|
||||
const orders = await Promise.all(
|
||||
accessConsentRequestedData
|
||||
.map(async (event: any) => {
|
||||
const template = new Access()
|
||||
const serviceAgreementTemplate = new ServiceAgreementTemplate(template)
|
||||
|
||||
const {returnValues} = event
|
||||
// get condition keys from template
|
||||
const conditions: Condition[] = await serviceAgreementTemplate.getConditions()
|
||||
|
||||
const order: Order = new Order(
|
||||
null,
|
||||
parseInt(returnValues._timeout, 10),
|
||||
null, null)
|
||||
|
||||
order.setId(returnValues._id)
|
||||
|
||||
return order
|
||||
// create ddo conditions out of the keys
|
||||
const ddoConditions: DDOCondition[] = conditions.map((condition: Condition): DDOCondition => {
|
||||
return {
|
||||
name: condition.methodReflection.methodName,
|
||||
timeout: condition.timeout,
|
||||
conditionKey: condition.condtionKey,
|
||||
parameters: condition.methodReflection.inputs.map((input: ValuePair) => {
|
||||
return {
|
||||
...input,
|
||||
value: "xxx",
|
||||
} as Parameter
|
||||
}),
|
||||
)
|
||||
|
||||
// Logger.log("Got orders:", JSON.stringify(orders, null, 2))
|
||||
Logger.log(`Got ${Object.keys(orders).length} orders`)
|
||||
} as DDOCondition
|
||||
})
|
||||
const serviceEndpoint = this.aquarius.getServiceEndpoint(did)
|
||||
|
||||
return orders
|
||||
// create ddo itself
|
||||
const ddo: DDO = new DDO({
|
||||
id: did,
|
||||
service: [
|
||||
{
|
||||
type: template.templateName,
|
||||
// tslint:disable-next-line
|
||||
serviceEndpoint: "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}",
|
||||
purchaseEndpoint: "http://mybrizo.org/api/v1/brizo/services/access/purchase?",
|
||||
// the id of the service agreement?
|
||||
serviceDefinitionId,
|
||||
// the id of the service agreement template
|
||||
templateId: serviceAgreementTemplate.getId(),
|
||||
conditions: ddoConditions,
|
||||
} as Service,
|
||||
{
|
||||
serviceEndpoint,
|
||||
metadata,
|
||||
} as Service,
|
||||
],
|
||||
})
|
||||
|
||||
const storedDdo = await this.aquarius.storeDDO(ddo)
|
||||
|
||||
await didRegistry.registerAttribute(id, ValueType.DID, "Metadata", serviceEndpoint,
|
||||
publisher.getId())
|
||||
|
||||
return storedDdo
|
||||
}
|
||||
|
||||
public async searchAssets(query): Promise<any[]> {
|
||||
return AquariusProvider.getAquarius().queryMetadata(query)
|
||||
public async purchase(did: string, consumer: Account): Promise<ServiceAgreement> {
|
||||
|
||||
const ddo = await AquariusProvider.getAquarius().retrieveDDO(did)
|
||||
const id = did.replace("did:op:", "")
|
||||
|
||||
const serviceAgreementId: string = IdGenerator.generateId()
|
||||
const serviceAgreement: ServiceAgreement = await ServiceAgreement.signServiceAgreement(id,
|
||||
// todo get publisher from ddo
|
||||
ddo, serviceAgreementId, consumer, new Account())
|
||||
|
||||
return serviceAgreement
|
||||
}
|
||||
|
||||
public async searchAssets(query: SearchQuery): Promise<any[]> {
|
||||
return this.aquarius.queryMetadata(query)
|
||||
}
|
||||
|
||||
public async searchAssetsByText(text: string): Promise<any[]> {
|
||||
return this.aquarius.queryMetadataByText({
|
||||
text,
|
||||
page: 1,
|
||||
offset: 100,
|
||||
query: {
|
||||
value: 1,
|
||||
},
|
||||
sort: {
|
||||
value: 1,
|
||||
},
|
||||
} as SearchQuery)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
import * as EthEcies from "eth-ecies"
|
||||
import * as JWT from "jsonwebtoken"
|
||||
import AquariusProvider from "../aquarius/AquariusProvider"
|
||||
import Keeper from "../keeper/Keeper"
|
||||
import Web3Provider from "../keeper/Web3Provider"
|
||||
import AccessStatus from "../models/AccessStatus"
|
||||
import Logger from "../utils/Logger"
|
||||
import Account from "./Account"
|
||||
import Asset from "./Asset"
|
||||
import OceanBase from "./OceanBase"
|
||||
|
||||
export default class Order extends OceanBase {
|
||||
|
||||
constructor(private asset: Asset, private timeout: number,
|
||||
private pubkey: string, private key: any) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async getStatus(): Promise<AccessStatus> {
|
||||
const {auth} = await Keeper.getInstance()
|
||||
return auth.getOrderStatus(this.id)
|
||||
}
|
||||
|
||||
public async pay(consumer: Account): Promise<string> {
|
||||
const {market} = await Keeper.getInstance()
|
||||
Logger.log(
|
||||
`Sending payment: ${this.getId()} ${this.asset.publisher.getId()} ${this.asset.price} ${this.timeout}`,
|
||||
)
|
||||
const payReceipt =
|
||||
await market.payOrder(this, this.asset.publisher.getId(), this.asset.price, consumer.getId(), this.timeout)
|
||||
|
||||
return payReceipt.events.PaymentReceived.returnValues._paymentId
|
||||
}
|
||||
|
||||
public async commit(accessToken: string): Promise<boolean> {
|
||||
const {auth} = await Keeper.getInstance()
|
||||
const commitAccessRequestReceipt = await auth.commitAccessRequest(this, this.asset.publisher.getId())
|
||||
if (commitAccessRequestReceipt.events.AccessRequestRejected) {
|
||||
|
||||
const {returnValues} = commitAccessRequestReceipt.events.AccessRequestRejected
|
||||
throw new Error(`commitAccessRequest failed ${JSON.stringify(returnValues, null, 2)}`)
|
||||
}
|
||||
|
||||
const pubKey = await auth.getTempPubKey(this.getId())
|
||||
|
||||
if (this.pubkey !== pubKey) {
|
||||
throw new Error("Pubkey missmatch")
|
||||
}
|
||||
|
||||
const encryptedAccessToken =
|
||||
EthEcies.encrypt(new Buffer(pubKey, "hex"), new Buffer(accessToken)).toString("hex")
|
||||
|
||||
await auth.deliverAccessToken(this.getId(), `0x${encryptedAccessToken}`, this.asset.publisher.getId())
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public async consume(consumer: Account): Promise<string> {
|
||||
const {auth} = await Keeper.getInstance()
|
||||
|
||||
const encryptedAccessToken = await auth.getEncryptedAccessToken(this.getId(), consumer.getId())
|
||||
|
||||
// grab the access token from acl contract
|
||||
const tokenNo0x = encryptedAccessToken.slice(2)
|
||||
const encryptedTokenBuffer = Buffer.from(tokenNo0x, "hex")
|
||||
|
||||
const privateKey = this.key.privateKey.slice(2)
|
||||
const accessTokenEncoded: string =
|
||||
EthEcies.decrypt(Buffer.from(privateKey, "hex"), encryptedTokenBuffer).toString()
|
||||
const accessToken = JWT.decode(accessTokenEncoded) // Returns a json object
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error(`AccessToken is not an jwt: ${accessTokenEncoded}`)
|
||||
}
|
||||
|
||||
const signature = Web3Provider.getWeb3().eth.sign(encryptedAccessToken, consumer.getId())
|
||||
const encryptedAccessTokenSha3 = Web3Provider.getWeb3().utils.sha3(encryptedAccessToken)
|
||||
|
||||
// Download the data set from the provider using the url in the access token
|
||||
// decode the access token, grab the service_endpoint, request_id,
|
||||
|
||||
// payload keys: ['consumerId', 'fixed_msg', 'sigEncJWT', 'jwt']
|
||||
const payload = JSON.stringify({
|
||||
consumerId: consumer.getId(),
|
||||
fixed_msg: encryptedAccessTokenSha3,
|
||||
sigEncJWT: signature,
|
||||
jwt: accessTokenEncoded,
|
||||
})
|
||||
|
||||
const accessUrl = await AquariusProvider.getAquarius().getAccessUrl(accessToken, payload)
|
||||
|
||||
Logger.log("consume url: ", accessUrl)
|
||||
|
||||
return accessUrl
|
||||
}
|
||||
|
||||
}
|
7
src/ocean/ServiceAgreements/Condition.ts
Normal file
7
src/ocean/ServiceAgreements/Condition.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import MethodReflection from "../../models/MethodReflection"
|
||||
|
||||
export default class Condition {
|
||||
public methodReflection: MethodReflection
|
||||
public condtionKey: string
|
||||
public timeout: number
|
||||
}
|
5
src/ocean/ServiceAgreements/Method.ts
Normal file
5
src/ocean/ServiceAgreements/Method.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default class Method {
|
||||
public path: string
|
||||
public dependency: number
|
||||
public timeout: number
|
||||
}
|
132
src/ocean/ServiceAgreements/ServiceAgreement.ts
Normal file
132
src/ocean/ServiceAgreements/ServiceAgreement.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
import Condition from "../../ddo/Condition"
|
||||
import DDO from "../../ddo/DDO"
|
||||
import Keeper from "../../keeper/Keeper"
|
||||
import Web3Provider from "../../keeper/Web3Provider"
|
||||
import ValuePair from "../../models/ValuePair"
|
||||
import Account from "../Account"
|
||||
import OceanBase from "../OceanBase"
|
||||
|
||||
export default class ServiceAgreement extends OceanBase {
|
||||
|
||||
public static async signServiceAgreement(assetId: string, ddo: DDO, serviceAgreementId: string, consumer: Account,
|
||||
publisher: Account):
|
||||
Promise<ServiceAgreement> {
|
||||
|
||||
const values: ValuePair[] = ServiceAgreement.getValuesFromDDO(ddo, serviceAgreementId)
|
||||
const valueHashes = ServiceAgreement.createValueHashes(values)
|
||||
const timeoutValues: number[] = ServiceAgreement.getTimeoutValuesFromDDO(ddo)
|
||||
|
||||
const serviceAgreementHashSignature = await ServiceAgreement.createSAHashSignature(ddo, serviceAgreementId,
|
||||
values, valueHashes, timeoutValues, consumer)
|
||||
|
||||
const serviceAgreement: ServiceAgreement = await ServiceAgreement.executeAgreement(ddo,
|
||||
serviceAgreementId, values, valueHashes, timeoutValues, serviceAgreementHashSignature, consumer, publisher)
|
||||
|
||||
return serviceAgreement
|
||||
}
|
||||
|
||||
public static async createSAHashSignature(ddo: DDO, serviceAgreementId: string, values: ValuePair[],
|
||||
valueHashes: string[], timeoutValues: number[], consumer: Account):
|
||||
Promise<string> {
|
||||
|
||||
const conditionKeys: string[] = ddo.service[0].conditions.map((condition) => {
|
||||
return condition.conditionKey
|
||||
})
|
||||
|
||||
const serviceAgreementHash = ServiceAgreement.hashServiceAgreement(ddo.service[0].templateId,
|
||||
serviceAgreementId, conditionKeys, valueHashes, timeoutValues)
|
||||
|
||||
const serviceAgreementHashSignature =
|
||||
await Web3Provider.getWeb3().eth.sign(serviceAgreementHash, consumer.getId())
|
||||
|
||||
return serviceAgreementHashSignature
|
||||
}
|
||||
|
||||
private static async executeAgreement(ddo: DDO, serviceAgreementId: string, values: ValuePair[],
|
||||
valueHashes: string[], timeoutValues: number[],
|
||||
serviceAgreementHashSignature: string, consumer: Account,
|
||||
publisher: Account): Promise<ServiceAgreement> {
|
||||
|
||||
const {serviceAgreement} = await Keeper.getInstance()
|
||||
const executeAgreementReceipt = await serviceAgreement.executeAgreement(
|
||||
ddo.service[0].templateId, serviceAgreementHashSignature, consumer.getId(), valueHashes,
|
||||
timeoutValues, serviceAgreementId, ddo.id, publisher.getId())
|
||||
|
||||
if (executeAgreementReceipt.events.ExecuteAgreement.returnValues.state === false) {
|
||||
throw new Error("signing service agreement failed.")
|
||||
}
|
||||
|
||||
return new ServiceAgreement(
|
||||
executeAgreementReceipt.events.ExecuteAgreement.returnValues.serviceAgreementId,
|
||||
ddo,
|
||||
publisher,
|
||||
consumer,
|
||||
executeAgreementReceipt.events.ExecuteAgreement.returnValues.state,
|
||||
executeAgreementReceipt.events.ExecuteAgreement.returnValues.status,
|
||||
)
|
||||
}
|
||||
|
||||
private static createValueHashes(valuePairs: ValuePair[]): any[] {
|
||||
return valuePairs.map((valuePair) => {
|
||||
return ServiceAgreement.hashSingleValue(valuePair)
|
||||
})
|
||||
}
|
||||
|
||||
private static hashSingleValue(data: ValuePair): string {
|
||||
return Web3Provider.getWeb3().utils.soliditySha3(data).toString("hex")
|
||||
}
|
||||
|
||||
private static hashServiceAgreement(serviceAgreementTemplateId: string, serviceAgreementId: string,
|
||||
conditionKeys: string[], valueHashes: string[], timeouts: number[])
|
||||
: string {
|
||||
const args = [
|
||||
{type: "bytes32", value: serviceAgreementTemplateId} as ValuePair,
|
||||
{type: "bytes32[]", value: conditionKeys} as ValuePair,
|
||||
{type: "bytes32[]", value: valueHashes} as ValuePair,
|
||||
{type: "uint256[]", value: timeouts} as ValuePair,
|
||||
{type: "bytes32", value: serviceAgreementId} as ValuePair,
|
||||
]
|
||||
|
||||
return Web3Provider.getWeb3().utils.soliditySha3(...args).toString("hex")
|
||||
}
|
||||
|
||||
private static getTimeoutValuesFromDDO(ddo: DDO): number[] {
|
||||
const timeoutValues: number[] = ddo.service[0].conditions.map((condition: Condition) => {
|
||||
return condition.timeout
|
||||
})
|
||||
|
||||
return timeoutValues
|
||||
}
|
||||
|
||||
private static getValuesFromDDO(ddo: DDO, serviceAgreementId: string): ValuePair[] {
|
||||
const values: ValuePair[] = [
|
||||
{type: "bool", value: true} as ValuePair,
|
||||
{type: "bool", value: false} as ValuePair,
|
||||
{type: "bool", value: false} as ValuePair,
|
||||
{type: "uint", value: 120} as ValuePair,
|
||||
{type: "string", value: serviceAgreementId} as ValuePair,
|
||||
]
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
private constructor(serviceAgreementId: string, ddo: DDO, private publisher: Account, consumer: Account,
|
||||
state: boolean, status: boolean) {
|
||||
super(serviceAgreementId)
|
||||
}
|
||||
|
||||
public async grantAccess(assetId: string, documentId: string): Promise<boolean> {
|
||||
const {accessConditions} = await Keeper.getInstance()
|
||||
|
||||
const grantAccessReceipt =
|
||||
await accessConditions.grantAccess(this.getId(), assetId, documentId,
|
||||
this.publisher.getId())
|
||||
|
||||
return grantAccessReceipt.status
|
||||
}
|
||||
|
||||
public async getStatus() {
|
||||
const {serviceAgreement} = await Keeper.getInstance()
|
||||
return serviceAgreement.getAgreementStatus(this.getId())
|
||||
}
|
||||
}
|
105
src/ocean/ServiceAgreements/ServiceAgreementTemplate.ts
Normal file
105
src/ocean/ServiceAgreements/ServiceAgreementTemplate.ts
Normal file
|
@ -0,0 +1,105 @@
|
|||
import ContractReflector from "../../keeper/ContractReflector"
|
||||
import Keeper from "../../keeper/Keeper"
|
||||
import Web3Provider from "../../keeper/Web3Provider"
|
||||
import MethodReflection from "../../models/MethodReflection"
|
||||
import ValuePair from "../../models/ValuePair"
|
||||
import Logger from "../../utils/Logger"
|
||||
import Account from "../Account"
|
||||
import OceanBase from "../OceanBase"
|
||||
import Condition from "./Condition"
|
||||
import Method from "./Method"
|
||||
import TemplateBase from "./Templates/TemplateBase"
|
||||
|
||||
export default class ServiceAgreementTemplate extends OceanBase {
|
||||
|
||||
private static generateConditionsKey(serviceAgreementTemplateId: string, methodReflection: MethodReflection)
|
||||
: string {
|
||||
const values = [
|
||||
{type: "bytes32", value: serviceAgreementTemplateId} as ValuePair,
|
||||
{type: "address", value: methodReflection.address} as ValuePair,
|
||||
{type: "bytes4", value: methodReflection.signature} as ValuePair,
|
||||
]
|
||||
return Web3Provider.getWeb3().utils.soliditySha3(...values).toString("hex")
|
||||
}
|
||||
|
||||
public constructor(private template: TemplateBase) {
|
||||
super(template.id)
|
||||
}
|
||||
|
||||
public async register(templateOwnerAddress: string)
|
||||
: Promise<boolean> {
|
||||
|
||||
const dependencyMatrix: number[] =
|
||||
await Promise.all(this.template.Methods.map(async (method: Method) => {
|
||||
// tslint:disable-next-line
|
||||
return method.dependency | method.timeout
|
||||
}))
|
||||
|
||||
const {serviceAgreement} = await Keeper.getInstance()
|
||||
|
||||
const methodReflections = await this.getMethodReflections()
|
||||
|
||||
const owner = await this.getOwner()
|
||||
|
||||
if (!owner.getId().startsWith("0x0")) {
|
||||
Logger.error(`Template with id "${this.template.id}" already registered.`)
|
||||
return false
|
||||
}
|
||||
|
||||
const receipt = await serviceAgreement.setupAgreementTemplate(
|
||||
this.template.id, methodReflections, dependencyMatrix,
|
||||
Web3Provider.getWeb3().utils.fromAscii(this.template.templateName),
|
||||
templateOwnerAddress)
|
||||
|
||||
const templateId = receipt.events.SetupAgreementTemplate.returnValues.serviceTemplateId
|
||||
|
||||
if (templateId !== this.template.id) {
|
||||
// tslint:disable-next-line
|
||||
throw new Error(`TemplateId missmatch on ${this.template.templateName}! Should be "${this.template.id}" but is ${templateId}`)
|
||||
}
|
||||
|
||||
if (receipt.status) {
|
||||
Logger.error("Registering template failed")
|
||||
}
|
||||
|
||||
return receipt.status
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the status of a service agreement template
|
||||
*/
|
||||
public async getStatus(): Promise<boolean> {
|
||||
const {serviceAgreement} = await Keeper.getInstance()
|
||||
return serviceAgreement.getTemplateStatus(this.getId())
|
||||
}
|
||||
|
||||
public async getOwner(): Promise<Account> {
|
||||
const {serviceAgreement} = await Keeper.getInstance()
|
||||
|
||||
return new Account(await serviceAgreement.getTemplateOwner(this.id))
|
||||
}
|
||||
|
||||
public async getConditions(): Promise<Condition[]> {
|
||||
const methodReflections = await this.getMethodReflections()
|
||||
|
||||
const conditions: Condition[] = methodReflections.map((methodReflection, i) => {
|
||||
return {
|
||||
methodReflection,
|
||||
timeout: this.template.Methods[i].timeout,
|
||||
condtionKey: ServiceAgreementTemplate.generateConditionsKey(this.getId(),
|
||||
methodReflection),
|
||||
} as Condition
|
||||
})
|
||||
|
||||
return conditions
|
||||
}
|
||||
|
||||
private async getMethodReflections(): Promise<MethodReflection[]> {
|
||||
const methodReflections: MethodReflection[] =
|
||||
await Promise.all(this.template.Methods.map(async (method: Method) => {
|
||||
const methodReflection = await ContractReflector.reflectContractMethod(method.path)
|
||||
return methodReflection
|
||||
}))
|
||||
return methodReflections
|
||||
}
|
||||
}
|
30
src/ocean/ServiceAgreements/Templates/Access.ts
Normal file
30
src/ocean/ServiceAgreements/Templates/Access.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Method from "../Method"
|
||||
import TemplateBase from "./TemplateBase"
|
||||
|
||||
export default class Access extends TemplateBase {
|
||||
|
||||
public templateName: string = "Access"
|
||||
public id: string = "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"
|
||||
public Methods: Method[] = [
|
||||
{
|
||||
path: "PaymentConditions.lockPayment",
|
||||
dependency: 0,
|
||||
timeout: 10,
|
||||
} as Method,
|
||||
{
|
||||
path: "AccessConditions.grantAccess",
|
||||
dependency: 1,
|
||||
timeout: 500,
|
||||
} as Method,
|
||||
{
|
||||
path: "PaymentConditions.releasePayment",
|
||||
dependency: 4,
|
||||
timeout: 17,
|
||||
} as Method,
|
||||
{
|
||||
path: "PaymentConditions.refundPayment",
|
||||
dependency: 1,
|
||||
timeout: 40,
|
||||
} as Method,
|
||||
]
|
||||
}
|
30
src/ocean/ServiceAgreements/Templates/FitchainCompute.ts
Normal file
30
src/ocean/ServiceAgreements/Templates/FitchainCompute.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import Method from "../Method"
|
||||
import TemplateBase from "./TemplateBase"
|
||||
|
||||
export default class FitchainCompute extends TemplateBase {
|
||||
|
||||
public templateName: string = "FitchainCompute"
|
||||
public id: string = "0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6"
|
||||
public Methods: Method[] = [
|
||||
{
|
||||
path: "PaymentConditions.lockPayment",
|
||||
dependency: 0,
|
||||
timeout: 10,
|
||||
} as Method,
|
||||
{
|
||||
path: "AccessConditions.grantAccess",
|
||||
dependency: 1,
|
||||
timeout: 500,
|
||||
} as Method,
|
||||
{
|
||||
path: "PaymentConditions.releasePayment",
|
||||
dependency: 4,
|
||||
timeout: 17,
|
||||
} as Method,
|
||||
{
|
||||
path: "PaymentConditions.refundPayment",
|
||||
dependency: 1,
|
||||
timeout: 40,
|
||||
} as Method,
|
||||
]
|
||||
}
|
7
src/ocean/ServiceAgreements/Templates/TemplateBase.ts
Normal file
7
src/ocean/ServiceAgreements/Templates/TemplateBase.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Method from "../Method"
|
||||
|
||||
export default abstract class TemplateBase {
|
||||
public Methods: Method[]
|
||||
public templateName: string
|
||||
public id: string = "0x00000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
21
src/secretstore/SecretStoreProvider.ts
Normal file
21
src/secretstore/SecretStoreProvider.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import SecretStore from "@oceanprotocol/secret-store-client"
|
||||
import ConfigProvider from "../ConfigProvider"
|
||||
|
||||
export default class SecretStoreProvider {
|
||||
|
||||
public static setSecretStore(secretStore: SecretStore) {
|
||||
|
||||
SecretStoreProvider.secretStore = secretStore
|
||||
}
|
||||
|
||||
public static getSecretStore(): SecretStore {
|
||||
|
||||
if (!SecretStoreProvider.secretStore) {
|
||||
SecretStoreProvider.secretStore = new SecretStore(ConfigProvider.getConfig())
|
||||
}
|
||||
|
||||
return SecretStoreProvider.secretStore
|
||||
}
|
||||
|
||||
private static secretStore: SecretStore
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import Asset from "./ocean/Asset"
|
||||
import Account from "./ocean/Account"
|
||||
import Ocean from "./ocean/Ocean"
|
||||
import Order from "./ocean/Order"
|
||||
import ServiceAgreement from "./ocean/ServiceAgreements/ServiceAgreement"
|
||||
import Logger from "./utils/Logger"
|
||||
|
||||
export {
|
||||
Ocean,
|
||||
Order,
|
||||
Asset,
|
||||
ServiceAgreement,
|
||||
Logger,
|
||||
Account,
|
||||
}
|
||||
|
|
|
@ -13,12 +13,8 @@ describe("Squid", () => {
|
|||
assert(squid.Logger)
|
||||
})
|
||||
|
||||
it("should expose Asset", async () => {
|
||||
assert(squid.Asset)
|
||||
})
|
||||
|
||||
it("should expose Order", async () => {
|
||||
assert(squid.Order)
|
||||
it("should expose ServiceAgreement", async () => {
|
||||
assert(squid.ServiceAgreement)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
import * as assert from "assert"
|
||||
import Aquarius from "../../src/aquarius/Aquarius"
|
||||
import AquariusConnectorProvider from "../../src/aquarius/AquariusConnectorProvider"
|
||||
import SearchQuery from "../../src/aquarius/query/SearchQuery"
|
||||
import DDO from "../../src/ddo/DDO"
|
||||
import IdGenerator from "../../src/ocean/IdGenerator"
|
||||
import config from "../config"
|
||||
import AquariusConnectorMock from "../mocks/AquariusConnector.mock"
|
||||
|
||||
before(() => {
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock())
|
||||
})
|
||||
// import * as jsonDDO from "../testdata/ddo.json"
|
||||
|
||||
describe("Aquarius", () => {
|
||||
|
||||
const aquarius: Aquarius = new Aquarius(config)
|
||||
describe("#queryMetadata()", () => {
|
||||
|
||||
it("should query metadata", async () => {
|
||||
|
||||
const aquarius: Aquarius = new Aquarius(config)
|
||||
|
||||
const query = {
|
||||
offset: 100,
|
||||
page: 0,
|
||||
|
@ -26,11 +25,84 @@ describe("Aquarius", () => {
|
|||
value: 1,
|
||||
},
|
||||
text: "Office",
|
||||
}
|
||||
} as SearchQuery
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock())
|
||||
|
||||
const result: any[] = await aquarius.queryMetadata(query)
|
||||
assert(result)
|
||||
assert(result.length !== null)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#queryMetadataByText()", () => {
|
||||
|
||||
it("should query metadata by text", async () => {
|
||||
|
||||
const query = {
|
||||
offset: 100,
|
||||
page: 0,
|
||||
query: {
|
||||
value: 1,
|
||||
},
|
||||
sort: {
|
||||
value: 1,
|
||||
},
|
||||
text: "Office",
|
||||
} as SearchQuery
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock())
|
||||
|
||||
const result: any[] = await aquarius.queryMetadataByText(query)
|
||||
assert(result)
|
||||
assert(result.length !== null)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#storeDDO()", () => {
|
||||
|
||||
it("should store a ddo", async () => {
|
||||
|
||||
const did: string = `did:op:${IdGenerator.generateId()}`
|
||||
const ddo: DDO = new DDO({
|
||||
id: did,
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
|
||||
|
||||
const result: DDO = await aquarius.storeDDO(ddo)
|
||||
assert(result)
|
||||
assert(result.id === ddo.id)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#retrieveDDO()", () => {
|
||||
|
||||
it("should store a ddo", async () => {
|
||||
|
||||
const did: string = `did:op:${IdGenerator.generateId()}`
|
||||
const ddo: DDO = new DDO({
|
||||
id: did,
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
|
||||
|
||||
const storageResult: DDO = await aquarius.storeDDO(ddo)
|
||||
assert(storageResult)
|
||||
|
||||
assert(storageResult.id === did)
|
||||
|
||||
const restrieveResult: DDO = await aquarius.retrieveDDO(did)
|
||||
assert(restrieveResult)
|
||||
|
||||
assert(restrieveResult.id === did)
|
||||
assert(restrieveResult.id === storageResult.id)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,5 +3,10 @@ import Config from "../src/models/Config"
|
|||
export default {
|
||||
aquariusUri: "http://localhost:5000",
|
||||
nodeUri: "http://localhost:8545",
|
||||
parityUri: "http://localhost:9545",
|
||||
secretStoreUri: "https://secret-store.dev-ocean.com",
|
||||
threshold: 2,
|
||||
password: "unittest",
|
||||
address: "0xed243adfb84a6626eba46178ccb567481c6e655d",
|
||||
web3Provider: null,
|
||||
} as Config
|
||||
|
|
216
test/ddo/DDO.test.ts
Normal file
216
test/ddo/DDO.test.ts
Normal file
|
@ -0,0 +1,216 @@
|
|||
import {assert} from "chai"
|
||||
import AdditionalInformation from "../../src/ddo/AdditionalInformation"
|
||||
import Authentication from "../../src/ddo/Authentication"
|
||||
import Curation from "../../src/ddo/Curation"
|
||||
import DDO from "../../src/ddo/DDO"
|
||||
import MetaData from "../../src/ddo/MetaData"
|
||||
import MetaDataBase from "../../src/ddo/MetaDataBase"
|
||||
import PublicKey from "../../src/ddo/PublicKey"
|
||||
import Service from "../../src/ddo/Service"
|
||||
import StructuredMarkup from "../../src/ddo/StructuredMarkup"
|
||||
import * as jsonDDO from "../testdata/ddo.json"
|
||||
|
||||
describe("DDO", () => {
|
||||
|
||||
const testDDO: DDO = new DDO({
|
||||
publicKey: [
|
||||
{
|
||||
id: "did:op:123456789abcdefghi#keys-1",
|
||||
type: "RsaVerificationKey2018",
|
||||
owner: "did:op:123456789abcdefghi",
|
||||
publicKeyPem: "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n",
|
||||
} as PublicKey,
|
||||
{
|
||||
id: "did:op:123456789abcdefghi#keys-2",
|
||||
type: "Ed25519VerificationKey2018",
|
||||
owner: "did:op:123456789abcdefghi",
|
||||
publicKeyBase58: "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV",
|
||||
} as PublicKey,
|
||||
{
|
||||
id: "did:op:123456789abcdefghi#keys-3",
|
||||
type: "RsaPublicKeyExchangeKey2018",
|
||||
owner: "did:op:123456789abcdefghi",
|
||||
publicKeyPem: "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n",
|
||||
} as PublicKey,
|
||||
],
|
||||
authentication: [
|
||||
{
|
||||
type: "RsaSignatureAuthentication2018",
|
||||
publicKey: "did:op:123456789abcdefghi#keys-1",
|
||||
} as Authentication,
|
||||
{
|
||||
type: "ieee2410Authentication2018",
|
||||
publicKey: "did:op:123456789abcdefghi#keys-2",
|
||||
} as Authentication,
|
||||
],
|
||||
service: [
|
||||
{
|
||||
type: "OpenIdConnectVersion1.0Service",
|
||||
serviceEndpoint: "https://openid.example.com/",
|
||||
} as Service,
|
||||
{
|
||||
type: "CredentialRepositoryService",
|
||||
serviceEndpoint: "https://repository.example.com/service/8377464",
|
||||
} as Service,
|
||||
{
|
||||
type: "XdiService",
|
||||
serviceEndpoint: "https://xdi.example.com/8377464",
|
||||
} as Service,
|
||||
{
|
||||
type: "HubService",
|
||||
serviceEndpoint: "https://hub.example.com/.identity/did:op:0123456789abcdef/",
|
||||
} as Service,
|
||||
{
|
||||
type: "MessagingService",
|
||||
serviceEndpoint: "https://example.com/messages/8377464",
|
||||
} as Service,
|
||||
{
|
||||
type: "SocialWebInboxService",
|
||||
serviceEndpoint: "https://social.example.com/83hfh37dj",
|
||||
description: "My public social inbox",
|
||||
spamCost: {
|
||||
amount: "0.50",
|
||||
currency: "USD",
|
||||
},
|
||||
} as Service,
|
||||
{
|
||||
id: "did:op:123456789abcdefghi;bops",
|
||||
type: "BopsService",
|
||||
serviceEndpoint: "https://bops.example.com/enterprise/",
|
||||
} as Service,
|
||||
{
|
||||
type: "Consume",
|
||||
// tslint:disable-next-line
|
||||
serviceEndpoint: "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}",
|
||||
} as Service,
|
||||
{
|
||||
type: "Compute",
|
||||
// tslint:disable-next-line
|
||||
serviceEndpoint: "http://mybrizo.org/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId={serviceId}&algo={algo}&container={container}",
|
||||
} as Service,
|
||||
{
|
||||
type: "Metadata",
|
||||
serviceEndpoint: "http://myaquarius.org/api/v1/provider/assets/metadata/{did}",
|
||||
metadata: {
|
||||
base: {
|
||||
name: "UK Weather information 2011",
|
||||
type: "dataset",
|
||||
description: "Weather information of UK including temperature and humidity",
|
||||
size: "3.1gb",
|
||||
dateCreated: "2012-10-10T17:00:000Z",
|
||||
author: "Met Office",
|
||||
license: "CC-BY",
|
||||
copyrightHolder: "Met Office",
|
||||
encoding: "UTF-8",
|
||||
compression: "zip",
|
||||
contentType: "text/csv",
|
||||
workExample: "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
|
||||
contentUrls: [
|
||||
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip",
|
||||
],
|
||||
links: [
|
||||
{
|
||||
// tslint:disable-next-line
|
||||
sample1: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/"
|
||||
},
|
||||
{
|
||||
// tslint:disable-next-line
|
||||
sample2: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-averages-25km/"
|
||||
},
|
||||
{
|
||||
fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/",
|
||||
},
|
||||
],
|
||||
inLanguage: "en",
|
||||
tags: "weather, uk, 2011, temperature, humidity",
|
||||
price: 10,
|
||||
} as MetaDataBase,
|
||||
curation: {
|
||||
rating: 0.93,
|
||||
numVotes: 123,
|
||||
schema: "Binary Votting",
|
||||
} as Curation,
|
||||
additionalInformation: {
|
||||
updateFrecuency: "yearly",
|
||||
structuredMarkup: [
|
||||
{
|
||||
uri: "http://skos.um.es/unescothes/C01194/jsonld",
|
||||
mediaType: "application/ld+json",
|
||||
} as StructuredMarkup,
|
||||
{
|
||||
uri: "http://skos.um.es/unescothes/C01194/turtle",
|
||||
mediaType: "text/turtle",
|
||||
} as StructuredMarkup,
|
||||
],
|
||||
} as AdditionalInformation,
|
||||
} as MetaData,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
describe("#serialize()", () => {
|
||||
|
||||
it("should properly serialize", async () => {
|
||||
|
||||
const ddoString = DDO.serialize(testDDO)
|
||||
assert(ddoString)
|
||||
assert(ddoString.startsWith("{"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("#constructor()", () => {
|
||||
|
||||
it("should create an empty ddo", async () => {
|
||||
|
||||
const ddo = new DDO()
|
||||
assert(ddo)
|
||||
|
||||
assert(ddo.service.length === 0)
|
||||
assert(ddo.authentication.length === 0)
|
||||
assert(ddo.publicKey.length === 0)
|
||||
})
|
||||
|
||||
it("should create an predefined ddo", async () => {
|
||||
|
||||
const service: Service = {
|
||||
serviceEndpoint: "http://",
|
||||
description: "nice service",
|
||||
} as Service
|
||||
|
||||
const ddo = new DDO({
|
||||
service: [service],
|
||||
})
|
||||
assert(ddo)
|
||||
|
||||
assert(ddo.service.length === 1)
|
||||
assert(ddo.service[0].description === service.description)
|
||||
|
||||
assert(ddo.authentication.length === 0)
|
||||
assert(ddo.publicKey.length === 0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#deserialize()", () => {
|
||||
|
||||
it("should properly deserialize from serialized object", async () => {
|
||||
|
||||
const ddoString = DDO.serialize(testDDO)
|
||||
assert(ddoString)
|
||||
|
||||
const ddo: DDO = DDO.deserialize(ddoString)
|
||||
assert(ddo)
|
||||
|
||||
assert(ddo.id === testDDO.id)
|
||||
assert(ddo.publicKey[0].publicKeyPem === testDDO.publicKey[0].publicKeyPem)
|
||||
})
|
||||
|
||||
it("should properly deserialize from json file", async () => {
|
||||
|
||||
const ddo: DDO = DDO.deserialize(JSON.stringify(jsonDDO))
|
||||
assert(ddo)
|
||||
|
||||
assert(ddo.id === jsonDDO.id)
|
||||
assert(ddo.publicKey[0].publicKeyPem === jsonDDO.publicKey[0].publicKeyPem)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,16 +1,22 @@
|
|||
import {assert} from "chai"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import config from "../config"
|
||||
import ContractBaseMock from "../mocks/ContractBase.Mock"
|
||||
import TestContractHandler from "./TestContractHandler"
|
||||
|
||||
const wrappedContract = new ContractBaseMock("OceanToken")
|
||||
let accounts: Account[]
|
||||
|
||||
describe("ContractWrapperBase", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await ContractHandler.deployContracts()
|
||||
wrappedContract.initMock()
|
||||
await TestContractHandler.prepareContracts()
|
||||
await wrappedContract.initMock()
|
||||
const ocean: Ocean = await Ocean.getInstance(config)
|
||||
accounts = await ocean.getAccounts()
|
||||
})
|
||||
|
||||
describe("#call()", () => {
|
||||
|
@ -52,6 +58,29 @@ describe("ContractWrapperBase", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("#send()", () => {
|
||||
|
||||
it("should fail to call on an unknown contract function", (done) => {
|
||||
|
||||
wrappedContract.sendMock("transferxxx", accounts[0].getId(), [])
|
||||
.catch(() => {
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getSignatureOfMethod()", () => {
|
||||
|
||||
it("should a signature of the function", async () => {
|
||||
|
||||
const sig = wrappedContract.getSignatureOfMethod("name")
|
||||
assert(sig)
|
||||
assert(typeof sig === "string")
|
||||
assert(sig.startsWith("0x"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getEventData()", () => {
|
||||
|
||||
it("should fail on unknown event", (done) => {
|
||||
|
|
|
@ -7,7 +7,6 @@ describe("ContractHandler", () => {
|
|||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await ContractHandler.deployContracts()
|
||||
})
|
||||
|
||||
describe("#get()", () => {
|
||||
|
|
101
test/keeper/DIDRegistry.test.ts
Normal file
101
test/keeper/DIDRegistry.test.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import {assert} from "chai"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import DIDRegistry from "../../src/keeper/contracts/DIDRegistry"
|
||||
import Web3Provider from "../../src/keeper/Web3Provider"
|
||||
import ValueType from "../../src/models/ValueType"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import IdGenerator from "../../src/ocean/IdGenerator"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import Logger from "../../src/utils/Logger"
|
||||
import config from "../config"
|
||||
import TestContractHandler from "./TestContractHandler"
|
||||
|
||||
let ocean: Ocean
|
||||
let didRegistry: DIDRegistry
|
||||
|
||||
describe("DIDRegistry", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await TestContractHandler.prepareContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
didRegistry = await DIDRegistry.getInstance()
|
||||
})
|
||||
|
||||
describe("#registerAttribute()", () => {
|
||||
|
||||
it("should register an attribute in a new did", async () => {
|
||||
const ownerAccount: Account = (await ocean.getAccounts())[0]
|
||||
const did = IdGenerator.generateId()
|
||||
const providerKey = Web3Provider.getWeb3().utils.fromAscii("provider")
|
||||
const data = "my nice provider, is nice"
|
||||
const receipt = await didRegistry.registerAttribute(did, ValueType.DID, providerKey,
|
||||
data, ownerAccount.getId())
|
||||
assert(receipt.status)
|
||||
assert(receipt.events.DIDAttributeRegistered)
|
||||
})
|
||||
|
||||
it("should register another attribute in the same did", async () => {
|
||||
const ownerAccount: Account = (await ocean.getAccounts())[0]
|
||||
const did = IdGenerator.generateId()
|
||||
{
|
||||
// register the first attribute
|
||||
const providerKey = Web3Provider.getWeb3().utils.fromAscii("provider")
|
||||
const data = "my nice provider, is nice"
|
||||
await didRegistry.registerAttribute(did, ValueType.DID, providerKey,
|
||||
data, ownerAccount.getId())
|
||||
}
|
||||
{
|
||||
// register the second attribute with the same did
|
||||
const providerKey = Web3Provider.getWeb3().utils.fromAscii("provider2")
|
||||
const data = "asdsad"
|
||||
const receipt = await didRegistry.registerAttribute(did, ValueType.DID, providerKey,
|
||||
data, ownerAccount.getId())
|
||||
assert(receipt.status)
|
||||
assert(receipt.events.DIDAttributeRegistered)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#getOwner()", () => {
|
||||
|
||||
it("should get the owner of a did properly", async () => {
|
||||
const ownerAccount: Account = (await ocean.getAccounts())[0]
|
||||
const did = IdGenerator.generateId()
|
||||
const providerKey = Web3Provider.getWeb3().utils.fromAscii("provider")
|
||||
const data = "my nice provider, is nice"
|
||||
await didRegistry.registerAttribute(did, ValueType.DID, providerKey,
|
||||
data, ownerAccount.getId())
|
||||
|
||||
const owner = await didRegistry.getOwner(did)
|
||||
|
||||
assert(owner === ownerAccount.getId(), `Got ${owner} but expected ${ownerAccount.getId()}`)
|
||||
})
|
||||
|
||||
it("should get 0x00.. for a not registered did", async () => {
|
||||
const owner = await didRegistry.getOwner("1234")
|
||||
assert(owner === "0x0000000000000000000000000000000000000000")
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#getUpdateAt()", () => {
|
||||
|
||||
it("should the block number of the last update of the did attribute", async () => {
|
||||
const ownerAccount: Account = (await ocean.getAccounts())[0]
|
||||
const did = IdGenerator.generateId()
|
||||
const providerKey = Web3Provider.getWeb3().utils.fromAscii("provider")
|
||||
const data = "my nice provider, is nice"
|
||||
await didRegistry.registerAttribute(did, ValueType.DID, providerKey,
|
||||
data, ownerAccount.getId())
|
||||
|
||||
const updatedAt: number = await didRegistry.getUpdateAt(did)
|
||||
|
||||
assert(updatedAt > 0)
|
||||
Logger.log(typeof updatedAt)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
|
@ -1,8 +1,8 @@
|
|||
import {assert} from "chai"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import Keeper from "../../src/keeper/Keeper"
|
||||
import config from "../config"
|
||||
import TestContractHandler from "./TestContractHandler"
|
||||
|
||||
let keeper: Keeper
|
||||
|
||||
|
@ -10,10 +10,10 @@ describe("Keeper", () => {
|
|||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await ContractHandler.deployContracts()
|
||||
await TestContractHandler.prepareContracts()
|
||||
keeper = await Keeper.getInstance()
|
||||
})
|
||||
|
||||
|
||||
describe("public interface", () => {
|
||||
|
||||
it("should have market", () => {
|
||||
|
|
124
test/keeper/TestContractHandler.ts
Normal file
124
test/keeper/TestContractHandler.ts
Normal file
|
@ -0,0 +1,124 @@
|
|||
import Contract from "web3-eth-contract"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import Web3Provider from "../../src/keeper/Web3Provider"
|
||||
import ServiceAgreementTemplate from "../../src/ocean/ServiceAgreements/ServiceAgreementTemplate"
|
||||
import Access from "../../src/ocean/ServiceAgreements/Templates/Access"
|
||||
import FitchainCompute from "../../src/ocean/ServiceAgreements/Templates/FitchainCompute"
|
||||
import Logger from "../../src/utils/Logger"
|
||||
|
||||
export default class TestContractHandler extends ContractHandler {
|
||||
|
||||
public static async prepareContracts() {
|
||||
|
||||
const web3 = Web3Provider.getWeb3()
|
||||
const deployerAddress = (await web3.eth.getAccounts())[0]
|
||||
|
||||
// deploy contracts
|
||||
await TestContractHandler.deployContracts(deployerAddress)
|
||||
|
||||
// register templates
|
||||
await new ServiceAgreementTemplate(new Access()).register(deployerAddress)
|
||||
await new ServiceAgreementTemplate(new FitchainCompute()).register(deployerAddress)
|
||||
}
|
||||
|
||||
private static async deployContracts(deployerAddress: string) {
|
||||
Logger.log("Trying to deploy contracts")
|
||||
|
||||
// deploy libs
|
||||
/* not part of trilobite
|
||||
const dll = await ContractHandler.deployContract("DLL", deployerAddress)
|
||||
const attributeStore = await ContractHandler.deployContract("AttributeStore", deployerAddress)
|
||||
*/
|
||||
// deploy contracts
|
||||
const token = await TestContractHandler.deployContract("OceanToken", deployerAddress)
|
||||
/* not part of trilobite
|
||||
const plcrVoting = await ContractHandler.deployContract("PLCRVoting", deployerAddress, {
|
||||
args: [token.options.address],
|
||||
tokens: [
|
||||
{
|
||||
name: "DLL", address: dll.options.address,
|
||||
}, {
|
||||
name: "AttributeStore", address: attributeStore.options.address,
|
||||
},
|
||||
],
|
||||
})
|
||||
/* not part of trilobite
|
||||
const registry = await ContractHandler.deployContract("OceanRegistry", deployerAddress, {
|
||||
args: [token.options.address, plcrVoting.options.address],
|
||||
})
|
||||
*/
|
||||
const market = await TestContractHandler.deployContract("OceanMarket", deployerAddress, {
|
||||
args: [token.options.address],
|
||||
})
|
||||
|
||||
const sa = await TestContractHandler.deployContract("ServiceAgreement", deployerAddress, {
|
||||
args: [],
|
||||
})
|
||||
|
||||
await TestContractHandler.deployContract("AccessConditions", deployerAddress, {
|
||||
args: [sa.options.address],
|
||||
})
|
||||
|
||||
await TestContractHandler.deployContract("PaymentConditions", deployerAddress, {
|
||||
args: [sa.options.address, token.options.address],
|
||||
})
|
||||
|
||||
await TestContractHandler.deployContract("DIDRegistry", deployerAddress, {})
|
||||
/* not part of trilobite
|
||||
const dispute = await ContractHandler.deployContract("OceanDispute", deployerAddress, {
|
||||
args: [market.options.address, registry.options.address, plcrVoting.options.address],
|
||||
})
|
||||
*/
|
||||
await TestContractHandler.deployContract("OceanAuth", deployerAddress, {
|
||||
args: [market.options.address],
|
||||
})
|
||||
}
|
||||
|
||||
private static async deployContract(name: string, from: string, params?): Promise<Contract> {
|
||||
|
||||
// dont redeploy if there is already something loaded
|
||||
if (ContractHandler.has(name)) {
|
||||
return ContractHandler.get(name)
|
||||
}
|
||||
|
||||
const web3 = Web3Provider.getWeb3()
|
||||
|
||||
let contractInstance: Contract
|
||||
try {
|
||||
Logger.log("Deploying", name)
|
||||
|
||||
const artifact = require(`@oceanprotocol/keeper-contracts/artifacts/${name}.development.json`)
|
||||
const tempContract = new web3.eth.Contract(artifact.abi, artifact.address)
|
||||
contractInstance = await tempContract.deploy({
|
||||
data: params && params.tokens ?
|
||||
TestContractHandler.replaceTokens(artifact.bytecode.toString(), params.tokens) :
|
||||
artifact.bytecode,
|
||||
arguments: params && params.args ? params.args : null,
|
||||
}).send({
|
||||
from,
|
||||
gas: 3000000,
|
||||
gasPrice: 10000000000,
|
||||
})
|
||||
TestContractHandler.set(name, contractInstance)
|
||||
// Logger.log("Deployed", name, "at", contractInstance.options.address);
|
||||
} catch (err) {
|
||||
Logger.error("Deployment failed for", name, "with params", JSON.stringify(params, null, 2), err.message)
|
||||
throw err
|
||||
}
|
||||
|
||||
return contractInstance
|
||||
}
|
||||
|
||||
private static replaceTokens(bytecode: string, tokens: any[]) {
|
||||
|
||||
for (const token of tokens) {
|
||||
|
||||
bytecode = bytecode.replace(
|
||||
new RegExp(`_+${token.name}_+`, "g"),
|
||||
token.address.replace("0x", ""))
|
||||
}
|
||||
// Logger.log(bytecode)
|
||||
|
||||
return bytecode.toString()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
--require ts-node/register
|
||||
--require source-map-support/register
|
||||
--full-trace
|
||||
--bail
|
||||
test/**/*.test.ts
|
||||
--require ts-node/register
|
||||
--require source-map-support/register
|
||||
--full-trace
|
||||
--bail
|
||||
--timeout 5000
|
||||
test/**/*.test.ts
|
|
@ -1,17 +1,25 @@
|
|||
import AquariusConnector from "../../src/aquarius/AquariusConnector"
|
||||
|
||||
// @ts-ignore
|
||||
export default class AquariusConnectorMock extends AquariusConnector {
|
||||
|
||||
public async post(url: string, payload: any) {
|
||||
constructor(private returnData: any) {
|
||||
super()
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
json: () => {
|
||||
return []
|
||||
},
|
||||
text: () => {
|
||||
return ""
|
||||
},
|
||||
}
|
||||
// @ts-ignore
|
||||
private async fetch(url, opts): Promise<any> {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
ok: true,
|
||||
json: () => {
|
||||
return this.returnData ? this.returnData : []
|
||||
},
|
||||
text: () => {
|
||||
return this.returnData ? this.returnData.toString() : ""
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import ContractBase from "../../src/keeper/ContractBase"
|
||||
import ContractBase from "../../src/keeper/contracts/ContractBase"
|
||||
|
||||
export default class ContractBaseMock extends ContractBase {
|
||||
public async initMock() {
|
||||
this.init()
|
||||
await this.init()
|
||||
}
|
||||
|
||||
public async callMock(name: string, args: any[], from?: string) {
|
||||
|
@ -10,6 +10,6 @@ export default class ContractBaseMock extends ContractBase {
|
|||
}
|
||||
|
||||
public async sendMock(name: string, from: string, args: any[]) {
|
||||
return this.sendTransaction(name, from, args)
|
||||
return this.send(name, from, args)
|
||||
}
|
||||
}
|
||||
|
|
16
test/mocks/SecretStore.mock.ts
Normal file
16
test/mocks/SecretStore.mock.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import SecretStore from "@oceanprotocol/secret-store-client"
|
||||
|
||||
export default class SecretStoreMock extends SecretStore {
|
||||
|
||||
public async encryptDocument(documentId, document: any) {
|
||||
|
||||
return "0x283asdgahd1t371t23h"
|
||||
}
|
||||
|
||||
public async decryptDocument(documentId, encryptedDocument: any) {
|
||||
|
||||
return {
|
||||
doc: "test",
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import {assert} from "chai"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import Web3Provider from "../../src/keeper/Web3Provider"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import config from "../config"
|
||||
import TestContractHandler from "../keeper/TestContractHandler"
|
||||
|
||||
let ocean: Ocean
|
||||
let accounts: Account[]
|
||||
|
@ -13,7 +13,7 @@ describe("Account", () => {
|
|||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await ContractHandler.deployContracts()
|
||||
await TestContractHandler.prepareContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
|
||||
accounts = await ocean.getAccounts()
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import {assert} from "chai"
|
||||
import AquariusProvider from "../../src/aquarius/AquariusProvider"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import Asset from "../../src/ocean/Asset"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import Order from "../../src/ocean/Order"
|
||||
import config from "../config"
|
||||
import AquariusMock from "../mocks/Aquarius.mock"
|
||||
|
||||
const testName = "Test Asset 2"
|
||||
const testDescription = "This asset is pure owange"
|
||||
const testPrice = 100
|
||||
const timeout = 100000
|
||||
|
||||
let ocean: Ocean
|
||||
let testAsset: Asset
|
||||
let accounts: Account[]
|
||||
let testPublisher: Account
|
||||
|
||||
describe("Asset", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
AquariusProvider.setAquarius(new AquariusMock(config))
|
||||
|
||||
await ContractHandler.deployContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
accounts = await ocean.getAccounts()
|
||||
testPublisher = accounts[0]
|
||||
testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
|
||||
|
||||
await ocean.register(testAsset)
|
||||
})
|
||||
|
||||
describe("#purchase()", () => {
|
||||
|
||||
it("should purchase an asset", async () => {
|
||||
|
||||
// todo
|
||||
const consumerAccount = accounts[5]
|
||||
const order: Order = await testAsset.purchase(consumerAccount, timeout)
|
||||
assert(order)
|
||||
})
|
||||
})
|
||||
})
|
53
test/ocean/IdGenerator.test.ts
Normal file
53
test/ocean/IdGenerator.test.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import * as assert from "assert"
|
||||
import IdGenerator from "../../src/ocean/IdGenerator"
|
||||
|
||||
describe("IdGenerator", () => {
|
||||
|
||||
describe("#generateId()", () => {
|
||||
|
||||
it("should generate an id", async () => {
|
||||
|
||||
const id = IdGenerator.generateId()
|
||||
assert(id)
|
||||
})
|
||||
|
||||
it("should generate an id that is 64 chars long", async () => {
|
||||
|
||||
const id: string = IdGenerator.generateId()
|
||||
assert(id.length === 64, id)
|
||||
})
|
||||
|
||||
it("should not contain -", async () => {
|
||||
|
||||
const id: string = IdGenerator.generateId()
|
||||
assert(id.indexOf("-") === -1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#generatePrefixedId()", () => {
|
||||
|
||||
it("should generate an id", async () => {
|
||||
|
||||
const id = IdGenerator.generatePrefixedId()
|
||||
assert(id)
|
||||
})
|
||||
|
||||
it("should generate an id that is 64 chars long", async () => {
|
||||
|
||||
const id: string = IdGenerator.generatePrefixedId()
|
||||
assert(id.length === 66, id)
|
||||
})
|
||||
|
||||
it("should be prefixed", async () => {
|
||||
|
||||
const id: string = IdGenerator.generatePrefixedId()
|
||||
assert(id.startsWith("0x"))
|
||||
})
|
||||
|
||||
it("should not contain -", async () => {
|
||||
|
||||
const id: string = IdGenerator.generatePrefixedId()
|
||||
assert(id.indexOf("-") === -1)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,46 +1,42 @@
|
|||
import {assert} from "chai"
|
||||
import AquariusProvider from "../../src/aquarius/AquariusProvider"
|
||||
import SearchQuery from "../../src/aquarius/query/SearchQuery"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import DDO from "../../src/ddo/DDO"
|
||||
import MetaData from "../../src/ddo/MetaData"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import Asset from "../../src/ocean/Asset"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import Order from "../../src/ocean/Order"
|
||||
import SecretStoreProvider from "../../src/secretstore/SecretStoreProvider"
|
||||
import config from "../config"
|
||||
import TestContractHandler from "../keeper/TestContractHandler"
|
||||
import AquariusMock from "../mocks/Aquarius.mock"
|
||||
import SecretStoreMock from "../mocks/SecretStore.mock"
|
||||
|
||||
let ocean: Ocean
|
||||
let accounts: Account[]
|
||||
let testAsset: Asset
|
||||
let testPublisher: Account
|
||||
|
||||
const name = "Test Asset 3"
|
||||
const description = "This asset is pure owange"
|
||||
const price = 100
|
||||
const timeout = 100000000
|
||||
|
||||
describe("Ocean", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
AquariusProvider.setAquarius(new AquariusMock(config))
|
||||
await ContractHandler.deployContracts()
|
||||
SecretStoreProvider.setSecretStore(new SecretStoreMock(config))
|
||||
await TestContractHandler.prepareContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
accounts = await ocean.getAccounts()
|
||||
|
||||
testPublisher = accounts[0]
|
||||
testAsset = new Asset(name, description, price, testPublisher)
|
||||
})
|
||||
|
||||
describe("#getInstance()", () => {
|
||||
|
||||
it("should list accounts", async () => {
|
||||
it("should get an instance of cean", async () => {
|
||||
|
||||
const ocn = Ocean.getInstance(config)
|
||||
const oceanInstance: Ocean = await Ocean.getInstance(config)
|
||||
|
||||
assert(ocn)
|
||||
assert(oceanInstance)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#getAccounts()", () => {
|
||||
|
@ -56,35 +52,16 @@ describe("Ocean", () => {
|
|||
|
||||
})
|
||||
|
||||
describe("#register()", () => {
|
||||
describe("#registerAsset()", () => {
|
||||
|
||||
it("should register an asset", async () => {
|
||||
|
||||
const assetId: string = await ocean.register(testAsset)
|
||||
const metaData: MetaData = new MetaData()
|
||||
const ddo: DDO = await ocean.registerAsset(metaData, testPublisher)
|
||||
|
||||
assert(assetId.length === 66)
|
||||
assert(assetId.startsWith("0x"))
|
||||
assert(ddo)
|
||||
assert(ddo.id.startsWith("did:op:"))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#getOrdersByConsumer()", () => {
|
||||
|
||||
it("should list orders", async () => {
|
||||
|
||||
const testConsumer = accounts[1]
|
||||
const asset: Asset = new Asset("getOrdersByConsumer test", description, price, testPublisher)
|
||||
|
||||
await ocean.register(asset)
|
||||
|
||||
const order: Order = await asset.purchase(testConsumer, timeout)
|
||||
const orders = await ocean.getOrdersByAccount(testConsumer)
|
||||
|
||||
assert(orders.length === 1)
|
||||
assert(orders[0].getId() === order.getId())
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#searchAssets()", () => {
|
||||
|
@ -101,7 +78,7 @@ describe("Ocean", () => {
|
|||
value: 1,
|
||||
},
|
||||
text: "Office",
|
||||
}
|
||||
} as SearchQuery
|
||||
|
||||
const assets: any[] = await ocean.searchAssets(query)
|
||||
|
||||
|
@ -109,4 +86,15 @@ describe("Ocean", () => {
|
|||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#searchAssetsByText()", () => {
|
||||
|
||||
it("should search for assets", async () => {
|
||||
const text = "office"
|
||||
const assets: any[] = await ocean.searchAssetsByText(text)
|
||||
|
||||
assert(assets)
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
import {assert} from "chai"
|
||||
import AquariusProvider from "../../src/aquarius/AquariusProvider"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import ContractHandler from "../../src/keeper/ContractHandler"
|
||||
import AccessStatus from "../../src/models/AccessStatus"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import Asset from "../../src/ocean/Asset"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import Order from "../../src/ocean/Order"
|
||||
import config from "../config"
|
||||
import AquariusMock from "../mocks/Aquarius.mock"
|
||||
|
||||
const testName = "Order Test Asset"
|
||||
const testDescription = "This asset is pure owange"
|
||||
const testPrice = 100
|
||||
const timeout = 1000000
|
||||
const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1Mzk3ODcxMDEsImV4cCI6NDcyNjk5NjcwNCwiYXVkIjoiIiwic3ViIjoiIiwic2VydmljZV9lbmRwb2ludCI6Imh0dHA6Ly9hZGFzZCIsInJlc291cmNlX2lkIjoiMTIzNDUifQ.2H3TRC3CAToVE9divSckwHi_HNvgOHKrtJPo8128qrKBHTk7YYb0UNfVCuYqwhGR"
|
||||
|
||||
let ocean: Ocean
|
||||
let testAsset: Asset
|
||||
let accounts: Account[]
|
||||
let testPublisher: Account
|
||||
let testConsumer: Account
|
||||
|
||||
describe("Order", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
AquariusProvider.setAquarius(new AquariusMock(config))
|
||||
await ContractHandler.deployContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
accounts = await ocean.getAccounts()
|
||||
testPublisher = accounts[0]
|
||||
testConsumer = accounts[1]
|
||||
// register an asset to play around with
|
||||
testAsset = new Asset(testName, testDescription, testPrice, testPublisher)
|
||||
await ocean.register(testAsset)
|
||||
})
|
||||
|
||||
describe("#pay()", async () => {
|
||||
|
||||
it("should pay for an order", async () => {
|
||||
|
||||
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||
assert(order)
|
||||
|
||||
await order.commit(accessToken)
|
||||
await testConsumer.requestTokens(testAsset.price)
|
||||
const paymentId: string = await order.pay(testConsumer)
|
||||
assert(paymentId)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("#commit()", async () => {
|
||||
|
||||
it("should commit the order", async () => {
|
||||
|
||||
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||
assert(order)
|
||||
|
||||
await order.commit(accessToken)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getStatus()", async () => {
|
||||
|
||||
it("should get status Requested on new order", async () => {
|
||||
|
||||
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||
assert(order)
|
||||
|
||||
const status: AccessStatus = await order.getStatus()
|
||||
assert(status === AccessStatus.Requested)
|
||||
})
|
||||
|
||||
it("should get status Delivered on commited order", async () => {
|
||||
|
||||
const order: Order = await testAsset.purchase(testConsumer, timeout)
|
||||
assert(order)
|
||||
|
||||
await order.commit(accessToken)
|
||||
|
||||
const status: AccessStatus = await order.getStatus()
|
||||
assert(status === AccessStatus.Delivered)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#consume()", () => {
|
||||
|
||||
it("should consume an asset", async () => {
|
||||
const consumerAccount = accounts[5]
|
||||
await consumerAccount.requestTokens(testAsset.price)
|
||||
// place order - consumer
|
||||
const order: Order = await testAsset.purchase(consumerAccount, timeout)
|
||||
// commit order - provider
|
||||
await order.commit(accessToken)
|
||||
// pay order - consumer
|
||||
await order.pay(consumerAccount)
|
||||
const url = await order.consume(consumerAccount)
|
||||
assert(url)
|
||||
})
|
||||
})
|
||||
})
|
131
test/ocean/ServiceAgreement.test.ts
Normal file
131
test/ocean/ServiceAgreement.test.ts
Normal file
|
@ -0,0 +1,131 @@
|
|||
import {assert} from "chai"
|
||||
import AquariusConnectorProvider from "../../src/aquarius/AquariusConnectorProvider"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import DDOCondition from "../../src/ddo/Condition"
|
||||
import DDO from "../../src/ddo/DDO"
|
||||
import Parameter from "../../src/ddo/Parameter"
|
||||
import Service from "../../src/ddo/Service"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import IdGenerator from "../../src/ocean/IdGenerator"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import Condition from "../../src/ocean/ServiceAgreements/Condition"
|
||||
import ServiceAgreement from "../../src/ocean/ServiceAgreements/ServiceAgreement"
|
||||
import ServiceAgreementTemplate from "../../src/ocean/ServiceAgreements/ServiceAgreementTemplate"
|
||||
import Access from "../../src/ocean/ServiceAgreements/Templates/Access"
|
||||
import config from "../config"
|
||||
import TestContractHandler from "../keeper/TestContractHandler"
|
||||
import AquariusConnectorMock from "../mocks/AquariusConnector.mock"
|
||||
|
||||
let ocean: Ocean
|
||||
let accounts: Account[]
|
||||
let publisherAccount: Account
|
||||
let consumerAccount: Account
|
||||
|
||||
let serviceDefinition
|
||||
|
||||
describe("ServiceAgreement", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await TestContractHandler.prepareContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
accounts = await ocean.getAccounts()
|
||||
|
||||
publisherAccount = accounts[1]
|
||||
consumerAccount = accounts[2]
|
||||
|
||||
const serviceAgreementTemplate: ServiceAgreementTemplate =
|
||||
new ServiceAgreementTemplate(new Access())
|
||||
|
||||
// get condition keys from template
|
||||
const conditions: Condition[] = await serviceAgreementTemplate.getConditions()
|
||||
|
||||
// create ddo conditions out of the keys
|
||||
const ddoConditions: DDOCondition[] = conditions.map((condition): DDOCondition => {
|
||||
return {
|
||||
name: condition.methodReflection.methodName,
|
||||
timeout: condition.timeout,
|
||||
conditionKey: condition.condtionKey,
|
||||
parameters: condition.methodReflection.inputs.map((input) => {
|
||||
return {
|
||||
...input,
|
||||
value: "xx",
|
||||
}as Parameter
|
||||
}),
|
||||
} as DDOCondition
|
||||
})
|
||||
|
||||
serviceDefinition = [
|
||||
{
|
||||
serviceDefinitionId: IdGenerator.generateId(),
|
||||
templateId: serviceAgreementTemplate.getId(),
|
||||
conditions: ddoConditions,
|
||||
} as Service,
|
||||
]
|
||||
|
||||
})
|
||||
|
||||
describe("#signServiceAgreement()", () => {
|
||||
it("should execute an service agreement", async () => {
|
||||
|
||||
const id: string = IdGenerator.generateId()
|
||||
const did: string = `did:op:${id}`
|
||||
const ddo = new DDO({id: did, service: serviceDefinition})
|
||||
const assetId: string = IdGenerator.generateId()
|
||||
const serviceAgreementId: string = IdGenerator.generateId()
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
|
||||
const serviceAgreement: ServiceAgreement =
|
||||
await ServiceAgreement.signServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount,
|
||||
publisherAccount)
|
||||
assert(serviceAgreement)
|
||||
|
||||
const serviceDefinitionId = serviceAgreement.getId()
|
||||
assert(serviceDefinitionId)
|
||||
assert(serviceDefinitionId !== did)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getStatus()", () => {
|
||||
it("should get the status of a newly created service agreement", async () => {
|
||||
|
||||
const id: string = IdGenerator.generateId()
|
||||
const did: string = `did:op:${id}`
|
||||
const ddo = new DDO({id: did, service: serviceDefinition})
|
||||
const assetId: string = IdGenerator.generateId()
|
||||
const serviceAgreementId: string = IdGenerator.generateId()
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
|
||||
const serviceAgreement: ServiceAgreement =
|
||||
await ServiceAgreement.signServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount,
|
||||
publisherAccount)
|
||||
assert(serviceAgreement)
|
||||
|
||||
const status = await serviceAgreement.getStatus()
|
||||
assert(status === false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#grantAccess()", () => {
|
||||
it("should grant access in that service agreement", async () => {
|
||||
|
||||
const id: string = IdGenerator.generateId()
|
||||
const did: string = `did:op:${id}`
|
||||
const ddo = new DDO({id: did, service: serviceDefinition})
|
||||
const assetId: string = IdGenerator.generateId()
|
||||
const serviceAgreementId: string = IdGenerator.generateId()
|
||||
|
||||
// @ts-ignore
|
||||
AquariusConnectorProvider.setConnector(new AquariusConnectorMock(ddo))
|
||||
const serviceAgreement: ServiceAgreement =
|
||||
await ServiceAgreement.signServiceAgreement(assetId, ddo, serviceAgreementId, consumerAccount,
|
||||
publisherAccount)
|
||||
assert(serviceAgreement)
|
||||
|
||||
const fulfilled: boolean = await serviceAgreement.grantAccess(assetId, IdGenerator.generateId())
|
||||
assert(fulfilled)
|
||||
})
|
||||
})
|
||||
})
|
60
test/ocean/ServiceAgreementTemplate.test.ts
Normal file
60
test/ocean/ServiceAgreementTemplate.test.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import {assert} from "chai"
|
||||
import ConfigProvider from "../../src/ConfigProvider"
|
||||
import Account from "../../src/ocean/Account"
|
||||
import IdGenerator from "../../src/ocean/IdGenerator"
|
||||
import Ocean from "../../src/ocean/Ocean"
|
||||
import ServiceAgreementTemplate from "../../src/ocean/ServiceAgreements/ServiceAgreementTemplate"
|
||||
import Access from "../../src/ocean/ServiceAgreements/Templates/Access"
|
||||
import TemplateBase from "../../src/ocean/ServiceAgreements/Templates/TemplateBase"
|
||||
import config from "../config"
|
||||
import TestContractHandler from "../keeper/TestContractHandler"
|
||||
|
||||
let ocean: Ocean
|
||||
let accounts: Account[]
|
||||
|
||||
describe("ServiceAgreementTemplate", () => {
|
||||
|
||||
before(async () => {
|
||||
ConfigProvider.setConfig(config)
|
||||
await TestContractHandler.prepareContracts()
|
||||
ocean = await Ocean.getInstance(config)
|
||||
accounts = await ocean.getAccounts()
|
||||
})
|
||||
|
||||
describe("#register()", () => {
|
||||
it("should setup an Access agreement template correctly", async () => {
|
||||
|
||||
const templateOwner = accounts[0]
|
||||
const access: TemplateBase = new Access()
|
||||
access.id = IdGenerator.generatePrefixedId()
|
||||
const serviceAgreementTemplate: ServiceAgreementTemplate =
|
||||
new ServiceAgreementTemplate(access)
|
||||
assert(serviceAgreementTemplate)
|
||||
|
||||
const registered: boolean = await serviceAgreementTemplate.register(templateOwner.getId())
|
||||
assert(registered)
|
||||
|
||||
assert(serviceAgreementTemplate.getId())
|
||||
assert((await serviceAgreementTemplate.getOwner()).getId() === templateOwner.getId())
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getStatus()", () => {
|
||||
it("should get the status of a newly deployed agreement template", async () => {
|
||||
|
||||
const publisherAccount = accounts[0]
|
||||
const access: TemplateBase = new Access()
|
||||
access.id = IdGenerator.generatePrefixedId()
|
||||
const serviceAgreementTemplate: ServiceAgreementTemplate =
|
||||
new ServiceAgreementTemplate(access)
|
||||
assert(serviceAgreementTemplate)
|
||||
|
||||
const registered: boolean = await serviceAgreementTemplate.register(publisherAccount.getId())
|
||||
assert(registered)
|
||||
|
||||
const templateStatus = await serviceAgreementTemplate.getStatus()
|
||||
assert(templateStatus === true)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
1
test/testdata/AccessToken.json
vendored
Normal file
1
test/testdata/AccessToken.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1Mzk3ODcxMDEsImV4cCI6NDcyNjk5NjcwNCwiYXVkIjoiIiwic3ViIjoiIiwic2VydmljZV9lbmRwb2ludCI6Imh0dHA6Ly9hZGFzZCIsInJlc291cmNlX2lkIjoiMTIzNDUifQ.2H3TRC3CAToVE9divSckwHi_HNvgOHKrtJPo8128qrKBHTk7YYb0UNfVCuYqwhGR"
|
218
test/testdata/ddo.json
vendored
Normal file
218
test/testdata/ddo.json
vendored
Normal file
|
@ -0,0 +1,218 @@
|
|||
{
|
||||
"@context": "https://w3id.org/future-method/v1",
|
||||
"id": "did:op:08a429b8529856d59867503f8056903a680935a76950bb9649785cc97869a43d",
|
||||
"publicKey": [
|
||||
{
|
||||
"id": "did:op:123456789abcdefghi#keys-1",
|
||||
"type": "RsaVerificationKey2018",
|
||||
"owner": "did:op:123456789abcdefghi",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
|
||||
},
|
||||
{
|
||||
"id": "did:op:123456789abcdefghi#keys-2",
|
||||
"type": "Ed25519VerificationKey2018",
|
||||
"owner": "did:op:123456789abcdefghi",
|
||||
"publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
|
||||
},
|
||||
{
|
||||
"id": "did:op:123456789abcdefghi#keys-3",
|
||||
"type": "RsaPublicKeyExchangeKey2018",
|
||||
"owner": "did:op:123456789abcdefghi",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n"
|
||||
}
|
||||
],
|
||||
"authentication": [
|
||||
{
|
||||
"type": "RsaSignatureAuthentication2018",
|
||||
"publicKey": "did:op:123456789abcdefghi#keys-1"
|
||||
},
|
||||
{
|
||||
"type": "ieee2410Authentication2018",
|
||||
"publicKey": "did:op:123456789abcdefghi#keys-2"
|
||||
}
|
||||
],
|
||||
"proof": {
|
||||
"type": "UUIDSignature",
|
||||
"created": "2016-02-08T16:02:20Z",
|
||||
"creator": "did:example:8uQhQMGzWxR8vw5P3UWH1ja",
|
||||
"signatureValue": "QNB13Y7Q9...1tzjn4w=="
|
||||
},
|
||||
"service": [
|
||||
{
|
||||
"type": "Access",
|
||||
"serviceDefinitionId": "0",
|
||||
"serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/consume?pubKey=${pubKey}&serviceId={serviceId}&url={url}",
|
||||
"purchaseEndpoint": "http://mybrizo.org/api/v1/brizo/services/access/purchase?",
|
||||
"templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000000",
|
||||
"conditions": [
|
||||
{
|
||||
"name": "lockPayment",
|
||||
"timeout": 0,
|
||||
"conditionKey": {
|
||||
"contractAddress": "0x...",
|
||||
"fingerprint": "0x..."
|
||||
},
|
||||
"parameters": {
|
||||
"assetId": "bytes32",
|
||||
"price": "integer"
|
||||
},
|
||||
"events": {
|
||||
"PaymentLocked": {
|
||||
"actorType": [
|
||||
"publisher"
|
||||
],
|
||||
"handlers": [
|
||||
{
|
||||
"moduleName": "accessControl",
|
||||
"functionName": "grantAccess",
|
||||
"version": "0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "releasePayment",
|
||||
"timeout": 0,
|
||||
"conditionKey": {
|
||||
"contractAddress": "0x...",
|
||||
"fingerprint": "0xXXXXXXXX"
|
||||
},
|
||||
"parameters": {
|
||||
"assetId": "bytes32",
|
||||
"price": "integer"
|
||||
},
|
||||
"events": {
|
||||
"PaymentReleased": {
|
||||
"actorType": [
|
||||
"publisher"
|
||||
],
|
||||
"handlers": [
|
||||
{
|
||||
"moduleName": "serviceAgreement",
|
||||
"functionName": "fulfillAgreement",
|
||||
"version": "0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "grantAccess",
|
||||
"timeout": 0,
|
||||
"conditionKey": {
|
||||
"contractAddress": "0x",
|
||||
"fingerprint": "0xXXXXXXXX"
|
||||
},
|
||||
"parameters": {
|
||||
"assetId": "bytes32",
|
||||
"documentKeyId": "bytes32"
|
||||
},
|
||||
"events": {
|
||||
"AccessGranted": {
|
||||
"actorType": [
|
||||
"consumer"
|
||||
],
|
||||
"handlers": [
|
||||
{
|
||||
"moduleName": "asset",
|
||||
"functionName": "consumeService",
|
||||
"version": "0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refundPayment",
|
||||
"timeout": 1,
|
||||
"condition_key": {
|
||||
"contractAddress": "0x...",
|
||||
"fingerprint": "0xXXXXXXXX"
|
||||
},
|
||||
"parameters": {
|
||||
"assetId": "bytes32",
|
||||
"price": "int"
|
||||
},
|
||||
"events": {
|
||||
"PaymentRefund": {
|
||||
"actorType": [
|
||||
"consumer"
|
||||
],
|
||||
"handlers": [
|
||||
{
|
||||
"moduleName": "serviceAgreement",
|
||||
"functionName": "fulfillAgreement",
|
||||
"version": "0.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "CloudCompute",
|
||||
"serviceDefinitionId": "1",
|
||||
"serviceEndpoint": "http://mybrizo.org/api/v1/brizo/services/compute?pubKey=${pubKey}&serviceId={serviceId}&algo={algo}&container={container}",
|
||||
"templateId": "044852b2a670ade5407e78fb2863c51000000000000000000000000000000002"
|
||||
},
|
||||
{
|
||||
"type": "Metadata",
|
||||
"serviceDefinitionId": "2",
|
||||
"serviceEndpoint": "http://myaquarius.org/api/v1/provider/assets/metadata/{did}",
|
||||
"metadata": {
|
||||
"base": {
|
||||
"name": "UK Weather information 2011",
|
||||
"type": "dataset",
|
||||
"description": "Weather information of UK including temperature and humidity",
|
||||
"size": "3.1gb",
|
||||
"dateCreated": "2012-10-10T17:00:000Z",
|
||||
"author": "Met Office",
|
||||
"license": "CC-BY",
|
||||
"copyrightHolder": "Met Office",
|
||||
"encoding": "UTF-8",
|
||||
"compression": "zip",
|
||||
"contentType": "text/csv",
|
||||
"workExample": "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68",
|
||||
"contentUrls": [
|
||||
"https://testocnfiles.blob.core.windows.net/testfiles/testzkp.zip"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"name": "Sample of Asset Data",
|
||||
"type": "sample",
|
||||
"url": "https://foo.com/sample.csv"
|
||||
},
|
||||
{
|
||||
"name": "Data Format Definition",
|
||||
"type": "format",
|
||||
"AssetID": "4d517500da0acb0d65a716f61330969334630363ce4a6a9d39691026ac7908ea"
|
||||
}
|
||||
],
|
||||
"inLanguage": "en",
|
||||
"tags": "weather, uk, 2011, temperature, humidity",
|
||||
"price": 10
|
||||
},
|
||||
"curation": {
|
||||
"rating": 0.93,
|
||||
"numVotes": 123,
|
||||
"schema": "Binary Votting"
|
||||
},
|
||||
"additionalInformation": {
|
||||
"updateFrecuency": "yearly",
|
||||
"structuredMarkup": [
|
||||
{
|
||||
"uri": "http://skos.um.es/unescothes/C01194/jsonld",
|
||||
"mediaType": "application/ld+json"
|
||||
},
|
||||
{
|
||||
"uri": "http://skos.um.es/unescothes/C01194/turtle",
|
||||
"mediaType": "text/turtle"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"es6",
|
||||
"es7"
|
||||
|
|
Loading…
Reference in New Issue
Block a user