Skip to content

Commit

Permalink
feat: add asset edit functionality (#58)
Browse files Browse the repository at this point in the history
* feat: add asset edit functionality

* feat: add missing edit functions for services

* feat: add getPublisherTrustedAlgorithms helper

* fix: test config

* chore: fix typo

* feat: update edit tests

* chore: update package-lock

* refactor: add resolvePublisherTrustedAlgorithms to publish and edit flow

* fix: handle undefined trustedAlgorithmAssets

* docs: add Trusted Algorithms docs link

* reafctor: rework asset retrieval and creation

* refactor: change lifecyclestate flow

* refactor: create checkIfFilesObjectChanged function

* refactor: extract createDatatokenAndPricing call from publish and edit

* docs: add TODO comment for price change via datatoken replacement

* refactor: expose editServicePrice via Nautilus

* refactor: improve getAssets return type

* refactor: add early continue to resolvePublisherTrustedAlgorithms

* refactor: relocate transformAquariusAssetToDDO into utils folder

* feat: update tests

* refactor: add AssetSpecificProps type

* docs: add TODO for AssetSpecificProps type

---------

Co-authored-by: Luca Milanese <luca.milanese90@gmail.com>
Co-authored-by: Moritz Kirstein <mkirstein@protonmail.com>
  • Loading branch information
3 people authored Nov 24, 2023
1 parent 25cd87c commit e44ffd7
Show file tree
Hide file tree
Showing 20 changed files with 1,681 additions and 167 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 22 additions & 3 deletions src/@types/Nautilus.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Metadata, PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
import { Asset, Metadata, PublisherTrustedAlgorithm } from '@oceanprotocol/lib'
import { NautilusConsumerParameter } from '../Nautilus/Asset/ConsumerParameters'
import { NautilusAsset } from '../Nautilus/Asset/NautilusAsset'
import {
Expand All @@ -17,6 +17,16 @@ export interface NautilusOptions {
skipDefaultConfig: boolean
}

export type ServiceBuilderConfig =
| {
serviceType: ServiceTypes
fileType: FileTypes
}
| {
aquariusAsset: Asset
serviceId: string
}

export interface IBuilder<T> {
build: () => T
reset: () => void
Expand All @@ -27,6 +37,15 @@ export enum CredentialListTypes {
DENY = 'deny'
}

export enum LifecycleStates {
ACTIVE = 0,
END_OF_LIFE = 1,
DEPRECATED = 2,
REVOKED_BY_PUBLISHER = 3,
ORDERING_DISABLED_TEMPORARILY = 4,
ASSET_UNLISTED = 5
}

export interface IAssetBuilder extends IBuilder<NautilusAsset> {
setType: (type: Metadata['type']) => IAssetBuilder
setName: (name: Metadata['name']) => IAssetBuilder
Expand Down Expand Up @@ -64,8 +83,8 @@ export interface IServiceBuilder<S extends ServiceTypes, F extends FileTypes>
parameter: NautilusConsumerParameter
) => IServiceBuilder<S, F>
addTrustedAlgorithmPublisher: (publisher: string) => IServiceBuilder<S, F>
addTrustedAlgorithm: (
algorithm: PublisherTrustedAlgorithm
addTrustedAlgorithms: (
algorithms: PublisherTrustedAlgorithm[]
) => IServiceBuilder<S, F>
allowRawAlgorithms: (allow?: boolean) => IServiceBuilder<S, F>
allowAlgorithmNetworkAccess: (allow?: boolean) => IServiceBuilder<S, F>
Expand Down
7 changes: 7 additions & 0 deletions src/@types/Publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { Signer, providers } from 'ethers'
import {
FileTypes,
NautilusAsset,
NautilusService,
PricingConfigWithoutOwner,
ServiceTypes
Expand Down Expand Up @@ -93,6 +94,7 @@ export interface PublishDDOConfig {
chainConfig: Config
signer: Signer
ddo: DDO
asset?: NautilusAsset
}

export interface PublishResponse {
Expand All @@ -105,3 +107,8 @@ export interface PublishResponse {
ddo: DDO
setMetadataTxReceipt: providers.TransactionReceipt
}

export type TrustedAlgorithmAsset = {
did: string
serviceIds?: string[]
}
68 changes: 62 additions & 6 deletions src/Nautilus/Asset/AssetBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { CredentialListTypes, IAssetBuilder } from '../../@types/Nautilus'
import { Asset } from '@oceanprotocol/lib'
import {
CredentialListTypes,
IAssetBuilder,
LifecycleStates
} from '../../@types/Nautilus'
import { MetadataConfig, NftCreateDataWithoutOwner } from '../../@types/Publish'
import { combineArrays } from '../../utils'
import { NautilusAsset } from './NautilusAsset'
Expand All @@ -7,9 +12,21 @@ import {
NautilusService,
ServiceTypes
} from './Service/NautilusService'
import { NautilusDDO } from './NautilusDDO'

export class AssetBuilder implements IAssetBuilder {
private asset: NautilusAsset = new NautilusAsset()
private asset: NautilusAsset

constructor(aquariusAsset?: Asset) {
if (aquariusAsset) {
const nautilusDDO = NautilusDDO.createFromAquariusAsset(aquariusAsset)
this.asset = new NautilusAsset(nautilusDDO)
this.asset.owner = aquariusAsset.nft.owner
this.asset.lifecycleState = aquariusAsset.nft.state
} else {
this.asset = new NautilusAsset()
}
}

reset() {
this.asset = new NautilusAsset()
Expand Down Expand Up @@ -57,12 +74,24 @@ export class AssetBuilder implements IAssetBuilder {
return this
}

removeService(serviceId: string) {
this.asset.ddo.removeServices.push(serviceId)

return this
}

setNftData(tokenData: NftCreateDataWithoutOwner) {
this.asset.nftCreateData = tokenData

return this
}

setLifecycleState(state: LifecycleStates) {
this.asset.lifecycleState = state

return this
}

setOwner(owner: string) {
this.asset.owner = owner

Expand Down Expand Up @@ -121,29 +150,56 @@ export class AssetBuilder implements IAssetBuilder {

addCredentialAddresses(list: CredentialListTypes, addresses: string[]) {
// first get the index of the address credential list
const addressCredentialIndex = this.asset.credentials[list].findIndex(
const addressCredentialIndex = this.asset.ddo.credentials[list].findIndex(
(credential) => credential.type === 'address'
)

// get addresses already added to the credential values
const oldAddresses =
this.asset.credentials[list][addressCredentialIndex]?.values || []
this.asset.ddo.credentials[list][addressCredentialIndex]?.values || []

// add new values and remove duplicates
const newAddresses = combineArrays(oldAddresses, addresses)

// update the existing credential or add a new one for type address
if (addressCredentialIndex > -1)
this.asset.credentials[list][addressCredentialIndex].values = newAddresses
this.asset.ddo.credentials[list][addressCredentialIndex].values =
newAddresses
else
this.asset.credentials[list].push({
this.asset.ddo.credentials[list].push({
type: 'address',
values: newAddresses
})

return this
}

removeCredentialAddresses(list: CredentialListTypes, addresses: string[]) {
// first get the index of the address credential list
const addressCredentialIndex = this.asset.ddo.credentials[list].findIndex(
(credential) => credential.type === 'address'
)

if (addressCredentialIndex === -1) return this

// get addresses already added to the credential values
const oldAddresses =
this.asset.ddo.credentials[list][addressCredentialIndex]?.values

const newAddresses = oldAddresses.filter(
(address) => !addresses.includes(address)
)

if (newAddresses.length > 0) {
this.asset.ddo.credentials[list][addressCredentialIndex].values =
newAddresses
} else {
this.asset.ddo.credentials[list].splice(addressCredentialIndex, 1)
}

return this
}

build() {
// TODO: look for errors / missing input
return this.asset
Expand Down
18 changes: 11 additions & 7 deletions src/Nautilus/Asset/NautilusAsset.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Credentials, NftCreateData } from '@oceanprotocol/lib'
import { NftCreateData } from '@oceanprotocol/lib'
import { NftCreateDataWithoutOwner, PricingConfig } from '../../@types/Publish'
import { NautilusDDO } from './NautilusDDO'
import { nftInitialCreateData } from './constants/nft.constants'
import { LifecycleStates } from '../../@types'

export type PricingConfigWithoutOwner = {
type: PricingConfig['type']
Expand All @@ -12,15 +13,18 @@ export type PricingConfigWithoutOwner = {
* @internal
*/
export class NautilusAsset {
ddo: NautilusDDO = new NautilusDDO()
ddo: NautilusDDO
nftCreateData: NftCreateDataWithoutOwner
owner: string
credentials: Credentials = {
allow: [],
deny: []
}
lifecycleState: LifecycleStates

constructor(ddo?: NautilusDDO) {
if (ddo) {
this.ddo = ddo
} else {
this.ddo = new NautilusDDO()
}

constructor() {
this.initNftData()
}

Expand Down
73 changes: 60 additions & 13 deletions src/Nautilus/Asset/NautilusDDO.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { DDO, Service, generateDid } from '@oceanprotocol/lib'
import {
Asset,
Credentials,
DDO,
Service,
generateDid
} from '@oceanprotocol/lib'
import { MetadataConfig } from '../../@types'
import {
dateToStringNoMS,
getAllPromisesOnArray,
combineArraysAndReplaceItems
combineArraysAndReplaceItems,
removeDuplicatesFromArray
} from '../../utils'
import {
FileTypes,
NautilusService,
ServiceTypes
} from './Service/NautilusService'
import { transformAquariusAssetToDDO } from '../../utils/aquarius'

export class NautilusDDO {
id: string
Expand All @@ -19,8 +27,19 @@ export class NautilusDDO {
version: string = '4.1.0'
metadata: Partial<MetadataConfig> = {}
services: NautilusService<ServiceTypes, FileTypes>[] = []
removeServices: string[] = []

private ddo: DDO
credentials: Credentials = {
allow: [],
deny: []
}

static createFromAquariusAsset(aquariusAsset: Asset): NautilusDDO {
const ddo = transformAquariusAssetToDDO(aquariusAsset)

return this.createFromDDO(ddo)
}

static createFromDDO(ddo: DDO): NautilusDDO {
const nautilusDDO = new NautilusDDO()
Expand All @@ -32,9 +51,18 @@ export class NautilusDDO {
nautilusDDO.chainId = ddo.chainId
nautilusDDO.version = ddo.version

if (ddo.credentials?.allow)
nautilusDDO.credentials.allow = ddo.credentials.allow
if (ddo.credentials?.deny)
nautilusDDO.credentials.deny = ddo.credentials.deny

return nautilusDDO
}

getOriginalDDO() {
return this.ddo
}

private async buildDDOServices(): Promise<Service[]> {
if (this.services.length < 1)
throw new Error('At least one service needs to be defined.')
Expand Down Expand Up @@ -68,20 +96,38 @@ export class NautilusDDO {
// take ddo.services
const existingServices: Service[] = this.ddo?.services || []

// we simply return ddo.services, if nothing new was added
if (this.services.length < 1) return existingServices
// remove service from existing services if id changes to prevent old service after edit
for (const service of this.services) {
const isFilesObjectChanged = service.checkIfFilesObjectChanged()

// build new services if needed
const newServices = await this.buildDDOServices()
if (service.id && isFilesObjectChanged) {
this.removeServices.push(service.id)
}
}

// replace all existing services with new ones, based on the servie.id
const replacedServices = combineArraysAndReplaceItems(
existingServices,
newServices,
NautilusDDO.replaceServiceBasedOnId
let newServices: Service[]
if (this.services.length > 0) {
// build new services if needed
newServices = await this.buildDDOServices()
}

this.removeServices = removeDuplicatesFromArray(this.removeServices)

const reducedExistingServices = existingServices.filter(
(service) => !this.removeServices.includes(service.id)
)

return replacedServices
// replace all existing services with new ones, based on the servie.id
let replacedServices: Service[] | PromiseLike<Service[]>
if (this.services.length > 0) {
replacedServices = combineArraysAndReplaceItems(
reducedExistingServices,
newServices,
NautilusDDO.replaceServiceBasedOnId
)
}

return replacedServices || reducedExistingServices
}

private async buildDDO(create: boolean): Promise<DDO> {
Expand Down Expand Up @@ -110,7 +156,8 @@ export class NautilusDDO {
chainId: this.chainId,
version: this.version,
metadata: newMetadata,
services: newServices
services: newServices,
credentials: this.credentials
}

return this.ddo
Expand Down
Loading

0 comments on commit e44ffd7

Please sign in to comment.