diff --git a/.travis.yml b/.travis.yml index 71ac5243..cf00c3b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ before_script: - export AQUARIUS_URI="http://172.15.0.5:5000" - export DEPLOY_CONTRACTS="true" - export CONTRACTS_VERSION=v0.5.7 - - export PROVIDER_VERSION=latest - bash -x start_ocean.sh --no-dashboard 2>&1 > start_ocean.log & - cd .. - ./scripts/waitforcontracts.sh diff --git a/src/ddo/interfaces/EditableMetadata.ts b/src/ddo/interfaces/EditableMetadata.ts index d6b6a2b9..279059c4 100644 --- a/src/ddo/interfaces/EditableMetadata.ts +++ b/src/ddo/interfaces/EditableMetadata.ts @@ -1,9 +1,7 @@ import { EditableMetadataLinks } from './EditableMetadataLinks' -import { ServicePrices } from './ServicePrices' export interface EditableMetadata { description?: string title?: string links?: EditableMetadataLinks[] - servicePrices?: ServicePrices[] } diff --git a/src/ddo/interfaces/File.ts b/src/ddo/interfaces/File.ts index 0cdbfc67..6830cc83 100644 --- a/src/ddo/interfaces/File.ts +++ b/src/ddo/interfaces/File.ts @@ -9,7 +9,13 @@ export interface File { * File URL. * @type {string} */ - url: string + url?: string + + /** + * Is URL accessible. + * @type {boolean} + */ + valid?: boolean /** * File index. diff --git a/src/metadatacache/MetadataCache.ts b/src/metadatacache/MetadataCache.ts index 840f4f58..329ac9f7 100644 --- a/src/metadatacache/MetadataCache.ts +++ b/src/metadatacache/MetadataCache.ts @@ -201,60 +201,6 @@ export class MetadataCache { 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} Result. - */ - public async updateComputePrivacy( - did: DID | string, - serviceIndex: number, - allowRawAlgorithm: boolean, - allowNetworkAccess: boolean, - trustedAlgorithms: string[], - updated: string, - signature: string - ): Promise { - 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: Response) => { - 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 - } - public async getOwnerAssets(owner: string): Promise { const q = { offset: 100, @@ -271,47 +217,6 @@ export class MetadataCache { 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} Result. - */ - public async editMetadata( - did: DID | string, - newMetadata: EditableMetadata, - updated: string, - signature: string - ): Promise { - 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: Response) => { - 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. diff --git a/src/ocean/Assets.ts b/src/ocean/Assets.ts index 11b8640e..2fa2c7c8 100644 --- a/src/ocean/Assets.ts +++ b/src/ocean/Assets.ts @@ -239,82 +239,52 @@ export class Assets extends Instantiable { return (await this.ocean.metadatacache.queryMetadata(searchQuery)).results } - /** - * Edit Metadata for a DDO. - * @param {did} string DID. - * @param {newMetadata} EditableMetadata Metadata fields & new values. - * @param {Account} account Ethereum account of owner to sign and prove the ownership. - * @return {Promise} + /** Metadata updates + * Don't forget to call ocean.OnChainMetadataCache.update after using this functions + * ie: ocean.OnChainMetadataCache.update(ddo.id,ddo,account.getId()) */ - public async editMetadata( - did: string, - newMetadata: EditableMetadata, - account: Account - ): Promise { - const oldDdo = await this.ocean.metadatacache.retrieveDDO(did) - let i - for (i = 0; i < oldDdo.service.length; i++) { - if (oldDdo.service[i].type === 'metadata') { - if (newMetadata.title) oldDdo.service[i].attributes.main.name = newMetadata.title - if (!oldDdo.service[i].attributes.additionalInformation) - oldDdo.service[i].attributes.additionalInformation = Object() - if (newMetadata.description) - oldDdo.service[i].attributes.additionalInformation.description = - newMetadata.description - if (newMetadata.links) - oldDdo.service[i].attributes.additionalInformation.links = newMetadata.links - } + + /** + * Edit Metadata for a DID. + * @param {ddo} DDO if empty, will trigger a retrieve + * @param {newMetadata} EditableMetadata Metadata fields & new values. + * @return {Promise} the new DDO + */ + public async editMetadata(ddo: DDO, newMetadata: EditableMetadata): Promise { + if (!ddo) return null + for (let i = 0; i < ddo.service.length; i++) { + if (ddo.service[i].type !== 'metadata') continue + if (newMetadata.title) ddo.service[i].attributes.main.name = newMetadata.title + if (!ddo.service[i].attributes.additionalInformation) + ddo.service[i].attributes.additionalInformation = Object() + if (newMetadata.description) + ddo.service[i].attributes.additionalInformation.description = + newMetadata.description + if (newMetadata.links) + ddo.service[i].attributes.additionalInformation.links = newMetadata.links } - if (newMetadata.servicePrices) { - for (i = 0; i < newMetadata.servicePrices.length; i++) { - if ( - newMetadata.servicePrices[i].cost && - newMetadata.servicePrices[i].serviceIndex - ) { - oldDdo.service[newMetadata.servicePrices[i].serviceIndex].attributes.main.cost = - newMetadata.servicePrices[i].cost - } - } - } - const storeTx = await this.ocean.OnChainMetadataCache.update( - oldDdo.id, - oldDdo, - account.getId() - ) - if (storeTx) return oldDdo - else return null + return ddo } /** - * Update Compute Privacy - * @param {did} string DID. - * @param {number} serviceIndex Index of the compute service in the DDO - * @param {ServiceComputePrivacy} computePrivacy ComputePrivacy fields & new values. - * @param {Account} account Ethereum account of owner to sign and prove the ownership. - * @return {Promise} + * Edit Service Timeouts + * @param {ddo} DDO if empty, will trigger a retrieve + * @param {number} serviceIndex Index of the compute service in the DDO. + * @param {number} timeout New timeout setting + * @return {Promise} */ - public async updateComputePrivacy( - did: string, + public async editServiceTimeout( + ddo: DDO, serviceIndex: number, - computePrivacy: ServiceComputePrivacy, - account: Account + timeout: number ): Promise { - const oldDdo = await this.ocean.metadatacache.retrieveDDO(did) - if (oldDdo.service[serviceIndex].type !== 'compute') return null - oldDdo.service[serviceIndex].attributes.main.privacy.allowRawAlgorithm = - computePrivacy.allowRawAlgorithm - oldDdo.service[serviceIndex].attributes.main.privacy.allowNetworkAccess = - computePrivacy.allowNetworkAccess - oldDdo.service[serviceIndex].attributes.main.privacy.trustedAlgorithms = - computePrivacy.trustedAlgorithms - const storeTx = await this.ocean.OnChainMetadataCache.update( - oldDdo.id, - oldDdo, - account.getId() - ) - if (storeTx) return oldDdo - else return null + if (!ddo) return null + if (typeof ddo.service[serviceIndex] === 'undefined') return null + if (timeout < 0) return null + ddo.service[serviceIndex].attributes.main.timeout = parseInt(timeout.toFixed()) + return ddo } + /** End metadata updates */ /** * Returns the creator of a asset. diff --git a/src/ocean/Compute.ts b/src/ocean/Compute.ts index 145b74cf..68027753 100644 --- a/src/ocean/Compute.ts +++ b/src/ocean/Compute.ts @@ -433,4 +433,35 @@ export class Compute extends Instantiable { return order }) } + + /** + * Edit Compute Privacy + * @param {did} string DID. You can leave this empty if you already have the DDO + * @param {ddo} DDO if empty, will trigger a retrieve + * @param {number} serviceIndex Index of the compute service in the DDO. If -1, will try to find it + * @param {ServiceComputePrivacy} computePrivacy ComputePrivacy fields & new values. + * @param {Account} account Ethereum account of owner to sign and prove the ownership. + * @return {Promise} + */ + public async editComputePrivacy( + ddo: DDO, + serviceIndex: number, + computePrivacy: ServiceComputePrivacy + ): Promise { + if (!ddo) return null + if (serviceIndex === -1) { + const service = ddo.findServiceByType('compute') + if (!service) return null + serviceIndex = service.index + } + if (typeof ddo.service[serviceIndex] === 'undefined') return null + if (ddo.service[serviceIndex].type !== 'compute') return null + ddo.service[serviceIndex].attributes.main.privacy.allowRawAlgorithm = + computePrivacy.allowRawAlgorithm + ddo.service[serviceIndex].attributes.main.privacy.allowNetworkAccess = + computePrivacy.allowNetworkAccess + ddo.service[serviceIndex].attributes.main.privacy.trustedAlgorithms = + computePrivacy.trustedAlgorithms + return ddo + } } diff --git a/src/provider/Provider.ts b/src/provider/Provider.ts index 715ad993..0a575b37 100644 --- a/src/provider/Provider.ts +++ b/src/provider/Provider.ts @@ -8,6 +8,7 @@ import { MetadataAlgorithm } from '../ddo/interfaces/MetadataAlgorithm' import { Versions } from '../ocean/Versions' import { Response } from 'node-fetch' import { DDO } from '../ddo/DDO' +import DID from '../ocean/DID' const apiPath = '/api/v1/services' @@ -73,20 +74,28 @@ export class Provider extends Instantiable { } } - public async checkURL(url: string): Promise> { - const args = { url } + /** Get URL details (if possible) + * @param {String | DID} url or did + * @return {Promise} urlDetails + */ + public async fileinfo(url: string | DID): Promise { + let args + const files: File[] = [] + if (url instanceof DID) { + args = { did: url.getDid() } + } else args = { url } try { const response = await this.ocean.utils.fetch.post( - this.getCheckURLEndpoint(), - decodeURI(JSON.stringify(args)) + this.getFileinfoEndpoint(), + JSON.stringify(args) ) - - const result = await response.json() - - return result.result + const results: File[] = await response.json() + for (const result of results) { + files.push(result) + } + return files } catch (e) { - this.logger.error(e) - throw new Error('HTTP request failed') + return null } } @@ -298,8 +307,8 @@ export class Provider extends Instantiable { return `${this.url}${apiPath}/encrypt` } - public getCheckURLEndpoint(): string { - return `${this.url}${apiPath}/checkURL` + public getFileinfoEndpoint(): string { + return `${this.url}${apiPath}/fileinfo` } public getPublishEndpoint(): string { diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index b4183ecb..0b4ee3fc 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -468,13 +468,14 @@ describe('Compute flow', () => { } } assert(computeIndex > 0) - const newDdo = await ocean.assets.updateComputePrivacy( - ddo.id, + const newDdo = await ocean.compute.editComputePrivacy( + ddo, computeIndex, - newComputePrivacy, - alice + newComputePrivacy ) assert(newDdo !== null) + const txid = await ocean.OnChainMetadataCache.update(newDdo.id, newDdo, alice.getId()) + assert(txid !== null) await sleep(60000) const metaData = await ocean.assets.getServiceByType(ddo.id, 'compute') assert.equal( diff --git a/test/integration/Marketplaceflow.test.ts b/test/integration/Marketplaceflow.test.ts index 2a508711..c1bf6269 100644 --- a/test/integration/Marketplaceflow.test.ts +++ b/test/integration/Marketplaceflow.test.ts @@ -5,7 +5,8 @@ import spies from 'chai-spies' import Web3 from 'web3' import { AbiItem } from 'web3-utils/types' import { DataTokens } from '../../src/datatokens/Datatokens' -import { Account, EditableMetadata, Service, ServiceAccess } from '../../src/lib' +import { Account, EditableMetadata, Service, ServiceAccess, DID } from '../../src/lib' +import { noDidPrefixed } from '../../src/utils/' import { Ocean } from '../../src/ocean/Ocean' import { ConfigHelper } from '../../src/utils/ConfigHelper' import { TestContractHandler } from '../TestContractHandler' @@ -89,8 +90,7 @@ describe('Marketplace flow', () => { license: 'MIT', files: [ { - url: - 'https://raw.githubusercontent.com/tbertinmahieux/MSongsDB/master/Tasks_Demos/CoverSongs/shs_dataset_test.txt', + url: 'https://s3.amazonaws.com/testfiles.oceanprotocol.com/info.0.json', checksum: 'efb2c764274b745f5fc37f97c6b0e761', contentLength: '4535431', contentType: 'text/csv', @@ -212,8 +212,10 @@ describe('Marketplace flow', () => { title: 'new title', links: [{ name: 'link1', type: 'sample', url: 'http://example.net' }] } - const newDdo = await ocean.assets.editMetadata(ddo.id, newMetaData, alice) + const newDdo = await ocean.assets.editMetadata(ddo, newMetaData) assert(newDdo !== null) + const txid = await ocean.OnChainMetadataCache.update(newDdo.id, newDdo, alice.getId()) + assert(txid !== null) await sleep(60000) const metaData = await ocean.assets.getServiceByType(ddo.id, 'metadata') assert.equal(metaData.attributes.main.name, newMetaData.title) @@ -224,6 +226,28 @@ describe('Marketplace flow', () => { assert.deepEqual(metaData.attributes.additionalInformation.links, newMetaData.links) }) + it('Alice updates timeout for the access service', async () => { + const service = ddo.findServiceByType('access') + assert(service !== null) + const serviceIndex = service.index + const newTimeout = 123 + const newDdo = await ocean.assets.editServiceTimeout(ddo, serviceIndex, newTimeout) + assert(newDdo !== null) + const txid = await ocean.OnChainMetadataCache.update(newDdo.id, newDdo, alice.getId()) + assert(txid !== null) + await sleep(60000) + const metaData = await ocean.assets.getServiceByType(ddo.id, 'access') + assert(parseInt(metaData.attributes.main.timeout) === parseInt(newTimeout.toFixed())) + }) + + it('Alice should check if her asset has valid url(s)', async () => { + const did: DID = DID.generate(noDidPrefixed(ddo.id)) + const response = await ocean.provider.fileinfo(did) + assert(response[0].valid === true) + assert(response[0].contentLength === '1161') + assert(response[0].contentType === 'application/json') + }) + it('Alice publishes a dataset but passed data token is invalid', async () => { price = '10' // in datatoken const publishedDate = new Date(Date.now()).toISOString().split('.')[0] + 'Z' diff --git a/test/integration/Provider.test.ts b/test/integration/Provider.test.ts index c0c5e93c..0bf38f42 100644 --- a/test/integration/Provider.test.ts +++ b/test/integration/Provider.test.ts @@ -18,8 +18,13 @@ describe('Provider tests', () => { }) it('Check a valid URL', async () => { const url = 'https://s3.amazonaws.com/testfiles.oceanprotocol.com/info.0.json' - const response = await ocean.provider.checkURL(url) - assert(response.contentLength === '1161') - assert(response.contentType === 'application/json') + const response = await ocean.provider.fileinfo(url) + assert(response[0].contentLength === '1161') + assert(response[0].contentType === 'application/json') + }) + it('Check a invalid URL', async () => { + const url = 'https://s3.amazonaws.com/testfiles.oceanprotocol.com/nosuchfile' + const response = await ocean.provider.fileinfo(url) + assert(response[0].valid === false) }) })