diff --git a/.changeset/serious-meals-knock.md b/.changeset/serious-meals-knock.md new file mode 100644 index 0000000..b24d018 --- /dev/null +++ b/.changeset/serious-meals-knock.md @@ -0,0 +1,5 @@ +--- +"@deltadao/nautilus": patch +--- + +Fix publishing/editing of multi-service assets diff --git a/src/Nautilus/Nautilus.ts b/src/Nautilus/Nautilus.ts index b2d1472..f9713a8 100644 --- a/src/Nautilus/Nautilus.ts +++ b/src/Nautilus/Nautilus.ts @@ -25,7 +25,6 @@ import { createServiceWithDatatokenAndPricing, publishDDO } from '../publish' -import { getAllPromisesOnArray } from '../utils' import { getAsset, getAssets } from '../utils/aquarius' import { editPrice } from '../utils/contracts' import { resolvePublisherTrustedAlgorithms } from '../utils/helpers/trusted-algorithms' @@ -158,18 +157,18 @@ export class Nautilus { // -------------------------------------------------- // 3. Create Datatokens and Pricing for new Services // -------------------------------------------------- - const services = await getAllPromisesOnArray( - asset.ddo.services, - async (service) => { - return createServiceWithDatatokenAndPricing( + const services = [] + for (const service of asset.ddo.services) { + const serviceWithDatatokenAndPricing = + await createServiceWithDatatokenAndPricing( service, signer, chainConfig, nftAddress, asset.owner ) - } - ) + services.push(serviceWithDatatokenAndPricing) + } // -------------------------------------------------- // 4. Create the DDO and publish it on NFT @@ -199,11 +198,11 @@ export class Nautilus { const { signer, chainConfig } = this.getChainConfig() const { nftAddress, services: nautilusDDOServices } = asset.ddo - let services: { + const services: { service: NautilusService datatokenAddress: string tx: TransactionReceipt - }[] + }[] = [] await resolvePublisherTrustedAlgorithms( nautilusDDOServices, @@ -216,19 +215,16 @@ export class Nautilus { ) // TODO check if service prices can be changed via datatoken replacement (currently buggy could be a caching problem) - if (changedPriceServices.length > 0) { - services = await getAllPromisesOnArray( - changedPriceServices, - async (service) => { - return createServiceWithDatatokenAndPricing( - service, - signer, - chainConfig, - nftAddress, - asset.owner - ) - } - ) + for (const service of changedPriceServices) { + const serviceWithDatatokenAndPricing = + await createServiceWithDatatokenAndPricing( + service, + signer, + chainConfig, + nftAddress, + asset.owner + ) + services.push(serviceWithDatatokenAndPricing) } const ddo = await asset.ddo.getDDO({ diff --git a/src/package.json b/src/package.json index 3904fe3..af3fb71 100644 --- a/src/package.json +++ b/src/package.json @@ -33,9 +33,7 @@ "documents": "./src/**/*.gql.ts", "generates": { "./src/@types/subgraph/api.ts": { - "plugins": [ - "typescript" - ] + "plugins": ["typescript"] }, "./src/": { "preset": "near-operation-file", @@ -43,9 +41,7 @@ "baseTypesPath": "@types/subgraph/api.ts", "extension": ".generated.ts" }, - "plugins": [ - "typescript-operations" - ], + "plugins": ["typescript-operations"], "config": { "omitOperationSuffix": true, "typesPrefix": "I" diff --git a/test/fixtures/Ethers.ts b/test/fixtures/Ethers.ts index e2ee7f7..87af94d 100644 --- a/test/fixtures/Ethers.ts +++ b/test/fixtures/Ethers.ts @@ -2,7 +2,7 @@ import dotenv from 'dotenv' import { type Signer, Wallet, providers } from 'ethers' dotenv.config() -export const MUMBAI_NODE_URI = 'https://rpc-mumbai.maticvigil.com' +export const MUMBAI_NODE_URI = 'https://polygon-mumbai-bor-rpc.publicnode.com' export function getSigner(key: 1 | 2 = 1, nodeUri?: string): Signer { const providerUrl = nodeUri || MUMBAI_NODE_URI diff --git a/test/integration/edit.test.ts b/test/integration/edit.test.ts index 3194587..8bcdf02 100644 --- a/test/integration/edit.test.ts +++ b/test/integration/edit.test.ts @@ -89,26 +89,52 @@ describe('Edit Integration tests', function () { const result = await nautilus.publish(asset) fixedPricedAlgoWithCredentials = result?.ddo + console.log( + `asset published (${fixedPricedAlgoWithCredentials?.id}), waiting for aquarius indexing...` + ) await aquarius.waitForAqua(fixedPricedAlgoWithCredentials?.id) assert(result) }) it('publishes a compute type dataset', async () => { - const serviceBuilder = new ServiceBuilder({ - serviceType: ServiceTypes.COMPUTE, - fileType: FileTypes.URL - }) + const assetBuilder = new AssetBuilder() - const testServiceOne = serviceBuilder - .setName('Test service 1') - .setServiceEndpoint(providerUri) - .setTimeout(datasetService.timeout) - .setPricing(await getPricing(signer, 'fixed')) - .addFile(datasetService.files[0]) - .build() + const pricing = await getPricing(signer, 'fixed') + const services = [ + { + name: 'test service 1', + serviceEndpoint: providerUri, + timeout: datasetService.timeout, + pricing, + file: datasetService.files[0] + }, + { + name: 'test service 2', + serviceEndpoint: providerUri, + timeout: datasetService.timeout, + pricing, + file: datasetService.files[0] + } + ] + + for (const service of services) { + const serviceBuilder = new ServiceBuilder({ + serviceType: ServiceTypes.COMPUTE, + fileType: FileTypes.URL + }) + + const builtService = serviceBuilder + .setName(service.name) + .setServiceEndpoint(service.serviceEndpoint) + .setTimeout(service.timeout) + .setPricing(pricing) + .addFile(service.file) + .build() + + assetBuilder.addService(builtService) + } - const assetBuilder = new AssetBuilder() const asset = assetBuilder .setAuthor('testAuthor') .setDescription('A dataset publishing test') @@ -116,12 +142,14 @@ describe('Edit Integration tests', function () { .setName('Test Publish Dataset Fixed') .setOwner(signerAddress) .setType('dataset') - .addService(testServiceOne) .build() const result = await nautilus.publish(asset) fixedPriceComputeDataset = result?.ddo - await aquarius.waitForAqua(fixedPricedAlgoWithCredentials?.id) + console.log( + `asset published (${fixedPriceComputeDataset?.id}), waiting for aquarius indexing...` + ) + await aquarius.waitForAqua(fixedPriceComputeDataset?.id) assert(result) }) @@ -667,8 +695,7 @@ describe('Edit Integration tests', function () { assert(result) }) - // TODO: reinstate test after fixing publishing with multiple services - it.skip('edit services - remove service', async () => { + it('edit services - remove service', async () => { const aquariusAsset = await nautilus.getAquariusAsset( fixedPriceComputeDataset?.id ) diff --git a/test/integration/publish.test.ts b/test/integration/publish.test.ts index 11086bd..d6ffe26 100644 --- a/test/integration/publish.test.ts +++ b/test/integration/publish.test.ts @@ -1,5 +1,9 @@ import assert from 'node:assert' -import type { ConsumerParameter } from '@oceanprotocol/lib' +import { + Aquarius, + type Config, + type ConsumerParameter +} from '@oceanprotocol/lib' import type { Signer } from 'ethers' import { CredentialListTypes } from '../../src/@types' import { @@ -17,14 +21,17 @@ import { datasetService, getPricing } from '../fixtures/AssetConfig' +import { getTestConfig } from '../fixtures/Config' import { MUMBAI_NODE_URI, getSigner } from '../fixtures/Ethers' const nodeUri = MUMBAI_NODE_URI describe('Publish Integration tests', function () { // set timeout for this describe block considering tsx will happen - this.timeout(50000) + this.timeout(100000) + let aquarius: Aquarius + let config: Config let signer: Signer let signerAddress: string let nautilus: Nautilus @@ -34,6 +41,7 @@ describe('Publish Integration tests', function () { Nautilus.setLogLevel(LogLevel.Verbose) signer = getSigner(1, nodeUri) signerAddress = await signer.getAddress() + config = await getTestConfig(signer) console.log('Testing with signer:', signerAddress) @@ -45,6 +53,10 @@ describe('Publish Integration tests', function () { process.env.PROVIDER_URI_TEST || nautilus.getOceanConfig().providerUri console.log('Testing with signer:', signerAddress) + + aquarius = new Aquarius( + process.env.METADATA_CACHE_URI_TEST || config?.metadataCacheUri + ) }) it('publishes a free access asset', async () => { @@ -103,6 +115,63 @@ describe('Publish Integration tests', function () { assert(result) }) + it('publishes a multi service compute type dataset', async () => { + const assetBuilder = new AssetBuilder() + + const pricing = await getPricing(signer, 'fixed') + const services = [ + { + name: 'test service 1', + serviceEndpoint: providerUri, + timeout: datasetService.timeout, + pricing, + file: datasetService.files[0] + }, + { + name: 'test service 2', + serviceEndpoint: providerUri, + timeout: datasetService.timeout, + pricing, + file: datasetService.files[0] + } + ] + + for (const service of services) { + const serviceBuilder = new ServiceBuilder({ + serviceType: ServiceTypes.COMPUTE, + fileType: FileTypes.URL + }) + + const builtService = serviceBuilder + .setName(service.name) + .setServiceEndpoint(service.serviceEndpoint) + .setTimeout(service.timeout) + .setPricing(pricing) + .addFile(service.file) + .build() + + assetBuilder.addService(builtService) + } + + const asset = assetBuilder + .setAuthor('testAuthor') + .setDescription('A dataset publishing test') + .setLicense('MIT') + .setName('Test Publish Dataset Fixed') + .setOwner(signerAddress) + .setType('dataset') + .build() + + const result = await nautilus.publish(asset) + const fixedPriceComputeDataset = result?.ddo + console.log( + `asset published (${fixedPriceComputeDataset?.id}), waiting for aquarius indexing...` + ) + await aquarius.waitForAqua(fixedPriceComputeDataset?.id) + + assert(result) + }) + it('publishes an asset with credentials', async () => { const serviceBuilder = new ServiceBuilder({ serviceType: ServiceTypes.ACCESS,