mirror of
https://github.com/oceanprotocol/ocean.js.git
synced 2024-11-26 20:39:05 +01:00
Merge pull request #30 from oceanprotocol/feature/alex_play
Feature: move squid-js to ocean-lib-js
This commit is contained in:
commit
cee10e72cb
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
||||
# https://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{json,yml,yaml,md}]
|
||||
indent_size = 2
|
||||
|
48
.eslintrc
Normal file
48
.eslintrc
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": false
|
||||
},
|
||||
"project": [
|
||||
"./tsconfig.json",
|
||||
"./test/unit/tsconfig.json",
|
||||
"./test/integration/tsconfig.json"
|
||||
]
|
||||
},
|
||||
"extends": [
|
||||
"oceanprotocol",
|
||||
"prettier/standard",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{ "multiline": { "delimiter": "none" } }
|
||||
],
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/indent": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-object-literal-type-assertion": "off",
|
||||
"@typescript-eslint/no-parameter-properties": "off",
|
||||
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||
"prefer-destructuring": ["warn"],
|
||||
"no-dupe-class-members": ["warn"],
|
||||
"no-useless-constructor": ["warn"]
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"mocha": true
|
||||
}
|
||||
}
|
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.nyc_output/
|
||||
coverage/
|
||||
doc/
|
||||
test/**/*.js
|
||||
src/**/*.js
|
||||
|
||||
src/metadata\.json
|
||||
.idea
|
||||
.vscode
|
21
.npmignore
Normal file
21
.npmignore
Normal file
@ -0,0 +1,21 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
.github
|
||||
.nyc_output
|
||||
.travis.yml
|
||||
test/
|
||||
src/
|
||||
tsconfig.json
|
||||
tslint.json
|
||||
oceanprotocol-squid-*.tgz
|
||||
squid-js.json
|
||||
barge/
|
||||
integration/
|
||||
plugins/
|
||||
scripts/
|
||||
webpack*
|
||||
ganache*
|
||||
.prettierrc
|
||||
.editorconfig
|
||||
.eslintrc
|
||||
SQUID_INTERFACE.md
|
6
.prettierrc
Normal file
6
.prettierrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 90,
|
||||
"trailingComma": "none"
|
||||
}
|
10
library.json
Normal file
10
library.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Ocean Library",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/oceanprotocol/ocean-lib-js.git"
|
||||
},
|
||||
"dependencies": [
|
||||
|
||||
]
|
||||
}
|
15592
package-lock.json
generated
Normal file
15592
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
123
package.json
Normal file
123
package.json
Normal file
@ -0,0 +1,123 @@
|
||||
{
|
||||
"name": "@oceanprotocol/lib",
|
||||
"version": "0.0.1",
|
||||
"description": "JavaScript client library for Ocean Protocol",
|
||||
"main": "./dist/node/lib.js",
|
||||
"typings": "./dist/node/lib.d.ts",
|
||||
"unpkg": "./dist/browser/lib.cjs2.min.js",
|
||||
"scripts": {
|
||||
"build": "npm run clean && npm run build:tsc && npm run build:dist",
|
||||
"build:tsc": "tsc --sourceMap",
|
||||
"build:metadata": "./scripts/get-metadata.js > src/metadata.json",
|
||||
"build:dist": "cross-env NODE_ENV=production webpack",
|
||||
"clean": "rm -rf ./dist/ ./doc/ ./.nyc_output",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .ts,.tsx .",
|
||||
"format": "prettier --parser typescript --ignore-path .gitignore --write '**/*.{js,jsx,ts,tsx}'",
|
||||
"run": "ts-node",
|
||||
"release": "release-it --non-interactive",
|
||||
"changelog": "auto-changelog -p",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/oceanprotocol/ocean-lib-js.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Ocean Protocol <devops@oceanprotocol.com>",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/oceanprotocol/ocean-lib-js/issues"
|
||||
},
|
||||
"homepage": "https://github.com/oceanprotocol/ocean-lib-js#readme",
|
||||
"peerDependencies": {
|
||||
"web3": "^1.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethereum-navigator/navigator": "^0.5.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"deprecated-decorator": "^0.1.6",
|
||||
"node-fetch": "^2.6.0",
|
||||
"save-file": "^2.3.1",
|
||||
"uuid": "^8.0.0",
|
||||
"web3": "^1.2.6",
|
||||
"whatwg-url": "^8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@release-it/bumper": "^1.1.0",
|
||||
"@truffle/hdwallet-provider": "^1.0.33",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/chai-spies": "^1.0.1",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^14.0.0",
|
||||
"@types/node-fetch": "^2.5.5",
|
||||
"@types/sinon": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.23.0",
|
||||
"@typescript-eslint/parser": "^2.23.0",
|
||||
"auto-changelog": "^2.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-spies": "^1.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-oceanprotocol": "^1.5.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"lcov-result-merger": "^3.1.0",
|
||||
"mocha": "^7.1.0",
|
||||
"mock-local-storage": "^1.1.11",
|
||||
"nyc": "^15.0.0",
|
||||
"ora": "^4.0.2",
|
||||
"prettier": "^1.19.1",
|
||||
"sinon": "^9.0.1",
|
||||
"source-map-support": "^0.5.16",
|
||||
"ts-node": "^8.6.2",
|
||||
"typedoc": "^0.17.1",
|
||||
"typescript": "^3.8.3",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"nyc": {
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"extension": [
|
||||
".ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register"
|
||||
],
|
||||
"reporter": [
|
||||
"text",
|
||||
"lcov",
|
||||
"html"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"instrument": true
|
||||
},
|
||||
"release-it": {
|
||||
"hooks": {
|
||||
"after:bump": "npm run changelog && npm run doc:json"
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/bumper": {
|
||||
"out": [
|
||||
"package.json",
|
||||
"package-lock.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"git": {
|
||||
"tagName": "v${version}"
|
||||
},
|
||||
"github": {
|
||||
"release": true,
|
||||
"assets": [
|
||||
"dist/squid-js.json"
|
||||
]
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
}
|
||||
}
|
||||
}
|
19
scripts/get-metadata.js
Executable file
19
scripts/get-metadata.js
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
const packageInfo = require('../package.json')
|
||||
|
||||
const execSync = require('child_process').execSync
|
||||
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
{
|
||||
version: require('../package.json').version,
|
||||
commit: execSync(`git rev-parse HEAD`)
|
||||
.toString()
|
||||
.trim()
|
||||
},
|
||||
null,
|
||||
' '
|
||||
)
|
||||
)
|
48
src/.eslintrc
Normal file
48
src/.eslintrc
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": false
|
||||
},
|
||||
"project": [
|
||||
"./tsconfig.json",
|
||||
"./test/unit/tsconfig.json",
|
||||
"./test/integration/tsconfig.json"
|
||||
]
|
||||
},
|
||||
"extends": [
|
||||
"oceanprotocol",
|
||||
"prettier/standard",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"rules": {
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{ "multiline": { "delimiter": "none" } }
|
||||
],
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/indent": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-object-literal-type-assertion": "off",
|
||||
"@typescript-eslint/no-parameter-properties": "off",
|
||||
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||
"prefer-destructuring": ["warn"],
|
||||
"no-dupe-class-members": ["warn"],
|
||||
"no-useless-constructor": ["warn"]
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"mocha": true
|
||||
}
|
||||
}
|
1
src/@types/node_modules.d.ts
vendored
Normal file
1
src/@types/node_modules.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module '@ethereum-navigator/navigator'
|
94
src/Instantiable.abstract.ts
Normal file
94
src/Instantiable.abstract.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import Web3 from 'web3'
|
||||
import Config from './models/Config'
|
||||
import { Logger, LoggerInstance, LogLevel } from './utils'
|
||||
import { Ocean } from './ocean/Ocean'
|
||||
import { OceanFactoryABI } from './datatokens/FactoryABI'
|
||||
import { OceanDataTokenABI } from './datatokens/DatatokensABI'
|
||||
|
||||
export interface InstantiableConfig {
|
||||
ocean: Ocean
|
||||
config?: Config
|
||||
web3?: Web3
|
||||
logger?: Logger
|
||||
}
|
||||
|
||||
export function generateIntantiableConfigFromConfig(
|
||||
config: Config
|
||||
): Partial<InstantiableConfig> {
|
||||
const logLevel =
|
||||
typeof config.verbose !== 'number'
|
||||
? config.verbose
|
||||
? LogLevel.Log
|
||||
: LogLevel.None
|
||||
: (config.verbose as LogLevel)
|
||||
return {
|
||||
config,
|
||||
web3: config.web3Provider,
|
||||
logger: new Logger(logLevel)
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Instantiable {
|
||||
protected get ocean() {
|
||||
if (!this._ocean) {
|
||||
this.logger.error('Ocean instance is not defined.')
|
||||
}
|
||||
return this._ocean
|
||||
}
|
||||
|
||||
protected get web3() {
|
||||
if (!this._web3) {
|
||||
this.logger.error('Web3 instance is not defined.')
|
||||
}
|
||||
return this._web3
|
||||
}
|
||||
|
||||
protected get config() {
|
||||
if (!this._config) {
|
||||
this.logger.error('Config instance is not defined.')
|
||||
}
|
||||
return this._config
|
||||
}
|
||||
|
||||
protected get logger() {
|
||||
if (!this._logger) {
|
||||
LoggerInstance.error('Logger instance is not defined.')
|
||||
LoggerInstance.error('Using default instance.')
|
||||
return LoggerInstance
|
||||
}
|
||||
return this._logger
|
||||
}
|
||||
|
||||
protected get instanceConfig(): InstantiableConfig {
|
||||
const { ocean, web3, config, logger } = this
|
||||
return { ocean, web3, config, logger }
|
||||
}
|
||||
|
||||
public static async getInstance(...args: any[]): Promise<any>
|
||||
|
||||
public static async getInstance(config: InstantiableConfig): Promise<any> {
|
||||
LoggerInstance.warn('getInstance() methods has needs to be added to child class.')
|
||||
}
|
||||
|
||||
protected static setInstanceConfig<T extends Instantiable>(
|
||||
instance: T,
|
||||
{ ocean, config, web3, logger }: InstantiableConfig
|
||||
) {
|
||||
instance._ocean = ocean
|
||||
instance._config = config
|
||||
instance._web3 = web3
|
||||
instance._logger = logger
|
||||
}
|
||||
|
||||
private _ocean: Ocean
|
||||
|
||||
private _web3: Web3
|
||||
|
||||
private _config: Config
|
||||
|
||||
private _logger: Logger
|
||||
|
||||
protected setInstanceConfig(config: InstantiableConfig) {
|
||||
Instantiable.setInstanceConfig(this, config)
|
||||
}
|
||||
}
|
425
src/aquarius/Aquarius.ts
Normal file
425
src/aquarius/Aquarius.ts
Normal file
@ -0,0 +1,425 @@
|
||||
import { URL } from 'whatwg-url'
|
||||
import { DDO } from '../ddo/DDO'
|
||||
import DID from '../ocean/DID'
|
||||
import { EditableMetaData } from '../ddo/MetaData'
|
||||
import { Logger } from '../utils'
|
||||
import { WebServiceConnector } from '../ocean/utils/WebServiceConnector'
|
||||
|
||||
const apiPath = '/api/v1/aquarius/assets/ddo'
|
||||
|
||||
export interface QueryResult {
|
||||
results: DDO[]
|
||||
page: number
|
||||
totalPages: number
|
||||
totalResults: number
|
||||
}
|
||||
|
||||
export interface SearchQuery {
|
||||
text?: string
|
||||
offset?: number
|
||||
page?: number
|
||||
query: { [property: string]: string | number | string[] | number[] }
|
||||
sort?: { [jsonPath: string]: number }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an interface with Aquarius.
|
||||
* Aquarius provides an off-chain database store for metadata about data assets.
|
||||
*/
|
||||
export class Aquarius {
|
||||
public fetch: WebServiceConnector
|
||||
private logger: Logger
|
||||
private aquariusUri: string
|
||||
|
||||
private get url() {
|
||||
return this.aquariusUri
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate Aquarius (independently of Ocean) for off-chain interaction.
|
||||
* @param {String} aquariusUri
|
||||
* @param {Logger} logger
|
||||
*/
|
||||
constructor(aquariusUri: string, logger: Logger) {
|
||||
this.fetch = new WebServiceConnector(logger)
|
||||
this.logger = logger
|
||||
this.aquariusUri = aquariusUri
|
||||
}
|
||||
|
||||
public async getVersionInfo() {
|
||||
return (await this.fetch.get(this.url)).json()
|
||||
}
|
||||
|
||||
public async getAccessUrl(accessToken: any, payload: any): Promise<string> {
|
||||
const accessUrl: string = await this.fetch
|
||||
.post(`${accessToken.service_endpoint}/${accessToken.resource_id}`, payload)
|
||||
.then((response: any): string => {
|
||||
if (response.ok) {
|
||||
return response.text()
|
||||
}
|
||||
this.logger.error('Failed: ', response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
.then((consumptionUrl: string): string => {
|
||||
this.logger.error('Success accessing consume endpoint: ', consumptionUrl)
|
||||
return consumptionUrl
|
||||
})
|
||||
.catch(error => {
|
||||
this.logger.error(
|
||||
'Error fetching the data asset consumption url: ',
|
||||
error
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
return accessUrl
|
||||
}
|
||||
|
||||
/**
|
||||
* Search over the DDOs using a query.
|
||||
* @param {SearchQuery} query Query to filter the DDOs.
|
||||
* @return {Promise<QueryResult>}
|
||||
*/
|
||||
public async queryMetadata(query: SearchQuery): Promise<QueryResult> {
|
||||
const result: QueryResult = await this.fetch
|
||||
.post(`${this.url}${apiPath}/query`, JSON.stringify(query))
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json() as DDO[]
|
||||
}
|
||||
this.logger.error(
|
||||
'queryMetadata failed:',
|
||||
response.status,
|
||||
response.statusText
|
||||
)
|
||||
return this.transformResult()
|
||||
})
|
||||
.then(results => {
|
||||
return this.transformResult(results)
|
||||
})
|
||||
.catch(error => {
|
||||
this.logger.error('Error fetching querying metadata: ', error)
|
||||
return this.transformResult()
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Search over the DDOs using a query.
|
||||
* @param {SearchQuery} query Query to filter the DDOs.
|
||||
* @return {Promise<QueryResult>}
|
||||
*/
|
||||
public async queryMetadataByText(query: SearchQuery): Promise<QueryResult> {
|
||||
const fullUrl = new URL(`${this.url}${apiPath}/query`)
|
||||
fullUrl.searchParams.append('text', query.text)
|
||||
fullUrl.searchParams.append(
|
||||
'sort',
|
||||
decodeURIComponent(JSON.stringify(query.sort))
|
||||
)
|
||||
fullUrl.searchParams.append('offset', query.offset.toString())
|
||||
fullUrl.searchParams.append('page', query.page.toString())
|
||||
|
||||
const result: QueryResult = await this.fetch
|
||||
.get(fullUrl)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json() as DDO[]
|
||||
}
|
||||
this.logger.log(
|
||||
'queryMetadataByText failed:',
|
||||
response.status,
|
||||
response.statusText
|
||||
)
|
||||
return this.transformResult()
|
||||
})
|
||||
.then(results => {
|
||||
return this.transformResult(results)
|
||||
})
|
||||
.catch(error => {
|
||||
this.logger.error('Error fetching querying metadata by text: ', error)
|
||||
return this.transformResult()
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a DDO in Aquarius.
|
||||
* @param {DDO} ddo DDO to be stored.
|
||||
* @return {Promise<DDO>} Final DDO.
|
||||
*/
|
||||
public async storeDDO(ddo: DDO): Promise<DDO> {
|
||||
const fullUrl = `${this.url}${apiPath}`
|
||||
const result: DDO = await this.fetch
|
||||
.post(fullUrl, DDO.serialize(ddo))
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
this.logger.error(
|
||||
'storeDDO failed:',
|
||||
response.status,
|
||||
response.statusText,
|
||||
ddo
|
||||
)
|
||||
return null as DDO
|
||||
})
|
||||
.then((response: DDO) => {
|
||||
return new DDO(response) as DDO
|
||||
})
|
||||
.catch(error => {
|
||||
this.logger.error('Error fetching querying metadata: ', error)
|
||||
return null as DDO
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a DDO by DID.
|
||||
* @param {DID | string} did DID of the asset.
|
||||
* @return {Promise<DDO>} DDO of the asset.
|
||||
*/
|
||||
public async retrieveDDO(
|
||||
did: DID | string,
|
||||
metadataServiceEndpoint?: string
|
||||
): Promise<DDO> {
|
||||
did = did && DID.parse(did)
|
||||
const fullUrl = metadataServiceEndpoint || `${this.url}${apiPath}/${did.getDid()}`
|
||||
const result = await this.fetch
|
||||
.get(fullUrl)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.json()
|
||||
}
|
||||
this.logger.log(
|
||||
'retrieveDDO failed:',
|
||||
response.status,
|
||||
response.statusText,
|
||||
did
|
||||
)
|
||||
return null as DDO
|
||||
})
|
||||
.then((response: DDO) => {
|
||||
return new DDO(response) as DDO
|
||||
})
|
||||
.catch(error => {
|
||||
this.logger.error('Error fetching querying metadata: ', error)
|
||||
return null as DDO
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public async retrieveDDOByUrl(metadataServiceEndpoint?: string) {
|
||||
return this.retrieveDDO(undefined, metadataServiceEndpoint)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer ownership of a DDO
|
||||
* @param {DID | string} did DID of the asset to update.
|
||||
* @param {String} newOwner New owner of the DDO
|
||||
* @param {String} updated Updated field of the DDO
|
||||
* @param {String} signature Signature using updated field to verify that the consumer has rights
|
||||
* @return {Promise<String>} Result.
|
||||
*/
|
||||
public async transferOwnership(
|
||||
did: DID | string,
|
||||
newOwner: string,
|
||||
updated: string,
|
||||
signature: string
|
||||
): Promise<string> {
|
||||
did = did && DID.parse(did)
|
||||
const fullUrl = `${this.url}${apiPath}/owner/update/${did.getDid()}`
|
||||
const result = await this.fetch
|
||||
.put(
|
||||
fullUrl,
|
||||
JSON.stringify({
|
||||
signature: signature,
|
||||
updated: updated,
|
||||
newOwner: newOwner
|
||||
})
|
||||
)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.text
|
||||
}
|
||||
this.logger.log(
|
||||
'transferownership failed:',
|
||||
response.status,
|
||||
response.statusText
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
.catch(error => {
|
||||
this.logger.error('Error transfering ownership metadata: ', error)
|
||||
return null
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Compute Privacy
|
||||
* @param {DID | string} did DID of the asset to update.
|
||||
* @param {number } serviceIndex Service index
|
||||
* @param {boolean} allowRawAlgorithm Allow Raw Algorithms
|
||||
* @param {boolean} allowNetworkAccess Allow Raw Algorithms
|
||||
* @param {String[]} trustedAlgorithms Allow Raw Algorithms
|
||||
* @param {String} updated Updated field of the DDO
|
||||
* @param {String} signature Signature using updated field to verify that the consumer has rights
|
||||
* @return {Promise<String>} Result.
|
||||
*/
|
||||
public async updateComputePrivacy(
|
||||
did: DID | string,
|
||||
serviceIndex: number,
|
||||
allowRawAlgorithm: boolean,
|
||||
allowNetworkAccess: boolean,
|
||||
trustedAlgorithms: string[],
|
||||
updated: string,
|
||||
signature: string
|
||||
): Promise<string> {
|
||||
did = did && DID.parse(did)
|
||||
const fullUrl = `${this.url}${apiPath}/computePrivacy/update/${did.getDid()}`
|
||||
const result = await this.fetch
|
||||
.put(
|
||||
fullUrl,
|
||||
JSON.stringify({
|
||||
signature: signature,
|
||||
updated: updated,
|
||||
serviceIndex: serviceIndex,
|
||||
allowRawAlgorithm: allowRawAlgorithm,
|
||||
allowNetworkAccess: allowNetworkAccess,
|
||||
trustedAlgorithms: trustedAlgorithms
|
||||
})
|
||||
)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.text
|
||||
}
|
||||
this.logger.log(
|
||||
'update compute privacy failed:',
|
||||
response.status,
|
||||
response.statusText
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
.catch(error => {
|
||||
this.logger.error('Error updating compute privacy: ', error)
|
||||
return null
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Metadata for a DDO.
|
||||
* @param {did} string DID.
|
||||
* @param {newMetadata} EditableMetaData Metadata fields & new values.
|
||||
* @param {String} updated Updated field of the DDO
|
||||
* @param {String} signature Signature using updated field to verify that the consumer has rights
|
||||
* @return {Promise<String>} Result.
|
||||
*/
|
||||
public async editMetadata(
|
||||
did: DID | string,
|
||||
newMetadata: EditableMetaData,
|
||||
updated: string,
|
||||
signature: string
|
||||
): Promise<string> {
|
||||
did = did && DID.parse(did)
|
||||
const fullUrl = `${this.url}${apiPath}/metadata/${did.getDid()}`
|
||||
const data = Object()
|
||||
if (newMetadata.description != null) data.description = newMetadata.description
|
||||
if (newMetadata.title != null) data.title = newMetadata.title
|
||||
if (newMetadata.servicePrices != null)
|
||||
data.servicePrices = newMetadata.servicePrices
|
||||
if (newMetadata.links != null) data.links = newMetadata.links
|
||||
data.updated = updated
|
||||
data.signature = signature
|
||||
const result = await this.fetch
|
||||
.put(fullUrl, JSON.stringify(data))
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.text
|
||||
}
|
||||
this.logger.log(
|
||||
'editMetaData failed:',
|
||||
response.status,
|
||||
response.statusText
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
.catch(error => {
|
||||
this.logger.error('Error transfering ownership metadata: ', error)
|
||||
return null
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Retire a DDO (Delete)
|
||||
* @param {DID | string} did DID of the asset to update.
|
||||
* @param {String} updated Updated field of the DDO
|
||||
* @param {String} signature Signature using updated field to verify that the consumer has rights
|
||||
* @return {Promise<String>} Result.
|
||||
*/
|
||||
public async retire(
|
||||
did: DID | string,
|
||||
updated: string,
|
||||
signature: string
|
||||
): Promise<string> {
|
||||
did = did && DID.parse(did)
|
||||
const fullUrl = `${this.url}${apiPath}/${did.getDid()}`
|
||||
const result = await this.fetch
|
||||
.delete(
|
||||
fullUrl,
|
||||
JSON.stringify({
|
||||
signature: signature,
|
||||
updated: updated
|
||||
})
|
||||
)
|
||||
.then((response: any) => {
|
||||
if (response.ok) {
|
||||
return response.text
|
||||
}
|
||||
this.logger.log('retire failed:', response.status, response.statusText)
|
||||
return null
|
||||
})
|
||||
|
||||
.catch(error => {
|
||||
this.logger.error('Error transfering ownership metadata: ', error)
|
||||
return null
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public getServiceEndpoint(did: DID) {
|
||||
return `${this.url}/api/v1/aquarius/assets/ddo/did:op:${did.getId()}`
|
||||
}
|
||||
|
||||
public getURI() {
|
||||
return `${this.url}`
|
||||
}
|
||||
|
||||
private transformResult(
|
||||
{ results, page, total_pages: totalPages, total_results: totalResults }: any = {
|
||||
result: [],
|
||||
page: 0,
|
||||
total_pages: 0, // eslint-disable-line @typescript-eslint/camelcase
|
||||
total_results: 0 // eslint-disable-line @typescript-eslint/camelcase
|
||||
}
|
||||
): QueryResult {
|
||||
return {
|
||||
results: (results || []).map(ddo => new DDO(ddo as DDO)),
|
||||
page,
|
||||
totalPages,
|
||||
totalResults
|
||||
}
|
||||
}
|
||||
}
|
99
src/brizo/Brizo.ts
Normal file
99
src/brizo/Brizo.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { File, MetaDataAlgorithm } from '../ddo/MetaData'
|
||||
import Account from '../ocean/Account'
|
||||
import { noZeroX, noDidPrefixed } from '../utils'
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
import { DDO } from '../ddo/DDO'
|
||||
import { ServiceType } from '../ddo/Service'
|
||||
|
||||
const apiPath = '/api/v1/brizo/services'
|
||||
|
||||
/**
|
||||
* Provides a interface with Brizo.
|
||||
* Brizo is the technical component executed by the Publishers allowing to them to provide extended data services.
|
||||
*/
|
||||
export class Brizo extends Instantiable {
|
||||
private get url() {
|
||||
return this.config.brizoUri
|
||||
}
|
||||
|
||||
constructor(config: InstantiableConfig) {
|
||||
super()
|
||||
this.setInstanceConfig(config)
|
||||
}
|
||||
|
||||
public async getVersionInfo() {
|
||||
return (await this.ocean.utils.fetch.get(this.url)).json()
|
||||
}
|
||||
|
||||
public getURI() {
|
||||
return `${this.url}`
|
||||
}
|
||||
|
||||
public getPurchaseEndpoint() {
|
||||
return `${this.url}${apiPath}/access/initialize`
|
||||
}
|
||||
|
||||
public getConsumeEndpoint() {
|
||||
return `${this.url}${apiPath}/consume`
|
||||
}
|
||||
|
||||
public getEncryptEndpoint() {
|
||||
return `${this.url}${apiPath}/publish`
|
||||
}
|
||||
|
||||
public getComputeEndpoint() {
|
||||
return `${this.url}${apiPath}/compute`
|
||||
}
|
||||
|
||||
public async createSignature(account: Account, agreementId: string): Promise<string> {
|
||||
const signature = await this.ocean.utils.signature.signText(
|
||||
noZeroX(agreementId),
|
||||
account.getId()
|
||||
)
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
public async createHashSignature(account: Account, message: string): Promise<string> {
|
||||
const signature = await this.ocean.utils.signature.signWithHash(
|
||||
message,
|
||||
account.getId()
|
||||
)
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
public async encrypt(
|
||||
did: string,
|
||||
document: any,
|
||||
account: Account,
|
||||
dtAddress: string
|
||||
): Promise<string> {
|
||||
const signature = this.ocean.utils.signature.signWithHash(
|
||||
did,
|
||||
account.getId(),
|
||||
account.getPassword()
|
||||
)
|
||||
|
||||
const args = {
|
||||
did,
|
||||
signature,
|
||||
document: JSON.stringify(document),
|
||||
publisherAddress: account.getId()
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.ocean.utils.fetch.post(
|
||||
this.getEncryptEndpoint(),
|
||||
decodeURI(JSON.stringify(args))
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP request failed')
|
||||
}
|
||||
return await response.text()
|
||||
} catch (e) {
|
||||
this.logger.error(e)
|
||||
throw new Error('HTTP request failed')
|
||||
}
|
||||
}
|
||||
}
|
146
src/datatokens/Datatokens.ts
Normal file
146
src/datatokens/Datatokens.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import Account from '../ocean/Account'
|
||||
|
||||
/**
|
||||
* Provides a interface to DataTokens
|
||||
|
||||
*/
|
||||
export class DataTokens {
|
||||
public factoryAddress: string
|
||||
public factoryABI: object
|
||||
public datatokensABI: object
|
||||
public web3: any
|
||||
|
||||
/**
|
||||
* Instantiate DataTokens (independently of Ocean).
|
||||
* @param {String} factoryAddress
|
||||
* @param {Object} factoryABI
|
||||
* @param {Object} datatokensABI
|
||||
* @param {Object} web3
|
||||
|
||||
*/
|
||||
constructor(
|
||||
factoryAddress: string,
|
||||
factoryABI: object,
|
||||
datatokensABI: object,
|
||||
web3: any
|
||||
) {
|
||||
this.factoryAddress = factoryAddress
|
||||
this.factoryABI = factoryABI
|
||||
this.datatokensABI = datatokensABI
|
||||
this.web3 = web3
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new datatoken
|
||||
* @param {String} metaDataStoreURI
|
||||
* @param {Account} account
|
||||
* @return {Promise<string>} datatoken address
|
||||
*/
|
||||
public async create(metaDataStoreURI: string, account: Account): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve
|
||||
* @param {String} dataTokenAddress
|
||||
* @param {String} toAddress
|
||||
* @param {Number} amount
|
||||
* @param {Account} account
|
||||
* @return {Promise<string>} transactionId
|
||||
*/
|
||||
public async approve(
|
||||
dataTokenAddress: string,
|
||||
toAddress: string,
|
||||
amount: number,
|
||||
account: Account
|
||||
): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve & Lock for a specified number of blocks (reverts after that if not used)
|
||||
* @param {String} dataTokenAddress
|
||||
* @param {String} toAddress
|
||||
* @param {Number} amount
|
||||
* @param {Number} blocks
|
||||
* @param {Account} account
|
||||
* @return {Promise<string>} transactionId
|
||||
*/
|
||||
public async approveAndLock(
|
||||
dataTokenAddress: string,
|
||||
toAddress: string,
|
||||
amount: number,
|
||||
blocks: number,
|
||||
account: Account
|
||||
): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Mint
|
||||
* @param {String} dataTokenAddress
|
||||
* @param {Account} account
|
||||
* @param {Number} amount
|
||||
* @param {String} toAddress - only if toAddress is different from the minter
|
||||
* @return {Promise<string>} transactionId
|
||||
*/
|
||||
public async mint(
|
||||
dataTokenAddress: string,
|
||||
account: Account,
|
||||
amount: number,
|
||||
toAddress?: string
|
||||
): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer from Account to Address
|
||||
* @param {String} dataTokenAddress
|
||||
* @param {String} toAddress
|
||||
* @param {Number} amount
|
||||
* @param {Account} account
|
||||
* @return {Promise<string>} transactionId
|
||||
*/
|
||||
public async transfer(
|
||||
dataTokenAddress: string,
|
||||
toAddress: string,
|
||||
amount: number,
|
||||
account: Account
|
||||
): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer from Address to Account (needs an Approve operation before)
|
||||
* @param {String} dataTokenAddress
|
||||
* @param {String} fromAddress
|
||||
* @param {Number} amount
|
||||
* @param {Account} account
|
||||
* @return {Promise<string>} transactionId
|
||||
*/
|
||||
public async transferFrom(
|
||||
dataTokenAddress: string,
|
||||
fromAddress: string,
|
||||
amount: number,
|
||||
account: Account
|
||||
): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Account Balance for datatoken
|
||||
* @param {String} dataTokenAddress
|
||||
* @param {Account} account
|
||||
* @return {Promise<number>} balance
|
||||
*/
|
||||
public async balance(dataTokenAddress: string, account: Account): Promise<number> {
|
||||
// TO DO
|
||||
return 0
|
||||
}
|
||||
}
|
1
src/datatokens/DatatokensABI.ts
Normal file
1
src/datatokens/DatatokensABI.ts
Normal file
@ -0,0 +1 @@
|
||||
export const OceanDataTokenABI = {}
|
1
src/datatokens/FactoryABI.ts
Normal file
1
src/datatokens/FactoryABI.ts
Normal file
@ -0,0 +1 @@
|
||||
export const OceanFactoryABI = {}
|
4
src/ddo/Authentication.ts
Normal file
4
src/ddo/Authentication.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface Authentication {
|
||||
type: string
|
||||
publicKey: string
|
||||
}
|
161
src/ddo/DDO.ts
Normal file
161
src/ddo/DDO.ts
Normal file
@ -0,0 +1,161 @@
|
||||
import { Ocean } from '../ocean/Ocean'
|
||||
import { Authentication } from './Authentication'
|
||||
import { Proof } from './Proof'
|
||||
import { PublicKey } from './PublicKey'
|
||||
import { Service, ServiceType } from './Service'
|
||||
|
||||
/**
|
||||
* DID Descriptor Object.
|
||||
* Contains all the data related to an asset.
|
||||
*/
|
||||
export class DDO {
|
||||
/**
|
||||
* Serializes the DDO object.
|
||||
* @param {DDO} DDO to be serialized.
|
||||
* @return {string} DDO serialized.
|
||||
*/
|
||||
public static serialize(ddo: DDO): string {
|
||||
return JSON.stringify(ddo, null, 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the DDO object.
|
||||
* @param {DDO} DDO to be deserialized.
|
||||
* @return {string} DDO deserialized.
|
||||
*/
|
||||
public static deserialize(ddoString: string): DDO {
|
||||
const ddo = JSON.parse(ddoString)
|
||||
|
||||
return new DDO(ddo)
|
||||
}
|
||||
|
||||
public '@context': string = 'https://w3id.org/did/v1'
|
||||
|
||||
/**
|
||||
* DID, descentralized ID.
|
||||
* @type {string}
|
||||
*/
|
||||
public id: string = null
|
||||
|
||||
public created: string
|
||||
|
||||
public updated: string
|
||||
|
||||
public dtAddress: string
|
||||
|
||||
public publicKey: PublicKey[] = []
|
||||
|
||||
public authentication: Authentication[] = []
|
||||
|
||||
public service: Service[] = []
|
||||
|
||||
public proof: Proof
|
||||
|
||||
public constructor(ddo: Partial<DDO> = {}) {
|
||||
Object.assign(this, ddo, {
|
||||
created:
|
||||
(ddo && ddo.created) || new Date().toISOString().replace(/\.[0-9]{3}/, '')
|
||||
})
|
||||
}
|
||||
|
||||
public shortId(): string {
|
||||
return this.id.replace('did:op:', '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a service of a DDO by index.
|
||||
* @param {number} Service index.
|
||||
* @return {Service} Service.
|
||||
*/
|
||||
public findServiceById<T extends ServiceType>(index: number): Service<T> {
|
||||
if (isNaN(index)) {
|
||||
throw new Error('index is not set')
|
||||
}
|
||||
|
||||
const service = this.service.find(s => s.index === index)
|
||||
|
||||
return service as Service<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a service of a DDO by type.
|
||||
* @param {string} serviceType Service type.
|
||||
* @return {Service} Service.
|
||||
*/
|
||||
public findServiceByType<T extends ServiceType>(serviceType: T): Service<T> {
|
||||
if (!serviceType) {
|
||||
throw new Error('serviceType not set')
|
||||
}
|
||||
|
||||
return this.service.find(s => s.type === serviceType) as Service<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the checksum using the current content.
|
||||
* @param {Any} web3Provider
|
||||
* @return {string[]} DDO checksum.
|
||||
*/
|
||||
public getChecksum(web3Provider: any): string {
|
||||
const { attributes } = this.findServiceByType('metadata')
|
||||
const { files, name, author, license } = attributes.main
|
||||
|
||||
const values = [
|
||||
...(files || []).map(({ checksum }) => checksum).filter(_ => !!_),
|
||||
name,
|
||||
author,
|
||||
license,
|
||||
this.id
|
||||
]
|
||||
|
||||
return web3Provider
|
||||
.getWeb3()
|
||||
.utils.sha3(values.join(''))
|
||||
.replace(/^0x([a-f0-9]{64})(:!.+)?$/i, '0x$1')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates proof using personal sing.
|
||||
* @param {Ocean} ocean Ocean instance.
|
||||
* @param {string} publicKey Public key to be used on personal sign.
|
||||
* @param {string} password Password if it's required.
|
||||
* @return {Promise<Proof>} Proof object.
|
||||
*/
|
||||
public async generateProof(
|
||||
ocean: Ocean,
|
||||
publicKey: string,
|
||||
password?: string
|
||||
): Promise<Proof> {
|
||||
const checksum = this.getChecksum(ocean.web3Provider)
|
||||
|
||||
const signature = await ocean.utils.signature.signText(
|
||||
checksum,
|
||||
publicKey,
|
||||
password
|
||||
)
|
||||
|
||||
return {
|
||||
created: new Date().toISOString().replace(/\.[0-9]{3}/, ''),
|
||||
creator: publicKey,
|
||||
type: 'DDOIntegritySignature',
|
||||
signatureValue: signature
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and adds a proof using personal sing on the DDO.
|
||||
* @param {Ocean} ocean Ocean instance.
|
||||
* @param {string} publicKey Public key to be used on personal sign.
|
||||
* @param {string} password Password if it's required.
|
||||
* @return {Promise<Proof>} Proof object.
|
||||
*/
|
||||
public async addProof(
|
||||
ocean: Ocean,
|
||||
publicKey: string,
|
||||
password?: string
|
||||
): Promise<void> {
|
||||
if (this.proof) {
|
||||
throw new Error('Proof already exists')
|
||||
}
|
||||
this.proof = await this.generateProof(ocean, publicKey, password)
|
||||
}
|
||||
}
|
303
src/ddo/MetaData.ts
Normal file
303
src/ddo/MetaData.ts
Normal file
@ -0,0 +1,303 @@
|
||||
export interface File {
|
||||
/**
|
||||
* File name.
|
||||
* @type {string}
|
||||
*/
|
||||
name?: string
|
||||
|
||||
/**
|
||||
* File URL.
|
||||
* @type {string}
|
||||
*/
|
||||
url: string
|
||||
|
||||
/**
|
||||
* File index.
|
||||
* @type {number}
|
||||
*/
|
||||
index?: number
|
||||
|
||||
/**
|
||||
* File format, if applicable.
|
||||
* @type {string}
|
||||
* @example "text/csv"
|
||||
*/
|
||||
contentType: string
|
||||
|
||||
/**
|
||||
* File checksum.
|
||||
* @type {[type]}
|
||||
*/
|
||||
checksum?: string
|
||||
|
||||
/**
|
||||
* Checksum hash algorithm.
|
||||
* @type {[type]}
|
||||
*/
|
||||
checksumType?: string
|
||||
|
||||
/**
|
||||
* File content length.
|
||||
* @type {[type]}
|
||||
*/
|
||||
contentLength?: string
|
||||
|
||||
/**
|
||||
* Resource ID (depending on the source).
|
||||
* @type {[type]}
|
||||
*/
|
||||
resourceId?: string
|
||||
|
||||
/**
|
||||
* File encoding.
|
||||
* @type {string}
|
||||
* @example "UTF-8"
|
||||
*/
|
||||
encoding?: string
|
||||
|
||||
/**
|
||||
* File compression (e.g. no, gzip, bzip2, etc).
|
||||
* @type {string}
|
||||
* @example "zip"
|
||||
*/
|
||||
compression?: string
|
||||
}
|
||||
|
||||
export interface MetaDataAlgorithm {
|
||||
url?: string
|
||||
rawcode?: string
|
||||
language?: string
|
||||
format?: string
|
||||
version?: string
|
||||
container: {
|
||||
entrypoint: string
|
||||
image: string
|
||||
tag: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main attributes of assets metadata.
|
||||
* @see https://github.com/oceanprotocol/OEPs/tree/master/8
|
||||
*/
|
||||
export interface MetaDataMain {
|
||||
/**
|
||||
* Descriptive name of the Asset.
|
||||
* @type {string}
|
||||
* @example "UK Weather information 2011"
|
||||
*/
|
||||
name: string
|
||||
|
||||
/**
|
||||
* Type of the Asset. Helps to filter by the type of asset ("dataset" or "algorithm").
|
||||
* @type {string}
|
||||
* @example "dataset"
|
||||
*/
|
||||
type: 'dataset' | 'algorithm'
|
||||
|
||||
/**
|
||||
* The date on which the asset was created by the originator in
|
||||
* ISO 8601 format, Coordinated Universal Time.
|
||||
* @type {string}
|
||||
* @example "2019-01-31T08:38:32Z"
|
||||
*/
|
||||
dateCreated: string
|
||||
|
||||
/**
|
||||
* The date on which the asset DDO was registered into the metadata store.
|
||||
* This value is created automatically by Aquarius upon registering,
|
||||
* so this value can't be set.
|
||||
* @type {string}
|
||||
* @example "2019-01-31T08:38:32Z"
|
||||
*/
|
||||
datePublished?: string
|
||||
|
||||
/**
|
||||
* Name of the entity generating this data (e.g. Tfl, Disney Corp, etc.).
|
||||
* @type {string}
|
||||
* @example "Met Office"
|
||||
*/
|
||||
author: string
|
||||
|
||||
/**
|
||||
* Short name referencing the license of the asset (e.g. Public Domain, CC-0, CC-BY, No License Specified, etc. ).
|
||||
* If it's not specified, the following value will be added: "No License Specified".
|
||||
* @type {string}
|
||||
* @example "CC-BY"
|
||||
*/
|
||||
license: string
|
||||
|
||||
/**
|
||||
* Price of the asset in vodka (attoOCEAN). It must be an integer encoded as a string.
|
||||
* @type {string}
|
||||
* @example "1000000000000000000"
|
||||
*/
|
||||
price: string
|
||||
|
||||
/**
|
||||
* Array of File objects including the encrypted file urls and some additional information.
|
||||
* @type {File[]}
|
||||
*/
|
||||
files: File[]
|
||||
|
||||
/**
|
||||
* Metadata used only for assets with type `algorithm`.
|
||||
* @type {MetaDataAlgorithm}
|
||||
*/
|
||||
algorithm?: MetaDataAlgorithm
|
||||
}
|
||||
|
||||
/**
|
||||
* Curation attributes of Assets Metadata.
|
||||
* @see https://github.com/oceanprotocol/OEPs/tree/master/8
|
||||
*/
|
||||
export interface Curation {
|
||||
/**
|
||||
* Decimal value between 0 and 1. 0 is the default value.
|
||||
* @type {number}
|
||||
* @example 0.93
|
||||
*/
|
||||
rating: number
|
||||
|
||||
/**
|
||||
* Number of votes. 0 is the default value.
|
||||
* @type {number}
|
||||
* @example 123
|
||||
*/
|
||||
numVotes: number
|
||||
|
||||
/**
|
||||
* Schema applied to calculate the rating.
|
||||
* @type {string}
|
||||
* @example "Binary Voting"
|
||||
*/
|
||||
schema?: string
|
||||
|
||||
/**
|
||||
* Flag unsuitable content.
|
||||
* @type {boolean}
|
||||
* @example true
|
||||
*/
|
||||
isListed?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional Information of Assets Metadata.
|
||||
* @see https://github.com/oceanprotocol/OEPs/tree/master/8#additional-information
|
||||
*/
|
||||
export interface AdditionalInformation {
|
||||
/**
|
||||
* Details of what the resource is. For a dataset, this attribute
|
||||
* explains what the data represents and what it can be used for.
|
||||
* @type {string}
|
||||
* @example "Weather information of UK including temperature and humidity"
|
||||
*/
|
||||
description?: string
|
||||
|
||||
/**
|
||||
* The party holding the legal copyright. Empty by default.
|
||||
* @type {string}
|
||||
* @example "Met Office"
|
||||
*/
|
||||
copyrightHolder?: string
|
||||
|
||||
/**
|
||||
* Example of the concept of this asset. This example is part
|
||||
* of the metadata, not an external link.
|
||||
* @type {string}
|
||||
* @example "423432fsd,51.509865,-0.118092,2011-01-01T10:55:11+00:00,7.2,68"
|
||||
*/
|
||||
workExample?: string
|
||||
|
||||
/**
|
||||
* Mapping of links for data samples, or links to find out more information.
|
||||
* Links may be to either a URL or another Asset. We expect marketplaces to
|
||||
* converge on agreements of typical formats for linked data: The Ocean Protocol
|
||||
* itself does not mandate any specific formats as these requirements are likely
|
||||
* to be domain-specific.
|
||||
* @type {any[]}
|
||||
* @example
|
||||
* [
|
||||
* {
|
||||
* anotherSample: "http://data.ceda.ac.uk/badc/ukcp09/data/gridded-land-obs/gridded-land-obs-daily/",
|
||||
* },
|
||||
* {
|
||||
* fieldsDescription: "http://data.ceda.ac.uk/badc/ukcp09/",
|
||||
* },
|
||||
* ]
|
||||
*/
|
||||
links?: { [name: string]: string }[]
|
||||
|
||||
/**
|
||||
* The language of the content. Please use one of the language
|
||||
* codes from the {@link https://tools.ietf.org/html/bcp47 IETF BCP 47 standard}.
|
||||
* @type {String}
|
||||
* @example "en"
|
||||
*/
|
||||
inLanguage?: string
|
||||
|
||||
/**
|
||||
* Categories used to describe this content. Empty by default.
|
||||
* @type {string[]}
|
||||
* @example ["Economy", "Data Science"]
|
||||
*/
|
||||
categories?: string[]
|
||||
|
||||
/**
|
||||
* Keywords or tags used to describe this content. Empty by default.
|
||||
* @type {string[]}
|
||||
* @example ["weather", "uk", "2011", "temperature", "humidity"]
|
||||
*/
|
||||
tags?: string[]
|
||||
|
||||
/**
|
||||
* An indication of update latency - i.e. How often are updates expected (seldom,
|
||||
* annually, quarterly, etc.), or is the resource static that is never expected
|
||||
* to get updated.
|
||||
* @type {string}
|
||||
* @example "yearly"
|
||||
*/
|
||||
updateFrequency?: string
|
||||
|
||||
/**
|
||||
* A link to machine-readable structured markup (such as ttl/json-ld/rdf)
|
||||
* describing the dataset.
|
||||
* @type {StructuredMarkup[]}
|
||||
*/
|
||||
structuredMarkup?: {
|
||||
uri: string
|
||||
mediaType: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface MetaData {
|
||||
main: MetaDataMain
|
||||
encryptedFiles?: string
|
||||
additionalInformation?: AdditionalInformation
|
||||
curation?: Curation
|
||||
}
|
||||
/** Warning. serviceIndex is the index of a services in Services array, and not service.index attribute.
|
||||
Let's assume that you have the following services array:
|
||||
[
|
||||
{"index":1,"type":"access","main":{"price":3}},
|
||||
{"index":0,"type":"compute","main":{"price":1}}
|
||||
]
|
||||
then calling update with { serviceIndex:1,price:2} will update the 'compute' service, and not the access one
|
||||
**/
|
||||
export interface ServicePrices {
|
||||
serviceIndex: number
|
||||
price: string
|
||||
}
|
||||
|
||||
export interface EditableMetaDataLinks {
|
||||
name: string
|
||||
url: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface EditableMetaData {
|
||||
description?: string
|
||||
title?: string
|
||||
links?: EditableMetaDataLinks[]
|
||||
servicePrices?: ServicePrices[]
|
||||
}
|
6
src/ddo/Proof.ts
Normal file
6
src/ddo/Proof.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface Proof {
|
||||
type: string
|
||||
created: string
|
||||
creator: string
|
||||
signatureValue: string
|
||||
}
|
32
src/ddo/PublicKey.ts
Normal file
32
src/ddo/PublicKey.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Public key data.
|
||||
*/
|
||||
export interface PublicKey {
|
||||
/**
|
||||
* ID of the key.
|
||||
* @type {string}
|
||||
* @example "did:op:123456789abcdefghi#keys-1"
|
||||
*/
|
||||
id: string
|
||||
|
||||
/**
|
||||
* Type of key.
|
||||
* @type {string}
|
||||
*/
|
||||
type:
|
||||
| 'Ed25519VerificationKey2018'
|
||||
| 'RsaVerificationKey2018'
|
||||
| 'EdDsaSAPublicKeySecp256k1'
|
||||
| 'EthereumECDSAKey'
|
||||
|
||||
/**
|
||||
* Key owner.
|
||||
* @type {string}
|
||||
* @example "did:op:123456789abcdefghi"
|
||||
*/
|
||||
owner: string
|
||||
|
||||
publicKeyPem?: string
|
||||
publicKeyBase58?: string
|
||||
publicKeyHex?: string
|
||||
}
|
95
src/ddo/Service.ts
Normal file
95
src/ddo/Service.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { MetaData } from './MetaData'
|
||||
|
||||
export type ServiceType = 'authorization' | 'metadata' | 'access' | 'compute'
|
||||
|
||||
export interface ServiceCommon {
|
||||
type: ServiceType
|
||||
index: number
|
||||
serviceEndpoint?: string
|
||||
attributes: ServiceCommonAttributes
|
||||
}
|
||||
|
||||
export interface ServiceCommonAttributes {
|
||||
main: { [key: string]: any }
|
||||
additionalInformation?: { [key: string]: any }
|
||||
}
|
||||
|
||||
export interface ServiceAccessAttributes extends ServiceCommonAttributes {
|
||||
main: {
|
||||
creator: string
|
||||
name: string
|
||||
datePublished: string
|
||||
dtCost: number
|
||||
timeout: number
|
||||
}
|
||||
}
|
||||
export interface ServiceComputePrivacy {
|
||||
allowRawAlgorithm: boolean
|
||||
allowNetworkAccess: boolean
|
||||
trustedAlgorithms: string[]
|
||||
}
|
||||
|
||||
export interface ServiceComputeAttributes extends ServiceCommonAttributes {
|
||||
main: {
|
||||
creator: string
|
||||
datePublished: string
|
||||
price: string
|
||||
timeout: number
|
||||
provider?: ServiceComputeProvider
|
||||
name: string
|
||||
privacy?: ServiceComputePrivacy
|
||||
}
|
||||
}
|
||||
|
||||
export interface ServiceComputeProvider {
|
||||
type: string
|
||||
description: string
|
||||
environment: {
|
||||
cluster: {
|
||||
type: string
|
||||
url: string
|
||||
}
|
||||
supportedContainers: {
|
||||
image: string
|
||||
tag: string
|
||||
checksum: string
|
||||
}[]
|
||||
supportedServers: {
|
||||
serverId: string
|
||||
serverType: string
|
||||
price: string
|
||||
cpu: string
|
||||
gpu: string
|
||||
memory: string
|
||||
disk: string
|
||||
maxExecutionTime: number
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface ServiceMetadata extends ServiceCommon {
|
||||
type: 'metadata'
|
||||
attributes: MetaData
|
||||
}
|
||||
|
||||
export interface ServiceAccess extends ServiceCommon {
|
||||
type: 'access'
|
||||
templateId?: string
|
||||
attributes: ServiceAccessAttributes
|
||||
}
|
||||
|
||||
export interface ServiceCompute extends ServiceCommon {
|
||||
type: 'compute'
|
||||
templateId?: string
|
||||
attributes: ServiceComputeAttributes
|
||||
}
|
||||
|
||||
export type Service<T extends ServiceType | 'default' = 'default'> = T extends 'metadata'
|
||||
? ServiceMetadata
|
||||
: T extends 'access'
|
||||
? ServiceAccess
|
||||
: T extends 'compute'
|
||||
? ServiceCompute
|
||||
: T extends 'default'
|
||||
? ServiceCommon
|
||||
: ServiceCommon
|
1
src/index.ts
Normal file
1
src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './lib'
|
23
src/lib.ts
Normal file
23
src/lib.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import Config from './models/Config'
|
||||
import Account from './ocean/Account'
|
||||
import DID from './ocean/DID'
|
||||
import { Ocean } from './ocean/Ocean'
|
||||
import { LoggerInstance as Logger } from './utils/Logger'
|
||||
import { Aquarius } from './aquarius/Aquarius'
|
||||
import { DataTokens } from './datatokens/Datatokens'
|
||||
|
||||
import * as utils from './utils'
|
||||
|
||||
// Exports
|
||||
export * from './ddo/DDO'
|
||||
export * from './ddo/MetaData'
|
||||
|
||||
export { CreateProgressStep, OrderProgressStep } from './ocean/Assets'
|
||||
|
||||
export {
|
||||
OceanPlatformTechStatus,
|
||||
OceanPlatformTech,
|
||||
OceanPlatformVersions
|
||||
} from './ocean/Versions'
|
||||
|
||||
export { Ocean, Account, Config, DID, Logger, Aquarius, DataTokens, utils }
|
65
src/models/Config.ts
Normal file
65
src/models/Config.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { LogLevel } from '../utils/Logger'
|
||||
export { LogLevel } from '../utils/Logger'
|
||||
|
||||
export class Config {
|
||||
/**
|
||||
* Aquarius URL.
|
||||
* @type {string}
|
||||
*/
|
||||
public aquariusUri: string
|
||||
|
||||
/**
|
||||
* Brizo URL.
|
||||
* @type {string}
|
||||
*/
|
||||
public brizoUri: string
|
||||
|
||||
/**
|
||||
* Web3 Provider.
|
||||
* @type {any}
|
||||
*/
|
||||
public web3Provider: any
|
||||
|
||||
/**
|
||||
* Factory address
|
||||
* @type {string}
|
||||
*/
|
||||
public factoryAddress: string
|
||||
|
||||
/**
|
||||
* Factory ABI
|
||||
* @type {string}
|
||||
*/
|
||||
public factoryABI: object
|
||||
|
||||
/**
|
||||
* datatokens ABI
|
||||
* @type {string}
|
||||
*/
|
||||
public datatokensABI: object
|
||||
|
||||
/**
|
||||
* Log level.
|
||||
* @type {boolean | LogLevel}
|
||||
*/
|
||||
public verbose?: boolean | LogLevel
|
||||
|
||||
/**
|
||||
* Message shown when the user creates its own token.
|
||||
* @type {string}
|
||||
*/
|
||||
public authMessage?: string
|
||||
|
||||
/**
|
||||
* Token expiration time in ms.
|
||||
* @type {number}
|
||||
*/
|
||||
public authTokenExpiration?: number
|
||||
|
||||
// Parity config
|
||||
public parityUri?: string
|
||||
|
||||
public threshold?: number
|
||||
}
|
||||
|
||||
export default Config
|
110
src/ocean/Account.ts
Normal file
110
src/ocean/Account.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
|
||||
/**
|
||||
* Account information.
|
||||
*/
|
||||
export default class Accounts extends Instantiable {
|
||||
private password?: string
|
||||
|
||||
private token?: string
|
||||
|
||||
constructor(private id: string = '0x0', config?: InstantiableConfig) {
|
||||
super()
|
||||
if (config) {
|
||||
this.setInstanceConfig(config)
|
||||
}
|
||||
}
|
||||
|
||||
public getId() {
|
||||
return this.id
|
||||
}
|
||||
|
||||
public setId(id) {
|
||||
this.id = id
|
||||
}
|
||||
|
||||
/**
|
||||
* Set account password.
|
||||
* @param {string} password Password for account.
|
||||
*/
|
||||
public setPassword(password: string): void {
|
||||
this.password = password
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns account password.
|
||||
* @return {string} Account password.
|
||||
*/
|
||||
public getPassword(): string {
|
||||
return this.password
|
||||
}
|
||||
|
||||
// TODO - Check with Samer if authentificate is still needed or we can use sign
|
||||
|
||||
/**
|
||||
* Set account token.
|
||||
* @param {string} token Token for account.
|
||||
|
||||
public setToken(token: string): void {
|
||||
this.token = token
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Returns account token.
|
||||
* @return {Promise<string>} Account token.
|
||||
|
||||
public async getToken(): Promise<string> {
|
||||
return this.token || this.ocean.auth.restore(this)
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns if account token is stored.
|
||||
* @return {Promise<boolean>} Is stored.
|
||||
|
||||
public isTokenStored(): Promise<boolean> {
|
||||
return this.ocean.auth.isStored(this)
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Authenticate the account.
|
||||
|
||||
public authenticate() {
|
||||
return this.ocean.auth.store(this)
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Balance of Any Token.
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
public async getTokenBalance(TokenAdress: string): Promise<number> {
|
||||
// TO DO
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Symbol of a Token
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
public async getTokenSymbol(TokenAdress: string): Promise<string> {
|
||||
// TO DO
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance of Ether.
|
||||
* @return {Promise<number>}
|
||||
*/
|
||||
public async getEtherBalance(): Promise<number> {
|
||||
// TO DO
|
||||
/* return this.web3.eth
|
||||
.getBalance(this.id, 'latest')
|
||||
.then((balance: string): number => {
|
||||
return new BigNumber(balance).toNumber()
|
||||
})
|
||||
*/
|
||||
return 0
|
||||
}
|
||||
}
|
51
src/ocean/Accounts.ts
Normal file
51
src/ocean/Accounts.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import Account from './Account'
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
|
||||
/**
|
||||
* Account submodule of Ocean Protocol.
|
||||
*/
|
||||
export class Accounts extends Instantiable {
|
||||
/**
|
||||
* Returns the instance of OceanAccounts.
|
||||
* @return {Promise<OceanAccounts>}
|
||||
*/
|
||||
public static async getInstance(config: InstantiableConfig): Promise<Accounts> {
|
||||
const instance = new Accounts()
|
||||
instance.setInstanceConfig(config)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of accounts.
|
||||
* @return {Promise<Account[]>}
|
||||
*/
|
||||
public async list(): Promise<Account[]> {
|
||||
// retrieve eth accounts
|
||||
const ethAccounts: string[] = await this.web3.eth.getAccounts()
|
||||
|
||||
const accountPromises = ethAccounts.map(
|
||||
address => new Account(address, this.instanceConfig)
|
||||
)
|
||||
return Promise.all(accountPromises)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return account balance.
|
||||
* @param {String} TokenAddress .
|
||||
* @param {Account} account Account instance.
|
||||
* @return {Promise<Balance>} Ether and Ocean Token balance.
|
||||
*/
|
||||
public balance(TokenAddress: string, account: Account): Promise<number> {
|
||||
return account.getTokenBalance(TokenAddress)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return account balance in ETH
|
||||
* @param {Account} account Account instance.
|
||||
* @return {Promise<Balance>} Ether and Ocean Token balance.
|
||||
*/
|
||||
public ETHbalance(account: Account): Promise<number> {
|
||||
return account.getEtherBalance()
|
||||
}
|
||||
}
|
172
src/ocean/Assets.ts
Normal file
172
src/ocean/Assets.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import { TransactionReceipt } from 'web3-core'
|
||||
import { SearchQuery } from '../aquarius/Aquarius'
|
||||
import { DDO } from '../ddo/DDO'
|
||||
import { MetaData, EditableMetaData } from '../ddo/MetaData'
|
||||
import { Service, ServiceAccess, ServiceComputePrivacy } from '../ddo/Service'
|
||||
import Account from './Account'
|
||||
import DID from './DID'
|
||||
import { SubscribablePromise, didZeroX } from '../utils'
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
|
||||
import { DataTokens } from '../lib'
|
||||
|
||||
export enum CreateProgressStep {
|
||||
CreatingDataToken,
|
||||
DataTokenCreated,
|
||||
EncryptingFiles,
|
||||
FilesEncrypted,
|
||||
GeneratingProof,
|
||||
ProofGenerated,
|
||||
StoringDdo,
|
||||
DdoStored
|
||||
}
|
||||
|
||||
export enum OrderProgressStep {
|
||||
CreatingAgreement,
|
||||
AgreementInitialized,
|
||||
LockingPayment,
|
||||
LockedPayment
|
||||
}
|
||||
|
||||
/**
|
||||
* Assets submodule of Ocean Protocol.
|
||||
*/
|
||||
export class Assets extends Instantiable {
|
||||
/**
|
||||
* Returns the instance of Assets.
|
||||
* @return {Promise<Assets>}
|
||||
*/
|
||||
public static async getInstance(config: InstantiableConfig): Promise<Assets> {
|
||||
const instance = new Assets()
|
||||
instance.setInstanceConfig(config)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple asset and a datatoken
|
||||
* @param {Account} publisher Publisher account.
|
||||
* @return {Promise<String>}
|
||||
*/
|
||||
public createSimpleAsset(publisher: Account): Promise<string> {
|
||||
const publisherURI = this.ocean.brizo.getURI()
|
||||
const jsonBlob = { t: 0, url: publisherURI }
|
||||
const { datatokens } = this.ocean
|
||||
return datatokens.create(JSON.stringify(jsonBlob), publisher)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DDO and publishes it
|
||||
* @param {MetaData} metadata DDO metadata.
|
||||
* @param {Account} publisher Publisher account.
|
||||
* @param {list} services list of Service description documents
|
||||
* @return {Promise<DDO>}
|
||||
*/
|
||||
public create(
|
||||
metadata: MetaData,
|
||||
publisher: Account,
|
||||
services: Service[] = [],
|
||||
dtAddress?: string
|
||||
): SubscribablePromise<CreateProgressStep, DDO> {
|
||||
this.logger.log('Creating asset')
|
||||
return new SubscribablePromise(async observer => {
|
||||
if (services.length === 0) {
|
||||
this.logger.log('You have no services. Are you sure about this?')
|
||||
}
|
||||
if (!dtAddress) {
|
||||
this.logger.log('Creating datatoken')
|
||||
observer.next(CreateProgressStep.CreatingDataToken)
|
||||
const metadataStoreURI = this.ocean.aquarius.getURI()
|
||||
const jsonBlob = { t: 1, url: metadataStoreURI }
|
||||
const { datatokens } = this.ocean
|
||||
dtAddress = await datatokens.create(JSON.stringify(jsonBlob), publisher)
|
||||
this.logger.log('DataToken creted')
|
||||
observer.next(CreateProgressStep.DataTokenCreated)
|
||||
}
|
||||
|
||||
const did: DID = DID.generate()
|
||||
|
||||
this.logger.log('Encrypting files')
|
||||
observer.next(CreateProgressStep.EncryptingFiles)
|
||||
const encryptedFiles = await this.ocean.brizo.encrypt(
|
||||
did.getId(),
|
||||
metadata.main.files,
|
||||
publisher,
|
||||
dtAddress
|
||||
)
|
||||
this.logger.log('Files encrypted')
|
||||
observer.next(CreateProgressStep.FilesEncrypted)
|
||||
|
||||
let indexCount = 0
|
||||
// create ddo itself
|
||||
const ddo: DDO = new DDO({
|
||||
id: did.getDid(),
|
||||
dtAddress: dtAddress,
|
||||
authentication: [
|
||||
{
|
||||
type: 'RsaSignatureAuthentication2018',
|
||||
publicKey: did.getDid()
|
||||
}
|
||||
],
|
||||
publicKey: [
|
||||
{
|
||||
id: did.getDid(),
|
||||
type: 'EthereumECDSAKey',
|
||||
owner: publisher.getId()
|
||||
}
|
||||
],
|
||||
service: [
|
||||
{
|
||||
type: 'metadata',
|
||||
attributes: {
|
||||
// Default values
|
||||
curation: {
|
||||
rating: 0,
|
||||
numVotes: 0
|
||||
},
|
||||
// Overwrites defaults
|
||||
...metadata,
|
||||
encryptedFiles,
|
||||
// Cleaning not needed information
|
||||
main: {
|
||||
...metadata.main,
|
||||
files: metadata.main.files.map((file, index) => ({
|
||||
...file,
|
||||
index,
|
||||
url: undefined
|
||||
}))
|
||||
} as any
|
||||
}
|
||||
},
|
||||
...services
|
||||
]
|
||||
// Remove duplications
|
||||
.reverse()
|
||||
.filter(
|
||||
({ type }, i, list) =>
|
||||
list.findIndex(({ type: t }) => t === type) === i
|
||||
)
|
||||
.reverse()
|
||||
// Adding index
|
||||
.map(_ => ({
|
||||
..._,
|
||||
index: indexCount++
|
||||
})) as Service[]
|
||||
})
|
||||
|
||||
this.logger.log('Generating proof')
|
||||
observer.next(CreateProgressStep.GeneratingProof)
|
||||
await ddo.addProof(this.ocean, publisher.getId(), publisher.getPassword())
|
||||
this.logger.log('Proof generated')
|
||||
observer.next(CreateProgressStep.ProofGenerated)
|
||||
|
||||
this.logger.log('Storing DDO')
|
||||
observer.next(CreateProgressStep.StoringDdo)
|
||||
const storedDdo = await this.ocean.aquarius.storeDDO(ddo)
|
||||
this.logger.log('DDO stored')
|
||||
observer.next(CreateProgressStep.DdoStored)
|
||||
|
||||
return storedDdo
|
||||
})
|
||||
}
|
||||
}
|
65
src/ocean/DID.ts
Normal file
65
src/ocean/DID.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { generateId } from '../utils/GeneratorHelpers'
|
||||
|
||||
const prefix = 'did:op:'
|
||||
|
||||
/**
|
||||
* Decentralized ID.
|
||||
*/
|
||||
export default class DID {
|
||||
/**
|
||||
* Parses a DID from a string.
|
||||
* @param {string} didString DID in string.
|
||||
* @return {DID}
|
||||
*/
|
||||
public static parse(didString: string | DID): DID {
|
||||
if (didString instanceof DID) {
|
||||
didString = didString.getDid()
|
||||
}
|
||||
let did: DID
|
||||
const didMatch = didString.match(/^did:op:([a-f0-9]{64})$/i)
|
||||
|
||||
if (didMatch) {
|
||||
did = new DID(didMatch[1])
|
||||
}
|
||||
|
||||
if (!did) {
|
||||
throw new Error(`Parsing DID failed, ${didString}`)
|
||||
}
|
||||
|
||||
return did
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new DID.
|
||||
* @return {DID}
|
||||
*/
|
||||
public static generate(): DID {
|
||||
return new DID(generateId())
|
||||
}
|
||||
|
||||
/**
|
||||
* ID.
|
||||
* @type {string}
|
||||
*/
|
||||
private id: string
|
||||
|
||||
private constructor(id: string) {
|
||||
this.id = id
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DID.
|
||||
* @return {string}
|
||||
*/
|
||||
public getDid(): string {
|
||||
return `${prefix}${this.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID.
|
||||
* @return {string}
|
||||
*/
|
||||
public getId(): string {
|
||||
return this.id
|
||||
}
|
||||
}
|
133
src/ocean/Ocean.ts
Normal file
133
src/ocean/Ocean.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { Accounts } from './Accounts'
|
||||
|
||||
import { Assets } from './Assets'
|
||||
|
||||
// import { Compute } from './Compute'
|
||||
|
||||
import { Versions } from './Versions'
|
||||
import { OceanUtils } from './utils/Utils'
|
||||
|
||||
import { Aquarius } from '../aquarius/Aquarius'
|
||||
import { Brizo } from '../brizo/Brizo'
|
||||
import { DataTokens } from '../datatokens/Datatokens'
|
||||
import { Config } from '../models/Config'
|
||||
|
||||
import {
|
||||
Instantiable,
|
||||
generateIntantiableConfigFromConfig
|
||||
} from '../Instantiable.abstract'
|
||||
|
||||
/**
|
||||
* Main interface for Ocean Protocol.
|
||||
*/
|
||||
export class Ocean extends Instantiable {
|
||||
/**
|
||||
* Returns the instance of Ocean.
|
||||
* @param {Config} config Ocean instance configuration.
|
||||
* @return {Promise<Ocean>}
|
||||
*/
|
||||
public static async getInstance(config: Config): Promise<Ocean> {
|
||||
const instance = new Ocean()
|
||||
|
||||
const instanceConfig = {
|
||||
...generateIntantiableConfigFromConfig(config),
|
||||
ocean: instance
|
||||
}
|
||||
instance.setInstanceConfig(instanceConfig)
|
||||
|
||||
instance.utils = await OceanUtils.getInstance(instanceConfig)
|
||||
|
||||
instance.brizo = new Brizo(instanceConfig)
|
||||
instance.aquarius = new Aquarius(
|
||||
instanceConfig.config.aquariusUri,
|
||||
instanceConfig.logger
|
||||
)
|
||||
|
||||
instance.accounts = await Accounts.getInstance(instanceConfig)
|
||||
// instance.auth = await Auth.getInstance(instanceConfig)
|
||||
instance.assets = await Assets.getInstance(instanceConfig)
|
||||
// instance.compute = await Compute.getInstance(instanceConfig)
|
||||
instance.datatokens = new DataTokens(
|
||||
instanceConfig.config.factoryAddress,
|
||||
instanceConfig.config.factoryABI,
|
||||
instanceConfig.config.datatokensABI,
|
||||
instanceConfig.config.web3Provider
|
||||
)
|
||||
instance.versions = await Versions.getInstance(instanceConfig)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Brizo instance.
|
||||
* @type {Brizo}
|
||||
*/
|
||||
public brizo: Brizo
|
||||
|
||||
/**
|
||||
* Web3 provider.
|
||||
* @type {any}
|
||||
*/
|
||||
public web3Provider: any
|
||||
|
||||
/**
|
||||
* Aquarius instance.
|
||||
* @type {Aquarius}
|
||||
*/
|
||||
public aquarius: Aquarius
|
||||
|
||||
/**
|
||||
* Ocean account submodule
|
||||
* @type {Accounts}
|
||||
*/
|
||||
public accounts: Accounts
|
||||
|
||||
/**
|
||||
* Ocean auth submodule
|
||||
* @type {OceanAuth}
|
||||
|
||||
public auth: OceanAuth
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ocean assets submodule
|
||||
* @type {Assets}
|
||||
*/
|
||||
public assets: Assets
|
||||
|
||||
/**
|
||||
* Ocean compute submodule
|
||||
* @type {Compute}
|
||||
|
||||
public compute: Compute
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ocean secretStore submodule
|
||||
* @type {OceanSecretStore}
|
||||
*/
|
||||
public datatokens: DataTokens
|
||||
|
||||
/**
|
||||
* Ocean tokens submodule
|
||||
* @type {OceanTokens}
|
||||
|
||||
public tokens: OceanTokens
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ocean versions submodule
|
||||
* @type {Versions}
|
||||
*/
|
||||
public versions: Versions
|
||||
|
||||
/**
|
||||
* Ocean utils submodule
|
||||
* @type {OceanUtils}
|
||||
*/
|
||||
public utils: OceanUtils
|
||||
|
||||
private constructor() {
|
||||
super()
|
||||
}
|
||||
}
|
144
src/ocean/OceanAuth.ts
Normal file
144
src/ocean/OceanAuth.ts
Normal file
@ -0,0 +1,144 @@
|
||||
import Account from './Account'
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
|
||||
const defaultAuthMessage = 'Ocean Protocol Authentication'
|
||||
const defaultExpirationTime = 30 * 24 * 60 * 60 * 1000 // 30 days
|
||||
const localStorageKey = 'SquidTokens'
|
||||
|
||||
/**
|
||||
* Tokens submodule of Ocean Protocol.
|
||||
*/
|
||||
export class OceanAuth extends Instantiable {
|
||||
/**
|
||||
* Returns the instance of OceanAuth.
|
||||
* @return {Promise<OceanAuth>}
|
||||
*/
|
||||
public static async getInstance(config: InstantiableConfig): Promise<OceanAuth> {
|
||||
const instance = new OceanAuth()
|
||||
instance.setInstanceConfig(config)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a token for a account.
|
||||
* @param {Account} account Signer account.
|
||||
* @return {Promise<string>} Token
|
||||
*/
|
||||
public async get(account: Account): Promise<string> {
|
||||
const time = Math.floor(Date.now() / 1000)
|
||||
const message = `${this.getMessage()}\n${time}`
|
||||
|
||||
try {
|
||||
const signature = await this.ocean.utils.signature.signText(
|
||||
message,
|
||||
account.getId(),
|
||||
account.getPassword()
|
||||
)
|
||||
|
||||
return `${signature}-${time}`
|
||||
} catch {
|
||||
throw new Error('User denied the signature.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of signed token.
|
||||
* @param {string} token Token.
|
||||
* @return {Promise<string>} Signer address.
|
||||
*/
|
||||
public async check(token: string): Promise<string> {
|
||||
const expiration = this.getExpiration()
|
||||
const [signature, timestamp] = token.split('-')
|
||||
|
||||
const message = `${this.getMessage()}\n${timestamp}`
|
||||
|
||||
if (+timestamp * 1000 + expiration < Date.now()) {
|
||||
return `0x${'0'.repeat(40)}`
|
||||
}
|
||||
|
||||
return this.web3.utils.toChecksumAddress(
|
||||
await this.ocean.utils.signature.verifyText(message, signature)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and stores the token for a account.
|
||||
* @param {Account} account Signer account.
|
||||
*/
|
||||
public async store(account: Account) {
|
||||
const token = await this.get(account)
|
||||
this.writeToken(account.getId(), token)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stored token.
|
||||
* @param {Account} account Signer account.
|
||||
*/
|
||||
public async restore(account: Account): Promise<string> {
|
||||
let token
|
||||
try {
|
||||
token = this.readToken(account.getId())
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
const signer = await this.check(token)
|
||||
if (signer.toLowerCase() !== account.getId().toLowerCase()) {
|
||||
return
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the token is stored and is valid.
|
||||
* @param {Account} account Signer account.
|
||||
* @return {Promise<boolean>} Is stored and valid.
|
||||
*/
|
||||
public async isStored(account: Account): Promise<boolean> {
|
||||
return !!(await this.restore(account))
|
||||
}
|
||||
|
||||
private writeToken(address: string, token: string) {
|
||||
const localStorage = this.getLocalStorage()
|
||||
const storedTokens = localStorage.getItem(localStorageKey)
|
||||
const tokens = storedTokens ? JSON.parse(storedTokens) : {}
|
||||
|
||||
localStorage.setItem(
|
||||
localStorageKey,
|
||||
JSON.stringify({
|
||||
...tokens,
|
||||
[address]: token
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
private readToken(address: string) {
|
||||
const localStorage = this.getLocalStorage()
|
||||
const storedTokens = localStorage.getItem(localStorageKey)
|
||||
const tokens = storedTokens ? JSON.parse(storedTokens) : {}
|
||||
|
||||
return tokens[address]
|
||||
}
|
||||
|
||||
private getLocalStorage() {
|
||||
try {
|
||||
localStorage.getItem('')
|
||||
} catch {
|
||||
throw new Error(
|
||||
'LocalStorage is not supported. This feature is only available on browsers.'
|
||||
)
|
||||
}
|
||||
return localStorage
|
||||
}
|
||||
|
||||
private getMessage() {
|
||||
return this.config.authMessage || defaultAuthMessage
|
||||
}
|
||||
|
||||
private getExpiration() {
|
||||
return this.config.authTokenExpiration || defaultExpirationTime
|
||||
}
|
||||
}
|
76
src/ocean/Versions.ts
Normal file
76
src/ocean/Versions.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import * as metadata from '../metadata.json'
|
||||
|
||||
import { Instantiable, InstantiableConfig } from '../Instantiable.abstract'
|
||||
|
||||
export enum OceanPlatformTechStatus {
|
||||
Loading = 'Loading',
|
||||
Unknown = 'Unknown',
|
||||
Stopped = 'Stopped',
|
||||
Working = 'Working'
|
||||
}
|
||||
|
||||
export interface OceanPlatformTech {
|
||||
name: string
|
||||
version?: string
|
||||
commit?: string
|
||||
status: OceanPlatformTechStatus
|
||||
}
|
||||
|
||||
export interface OceanPlatformVersions {
|
||||
lib: OceanPlatformTech
|
||||
metadataStore: OceanPlatformTech
|
||||
provider: OceanPlatformTech
|
||||
status: {
|
||||
ok: boolean
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versions submodule of Ocean Protocol.
|
||||
*/
|
||||
export class Versions extends Instantiable {
|
||||
/**
|
||||
* Returns the instance of Ocean Stack Versions.
|
||||
* @return {Promise<Versions>}
|
||||
*/
|
||||
public static async getInstance(config: InstantiableConfig): Promise<Versions> {
|
||||
const instance = new Versions()
|
||||
instance.setInstanceConfig(config)
|
||||
return instance
|
||||
}
|
||||
|
||||
public async get(): Promise<OceanPlatformVersions> {
|
||||
const versions = {} as OceanPlatformVersions
|
||||
|
||||
versions.lib = {
|
||||
name: 'Lib',
|
||||
version: metadata.version,
|
||||
commit: metadata.commit,
|
||||
status: OceanPlatformTechStatus.Working
|
||||
}
|
||||
|
||||
// MetadataStore
|
||||
try {
|
||||
const { software: name, version } = await this.ocean.aquarius.getVersionInfo()
|
||||
versions.metadataStore = {
|
||||
name,
|
||||
status: OceanPlatformTechStatus.Working,
|
||||
version
|
||||
}
|
||||
} catch {
|
||||
versions.metadataStore = {
|
||||
name: 'MetadataStore',
|
||||
status: OceanPlatformTechStatus.Stopped
|
||||
}
|
||||
}
|
||||
|
||||
// Status
|
||||
const techs: OceanPlatformTech[] = Object.values(versions as any)
|
||||
|
||||
versions.status = {
|
||||
ok: !techs.find(({ status }) => status !== OceanPlatformTechStatus.Working)
|
||||
}
|
||||
|
||||
return versions
|
||||
}
|
||||
}
|
103
src/ocean/utils/SignatureUtils.ts
Normal file
103
src/ocean/utils/SignatureUtils.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import Web3 from 'web3'
|
||||
import { Logger } from '../../utils'
|
||||
import { Account } from '../../lib'
|
||||
|
||||
export class SignatureUtils {
|
||||
private web3: Web3
|
||||
private logger: Logger
|
||||
|
||||
constructor(web3: Web3, logger: Logger) {
|
||||
this.web3 = web3
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
public async signText(
|
||||
text: string,
|
||||
publicKey: string,
|
||||
password?: string
|
||||
): Promise<string> {
|
||||
const isMetaMask =
|
||||
this.web3 &&
|
||||
this.web3.currentProvider &&
|
||||
(this.web3.currentProvider as any).isMetaMask
|
||||
try {
|
||||
return await this.web3.eth.personal.sign(text, publicKey, password)
|
||||
} catch (e) {
|
||||
if (isMetaMask) {
|
||||
throw e
|
||||
}
|
||||
this.logger.warn('Error on personal sign.')
|
||||
this.logger.warn(e)
|
||||
try {
|
||||
return await this.web3.eth.sign(text, publicKey)
|
||||
} catch (e2) {
|
||||
this.logger.error('Error on sign.')
|
||||
this.logger.error(e2)
|
||||
throw new Error('Error executing personal sign')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async signWithHash(
|
||||
text: string,
|
||||
publicKey: string,
|
||||
password?: string
|
||||
): Promise<string> {
|
||||
const hash = this.web3.utils.utf8ToHex(text)
|
||||
const isMetaMask =
|
||||
this.web3 &&
|
||||
this.web3.currentProvider &&
|
||||
(this.web3.currentProvider as any).isMetaMask
|
||||
try {
|
||||
return await this.web3.eth.personal.sign(hash, publicKey, password)
|
||||
} catch (e) {
|
||||
if (isMetaMask) {
|
||||
throw e
|
||||
}
|
||||
this.logger.warn('Error on personal sign.')
|
||||
this.logger.warn(e)
|
||||
try {
|
||||
return await this.web3.eth.sign(hash, publicKey)
|
||||
} catch (e2) {
|
||||
this.logger.error('Error on sign.')
|
||||
this.logger.error(e2)
|
||||
throw new Error('Error executing personal sign')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async verifyText(text: string, signature: string): Promise<string> {
|
||||
return this.web3.eth.personal.ecRecover(text, signature)
|
||||
}
|
||||
|
||||
public async getHash(message: string): Promise<string> {
|
||||
let hex = ''
|
||||
for (let i = 0; i < message.length; i++) {
|
||||
hex += '' + message.charCodeAt(i).toString(16)
|
||||
}
|
||||
const hexMessage = '0x' + hex
|
||||
return hexMessage as string
|
||||
}
|
||||
|
||||
public async signForAquarius(message: string, account: Account): Promise<string> {
|
||||
const hash = await this.getHash(message)
|
||||
const isMetaMask =
|
||||
this.web3 &&
|
||||
this.web3.currentProvider &&
|
||||
(this.web3.currentProvider as any).isMetaMask
|
||||
try {
|
||||
return this.web3.eth.personal.sign(
|
||||
hash,
|
||||
account.getId(),
|
||||
account.getPassword()
|
||||
)
|
||||
} catch (e) {
|
||||
if (isMetaMask) {
|
||||
throw e
|
||||
}
|
||||
this.logger.warn('Error on personal sign.')
|
||||
this.logger.warn(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
34
src/ocean/utils/Utils.ts
Normal file
34
src/ocean/utils/Utils.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Instantiable, InstantiableConfig } from '../../Instantiable.abstract'
|
||||
|
||||
import { SignatureUtils } from './SignatureUtils'
|
||||
import { WebServiceConnector } from './WebServiceConnector'
|
||||
/**
|
||||
* Utils internal submodule of Ocean Protocol.
|
||||
*/
|
||||
export class OceanUtils extends Instantiable {
|
||||
/**
|
||||
* Returns the instance of OceanUtils.
|
||||
* @return {Promise<OceanUtils>}
|
||||
*/
|
||||
public static async getInstance(config: InstantiableConfig): Promise<OceanUtils> {
|
||||
const instance = new OceanUtils()
|
||||
instance.setInstanceConfig(config)
|
||||
|
||||
instance.signature = new SignatureUtils(config.web3, config.logger)
|
||||
instance.fetch = new WebServiceConnector(config.logger)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Signature utils.
|
||||
* @type {SignatureUtils}
|
||||
*/
|
||||
public signature: SignatureUtils
|
||||
|
||||
/**
|
||||
* Fetch utils.
|
||||
* @type {WebServiceConnector}
|
||||
*/
|
||||
public fetch: WebServiceConnector
|
||||
}
|
112
src/ocean/utils/WebServiceConnector.ts
Normal file
112
src/ocean/utils/WebServiceConnector.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { BodyInit, RequestInit, Response } from 'node-fetch'
|
||||
import fs from 'fs'
|
||||
import { Logger } from '../../utils'
|
||||
|
||||
const fetch = require('node-fetch')
|
||||
import save = require('save-file')
|
||||
|
||||
/**
|
||||
* Provides a common interface to web services.
|
||||
*/
|
||||
export class WebServiceConnector {
|
||||
public logger: Logger
|
||||
constructor(logger: Logger) {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
public post(url: string, payload: BodyInit): Promise<Response> {
|
||||
return this.fetch(url, {
|
||||
method: 'POST',
|
||||
body: payload,
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public get(url: string): Promise<Response> {
|
||||
return this.fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public put(url: string, payload: BodyInit): Promise<Response> {
|
||||
return this.fetch(url, {
|
||||
method: 'PUT',
|
||||
body: payload,
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public delete(url: string, payload?: BodyInit): Promise<Response> {
|
||||
if (payload != null) {
|
||||
return this.fetch(url, {
|
||||
method: 'DELETE',
|
||||
body: payload,
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return this.fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public async downloadFile(
|
||||
url: string,
|
||||
destination?: string,
|
||||
index?: number
|
||||
): Promise<string> {
|
||||
const response = await this.get(url)
|
||||
if (!response.ok) {
|
||||
throw new Error('Response error.')
|
||||
}
|
||||
let filename: string
|
||||
try {
|
||||
filename = response.headers
|
||||
.get('content-disposition')
|
||||
.match(/attachment;filename=(.+)/)[1]
|
||||
} catch {
|
||||
try {
|
||||
filename = url.split('/').pop()
|
||||
} catch {
|
||||
filename = `file${index}`
|
||||
}
|
||||
}
|
||||
|
||||
if (destination) {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
await new Promise(async (resolve, reject) => {
|
||||
fs.mkdirSync(destination, { recursive: true })
|
||||
const fileStream = fs.createWriteStream(`${destination}${filename}`)
|
||||
response.body.pipe(fileStream)
|
||||
response.body.on('error', reject)
|
||||
fileStream.on('finish', resolve)
|
||||
})
|
||||
|
||||
return destination
|
||||
} else {
|
||||
save(await response.arrayBuffer(), filename)
|
||||
}
|
||||
}
|
||||
|
||||
private async fetch(url: string, opts: RequestInit): Promise<Response> {
|
||||
const result = await fetch(url, opts)
|
||||
if (!result.ok) {
|
||||
this.logger.error(`Error requesting [${opts.method}] ${url}`)
|
||||
this.logger.error(`Response message: \n${await result.text()}`)
|
||||
throw result
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
47
src/utils/ConversionTypeHelpers.ts
Normal file
47
src/utils/ConversionTypeHelpers.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { LoggerInstance } from './Logger'
|
||||
|
||||
// Ox transformer
|
||||
export const zeroX = (input: string) => zeroXTransformer(input, true)
|
||||
export const noZeroX = (input: string) => zeroXTransformer(input, false)
|
||||
export function zeroXTransformer(input: string = '', zeroOutput: boolean) {
|
||||
const { valid, output } = inputMatch(
|
||||
input,
|
||||
/^(?:0x)*([a-f0-9]+)$/i,
|
||||
'zeroXTransformer'
|
||||
)
|
||||
return (zeroOutput && valid ? '0x' : '') + output
|
||||
}
|
||||
|
||||
// did:op: transformer
|
||||
export const didPrefixed = (input: string) => didTransformer(input, true)
|
||||
export const noDidPrefixed = (input: string) => didTransformer(input, false)
|
||||
export function didTransformer(input: string = '', prefixOutput: boolean) {
|
||||
const { valid, output } = inputMatch(
|
||||
input,
|
||||
/^(?:0x|did:op:)*([a-f0-9]{64})$/i,
|
||||
'didTransformer'
|
||||
)
|
||||
return (prefixOutput && valid ? 'did:op:' : '') + output
|
||||
}
|
||||
|
||||
// 0x + did:op: transformer
|
||||
export const didZeroX = (input: string) => zeroX(didTransformer(input, false))
|
||||
|
||||
// Shared functions
|
||||
function inputMatch(
|
||||
input: string,
|
||||
regexp: RegExp,
|
||||
conversorName: string
|
||||
): { valid: boolean; output: string } {
|
||||
if (typeof input !== 'string') {
|
||||
LoggerInstance.debug('Not input string:')
|
||||
LoggerInstance.debug(input)
|
||||
throw new Error(`[${conversorName}] Expected string, input type: ${typeof input}`)
|
||||
}
|
||||
const match = input.match(regexp)
|
||||
if (!match) {
|
||||
LoggerInstance.warn(`[${conversorName}] Input transformation failed.`)
|
||||
return { valid: false, output: input }
|
||||
}
|
||||
return { valid: true, output: match[1] }
|
||||
}
|
9
src/utils/GeneratorHelpers.ts
Normal file
9
src/utils/GeneratorHelpers.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
export function generateId(length = 64) {
|
||||
let id = ''
|
||||
while (id.length < length) {
|
||||
id += v4().replace(/-/g, '')
|
||||
}
|
||||
return id.substr(0, length)
|
||||
}
|
44
src/utils/Logger.ts
Normal file
44
src/utils/Logger.ts
Normal file
@ -0,0 +1,44 @@
|
||||
export enum LogLevel {
|
||||
None = -1,
|
||||
Error = 0,
|
||||
Warn = 1,
|
||||
Log = 2,
|
||||
Verbose = 3
|
||||
}
|
||||
|
||||
export class Logger {
|
||||
constructor(private logLevel: LogLevel = LogLevel.Verbose) {}
|
||||
|
||||
public setLevel(logLevel: LogLevel) {
|
||||
this.logLevel = logLevel
|
||||
}
|
||||
|
||||
public bypass(...args: any[]) {
|
||||
this.dispatch('log', -Infinity as any, ...args)
|
||||
}
|
||||
|
||||
public debug(...args: any[]) {
|
||||
this.dispatch('debug', LogLevel.Verbose, ...args)
|
||||
}
|
||||
|
||||
public log(...args: any[]) {
|
||||
this.dispatch('log', LogLevel.Log, ...args)
|
||||
}
|
||||
|
||||
public warn(...args: any[]) {
|
||||
this.dispatch('warn', LogLevel.Warn, ...args)
|
||||
}
|
||||
|
||||
public error(...args: any[]) {
|
||||
this.dispatch('error', LogLevel.Error, ...args)
|
||||
}
|
||||
|
||||
private dispatch(verb: string, level: LogLevel, ...args: any[]) {
|
||||
if (this.logLevel >= level) {
|
||||
console[verb](...args) // eslint-disable-line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const LoggerInstance = new Logger()
|
||||
export default LoggerInstance
|
15
src/utils/PromiseResolver.ts
Normal file
15
src/utils/PromiseResolver.ts
Normal file
@ -0,0 +1,15 @@
|
||||
const zipObject = (keys = [], values = []) => {
|
||||
return keys.reduce(
|
||||
(acc, key, index) => ({
|
||||
...acc,
|
||||
[key]: values[index]
|
||||
}),
|
||||
{}
|
||||
)
|
||||
}
|
||||
|
||||
export const objectPromiseAll = async (obj: { [key: string]: Promise<any> }) => {
|
||||
const keys = Object.keys(obj)
|
||||
const result = await Promise.all(Object.values(obj))
|
||||
return zipObject(keys, result)
|
||||
}
|
51
src/utils/SubscribableObserver.ts
Normal file
51
src/utils/SubscribableObserver.ts
Normal file
@ -0,0 +1,51 @@
|
||||
export class SubscribableObserver<T, P> {
|
||||
public completed: boolean = false
|
||||
|
||||
private subscriptions = new Set<{
|
||||
onNext?: (next: T) => void
|
||||
onComplete?: (complete: P) => void
|
||||
onError?: (error: any) => void
|
||||
}>()
|
||||
|
||||
public subscribe(
|
||||
onNext?: (next: T) => void,
|
||||
onComplete?: (complete: P) => void,
|
||||
onError?: (error: any) => void
|
||||
) {
|
||||
if (this.completed) {
|
||||
throw new Error('Observer completed.')
|
||||
}
|
||||
const subscription = { onNext, onComplete, onError }
|
||||
this.subscriptions.add(subscription)
|
||||
|
||||
return {
|
||||
unsubscribe: () => this.subscriptions.delete(subscription)
|
||||
}
|
||||
}
|
||||
|
||||
public next(next?: T): void {
|
||||
this.emit('onNext', next)
|
||||
}
|
||||
|
||||
public complete(resolve?: P): void {
|
||||
this.emit('onComplete', resolve)
|
||||
this.unsubscribe()
|
||||
}
|
||||
|
||||
public error(error?: any): void {
|
||||
this.emit('onError', error)
|
||||
this.unsubscribe()
|
||||
}
|
||||
|
||||
private emit(type: 'onNext' | 'onComplete' | 'onError', value: any) {
|
||||
Array.from(this.subscriptions)
|
||||
.map(subscription => subscription[type])
|
||||
.filter((callback: any) => callback && typeof callback === 'function')
|
||||
.forEach((callback: any) => callback(value))
|
||||
}
|
||||
|
||||
private unsubscribe() {
|
||||
this.completed = true
|
||||
this.subscriptions.clear()
|
||||
}
|
||||
}
|
56
src/utils/SubscribablePromise.ts
Normal file
56
src/utils/SubscribablePromise.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { SubscribableObserver } from './SubscribableObserver'
|
||||
|
||||
export class SubscribablePromise<T extends any, P extends any> {
|
||||
private observer = new SubscribableObserver<T, P>()
|
||||
|
||||
private promise = Object.assign(
|
||||
new Promise<P>((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
this.observer.subscribe(undefined, resolve, reject)
|
||||
}, 0)
|
||||
}),
|
||||
this
|
||||
)
|
||||
|
||||
constructor(executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>) {
|
||||
// Defear
|
||||
setTimeout(() => this.init(executor), 1)
|
||||
}
|
||||
|
||||
public subscribe(onNext: (next: T) => void) {
|
||||
return this.observer.subscribe(onNext)
|
||||
}
|
||||
|
||||
public next(onNext: (next: T) => void) {
|
||||
this.observer.subscribe(onNext)
|
||||
return this
|
||||
}
|
||||
|
||||
public then(onfulfilled?: (value: P) => any, onrejected?: (error: any) => any) {
|
||||
return Object.assign(this.promise.then(onfulfilled, onrejected), this)
|
||||
}
|
||||
|
||||
public catch(onrejected?: (error: any) => any) {
|
||||
return Object.assign(this.promise.catch(onrejected), this)
|
||||
}
|
||||
|
||||
public finally(onfinally?: () => any) {
|
||||
return Object.assign(this.promise.finally(onfinally), this)
|
||||
}
|
||||
|
||||
private init(executor: (observer: SubscribableObserver<T, P>) => void | Promise<P>) {
|
||||
const execution = executor(this.observer)
|
||||
|
||||
Promise.resolve(execution as any)
|
||||
.then(result => {
|
||||
if (typeof (execution as any).then === 'function') {
|
||||
this.observer.complete(result)
|
||||
}
|
||||
})
|
||||
.catch(result => {
|
||||
if (typeof (execution as any).then === 'function') {
|
||||
this.observer.error(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
6
src/utils/index.ts
Normal file
6
src/utils/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from './PromiseResolver'
|
||||
export * from './Logger'
|
||||
export * from './ConversionTypeHelpers'
|
||||
export * from './GeneratorHelpers'
|
||||
export * from './SubscribablePromise'
|
||||
export * from './SubscribableObserver'
|
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["es2017", "es6", "es7", "dom"],
|
||||
"declaration": true,
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"experimentalDecorators": true,
|
||||
"preserveConstEnums": true,
|
||||
"outDir": "./dist/node/",
|
||||
"rootDir": "./src/",
|
||||
"sourceMap": true,
|
||||
"typeRoots": ["node_modules/@types"]
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user