Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add asset edit functionality #58

Merged
merged 25 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
aeada59
feat: add asset edit functionality
Abrom8 Sep 20, 2023
03e18dd
feat: add missing edit functions for services
Abrom8 Oct 27, 2023
5f37136
feat: add getPublisherTrustedAlgorithms helper
Abrom8 Nov 2, 2023
06fe332
fix: test config
LucaMilanese90 Nov 9, 2023
0b4591e
chore: fix typo
LucaMilanese90 Nov 9, 2023
92ee736
feat: update edit tests
LucaMilanese90 Nov 9, 2023
79f3bd7
chore: update package-lock
LucaMilanese90 Nov 10, 2023
0d7adf8
refactor: add resolvePublisherTrustedAlgorithms to publish and edit flow
Abrom8 Nov 15, 2023
d7a8d8d
fix: handle undefined trustedAlgorithmAssets
Abrom8 Nov 16, 2023
a745656
docs: add Trusted Algorithms docs link
Abrom8 Nov 16, 2023
d11e4fd
reafctor: rework asset retrieval and creation
Abrom8 Nov 17, 2023
e33eaad
refactor: change lifecyclestate flow
Abrom8 Nov 19, 2023
f079bb0
refactor: create checkIfFilesObjectChanged function
Abrom8 Nov 20, 2023
6676d0c
refactor: extract createDatatokenAndPricing call from publish and edit
Abrom8 Nov 20, 2023
0b68d27
docs: add TODO comment for price change via datatoken replacement
Abrom8 Nov 20, 2023
7a1b68c
refactor: expose editServicePrice via Nautilus
Abrom8 Nov 20, 2023
023fe2d
Merge remote-tracking branch 'origin/feat/edit' into feat/update-edit…
LucaMilanese90 Nov 23, 2023
d4ffe10
refactor: improve getAssets return type
Abrom8 Nov 23, 2023
ee5fcb3
refactor: add early continue to resolvePublisherTrustedAlgorithms
Abrom8 Nov 23, 2023
4314263
refactor: relocate transformAquariusAssetToDDO into utils folder
Abrom8 Nov 23, 2023
d1a4fe5
feat: update tests
LucaMilanese90 Nov 23, 2023
4f46325
refactor: add AssetSpecificProps type
Abrom8 Nov 24, 2023
e2cc885
Merge remote-tracking branch 'origin/feat/edit' into feat/update-edit…
LucaMilanese90 Nov 23, 2023
f7c5068
Merge pull request #60 from deltaDAO/feat/update-edit-tests
moritzkirstein Nov 24, 2023
f1a2ba6
docs: add TODO for AssetSpecificProps type
Abrom8 Nov 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Abrom8 marked this conversation as resolved.
Show resolved Hide resolved

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

return this
}

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

return this
}

Abrom8 marked this conversation as resolved.
Show resolved Hide resolved
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
Abrom8 marked this conversation as resolved.
Show resolved Hide resolved

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