- {assets.map((asset: AssetSelectionAsset) => (
-
-
-
-
+ const [searchValue, setSearchValue] = useState('')
-
-
-
-
-
- ))}
+ const styleClassesInput = cx({
+ input: true,
+ [styles.checkbox]: multiple,
+ [styles.radio]: !multiple
+ })
+
+ function handleSearchInput(e: ChangeEvent
) {
+ setSearchValue(e.target.value)
+ }
+
+ return (
+
+
+
+ {assets ? (
+ assets
+ .filter((asset: AssetSelectionAsset) =>
+ searchValue !== ''
+ ? asset.name.toLowerCase().includes(searchValue) ||
+ asset.did.includes(searchValue)
+ : asset
+ )
+ .map((asset: AssetSelectionAsset) => (
+
+
+
+
+
+
+ ))
+ ) : (
+
+ )}
+
)
}
diff --git a/src/components/organisms/AssetActions/Edit/DebugEditCompute.tsx b/src/components/organisms/AssetActions/Edit/DebugEditCompute.tsx
new file mode 100644
index 000000000..7f5698776
--- /dev/null
+++ b/src/components/organisms/AssetActions/Edit/DebugEditCompute.tsx
@@ -0,0 +1,40 @@
+import { DDO, ServiceComputePrivacy } from '@oceanprotocol/lib'
+import React, { ReactElement, useEffect, useState } from 'react'
+import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
+import { useOcean } from '../../../../providers/Ocean'
+import { transformComputeFormToServiceComputePrivacy } from '../../../../utils/compute'
+import DebugOutput from '../../../atoms/DebugOutput'
+
+export default function DebugEditCompute({
+ values,
+ ddo
+}: {
+ values: ComputePrivacyForm
+ ddo: DDO
+}): ReactElement {
+ const { ocean } = useOcean()
+ const [
+ formTransformed,
+ setFormTransformed
+ ] = useState()
+
+ useEffect(() => {
+ if (!ocean) return
+
+ async function transformValues() {
+ const privacy = await transformComputeFormToServiceComputePrivacy(
+ values,
+ ocean
+ )
+ setFormTransformed(privacy)
+ }
+ transformValues()
+ }, [values, ddo, ocean])
+
+ return (
+ <>
+
+
+ >
+ )
+}
diff --git a/src/components/organisms/AssetActions/Edit/Debug.tsx b/src/components/organisms/AssetActions/Edit/DebugEditMetadata.tsx
similarity index 100%
rename from src/components/organisms/AssetActions/Edit/Debug.tsx
rename to src/components/organisms/AssetActions/Edit/DebugEditMetadata.tsx
diff --git a/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx b/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx
new file mode 100644
index 000000000..8af2bbe37
--- /dev/null
+++ b/src/components/organisms/AssetActions/Edit/EditComputeDataset.tsx
@@ -0,0 +1,161 @@
+import { useOcean } from '../../../../providers/Ocean'
+import { useWeb3 } from '../../../../providers/Web3'
+import { Formik } from 'formik'
+import React, { ReactElement, useState } from 'react'
+import {
+ validationSchema,
+ getInitialValues,
+ ComputePrivacyForm
+} from '../../../../models/FormEditComputeDataset'
+import { useAsset } from '../../../../providers/Asset'
+import FormEditComputeDataset from './FormEditComputeDataset'
+import { Logger, ServiceComputePrivacy } from '@oceanprotocol/lib'
+import MetadataFeedback from '../../../molecules/MetadataFeedback'
+import { graphql, useStaticQuery } from 'gatsby'
+import { useUserPreferences } from '../../../../providers/UserPreferences'
+import DebugEditCompute from './DebugEditCompute'
+import styles from './index.module.css'
+import { transformComputeFormToServiceComputePrivacy } from '../../../../utils/compute'
+
+const contentQuery = graphql`
+ query EditComputeDataQuery {
+ content: allFile(
+ filter: { relativePath: { eq: "pages/editComputeDataset.json" } }
+ ) {
+ edges {
+ node {
+ childPagesJson {
+ description
+ form {
+ title
+ success
+ successAction
+ error
+ data {
+ name
+ placeholder
+ label
+ help
+ type
+ required
+ sortOptions
+ options
+ multiple
+ rows
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`
+
+export default function EditComputeDataset({
+ setShowEdit
+}: {
+ setShowEdit: (show: boolean) => void
+}): ReactElement {
+ const data = useStaticQuery(contentQuery)
+ const content = data.content.edges[0].node.childPagesJson
+
+ const { debug } = useUserPreferences()
+ const { ocean } = useOcean()
+ const { accountId } = useWeb3()
+ const { ddo, refreshDdo } = useAsset()
+ const [success, setSuccess] = useState()
+ const [error, setError] = useState()
+
+ const hasFeedback = error || success
+
+ async function handleSubmit(
+ values: ComputePrivacyForm,
+ resetForm: () => void
+ ) {
+ try {
+ const privacy = await transformComputeFormToServiceComputePrivacy(
+ values,
+ ocean
+ )
+
+ const ddoEditedComputePrivacy = await ocean.compute.editComputePrivacy(
+ ddo,
+ 1,
+ privacy as ServiceComputePrivacy
+ )
+
+ if (!ddoEditedComputePrivacy) {
+ setError(content.form.error)
+ Logger.error(content.form.error)
+ return
+ }
+
+ const storedddo = await ocean.assets.updateMetadata(
+ ddoEditedComputePrivacy,
+ accountId
+ )
+ if (!storedddo) {
+ setError(content.form.error)
+ Logger.error(content.form.error)
+ return
+ } else {
+ // Edit succeeded
+ setSuccess(content.form.success)
+ resetForm()
+ }
+ } catch (error) {
+ Logger.error(error.message)
+ setError(error.message)
+ }
+ }
+
+ return (
+ {
+ // move user's focus to top of screen
+ window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
+ // kick off editing
+ await handleSubmit(values, resetForm)
+ }}
+ >
+ {({ values, isSubmitting }) =>
+ isSubmitting || hasFeedback ? (
+ {
+ await refreshDdo()
+ setShowEdit(false)
+ }
+ }}
+ />
+ ) : (
+ <>
+ {content.description}
+
+
+
+
+ {debug === true && (
+
+
+
+ )}
+ >
+ )
+ }
+
+ )
+}
diff --git a/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx b/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx
new file mode 100644
index 000000000..40409fcfa
--- /dev/null
+++ b/src/components/organisms/AssetActions/Edit/FormEditComputeDataset.tsx
@@ -0,0 +1,76 @@
+import React, { ReactElement, useEffect, useState } from 'react'
+import { Field, Form, FormikContextType, useFormikContext } from 'formik'
+import Button from '../../../atoms/Button'
+import Input from '../../../atoms/Input'
+import { useOcean } from '../../../../providers/Ocean'
+import { useWeb3 } from '../../../../providers/Web3'
+import { FormFieldProps } from '../../../../@types/Form'
+import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
+import stylesIndex from './index.module.css'
+import styles from './FormEditMetadata.module.css'
+import { getAlgorithmsForAssetSelection } from '../../../../utils/aquarius'
+import { useAsset } from '../../../../providers/Asset'
+import { ComputePrivacyForm } from '../../../../models/FormEditComputeDataset'
+
+export default function FormEditComputeDataset({
+ data,
+ title,
+ setShowEdit
+}: {
+ data: FormFieldProps[]
+ title: string
+ setShowEdit: (show: boolean) => void
+}): ReactElement {
+ const { accountId } = useWeb3()
+ const { ocean, config } = useOcean()
+ const { ddo } = useAsset()
+ const {
+ isValid,
+ values
+ }: FormikContextType = useFormikContext()
+ const [allAlgorithms, setAllAlgorithms] = useState()
+
+ const { publisherTrustedAlgorithms } = ddo?.findServiceByType(
+ 'compute'
+ ).attributes.main.privacy
+
+ useEffect(() => {
+ getAlgorithmsForAssetSelection(
+ config.metadataCacheUri,
+ publisherTrustedAlgorithms
+ ).then((algorithms) => {
+ setAllAlgorithms(algorithms)
+ })
+ }, [config.metadataCacheUri, publisherTrustedAlgorithms])
+
+ return (
+
+ )
+}
diff --git a/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css b/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css
index f42d3a152..4bb393ac4 100644
--- a/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css
+++ b/src/components/organisms/AssetActions/Edit/FormEditMetadata.module.css
@@ -22,3 +22,7 @@
margin-left: calc(var(--spacer) / 2);
margin-right: calc(var(--spacer) / 2);
}
+
+select[multiple] {
+ height: 130px;
+}
diff --git a/src/components/organisms/AssetActions/Edit/index.module.css b/src/components/organisms/AssetActions/Edit/index.module.css
index ea4143cbe..740b8e04b 100644
--- a/src/components/organisms/AssetActions/Edit/index.module.css
+++ b/src/components/organisms/AssetActions/Edit/index.module.css
@@ -8,3 +8,14 @@
margin-top: -1.5rem;
max-width: 50rem;
}
+
+.title {
+ font-size: var(--font-size-large);
+ border-bottom: 1px solid var(--border-color);
+ padding-bottom: calc(var(--spacer) / 2);
+ margin-top: -1rem;
+ margin-left: -2rem;
+ margin-right: -2rem;
+ padding-left: 2rem;
+ padding-right: 2rem;
+}
diff --git a/src/components/organisms/AssetActions/Edit/index.tsx b/src/components/organisms/AssetActions/Edit/index.tsx
index 9ad4257f6..6f526469c 100644
--- a/src/components/organisms/AssetActions/Edit/index.tsx
+++ b/src/components/organisms/AssetActions/Edit/index.tsx
@@ -8,7 +8,7 @@ import {
import { useAsset } from '../../../../providers/Asset'
import { useUserPreferences } from '../../../../providers/UserPreferences'
import { MetadataPreview } from '../../../molecules/MetadataPreview'
-import Debug from './Debug'
+import Debug from './DebugEditMetadata'
import Web3Feedback from '../../../molecules/Wallet/Feedback'
import FormEditMetadata from './FormEditMetadata'
import { mapTimeoutStringToSeconds } from '../../../../utils/metadata'
@@ -64,6 +64,9 @@ export default function Edit({
const [success, setSuccess] = useState()
const [error, setError] = useState()
const [timeoutStringValue, setTimeoutStringValue] = useState()
+ const timeout = ddo.findServiceByType('access')
+ ? ddo.findServiceByType('access').attributes.main.timeout
+ : ddo.findServiceByType('compute').attributes.main.timeout
const hasFeedback = error || success
@@ -122,10 +125,7 @@ export default function Edit({
return (
{
// move user's focus to top of screen
diff --git a/src/components/organisms/AssetContent/index.module.css b/src/components/organisms/AssetContent/index.module.css
index bc9ef4f35..b54048e13 100644
--- a/src/components/organisms/AssetContent/index.module.css
+++ b/src/components/organisms/AssetContent/index.module.css
@@ -42,3 +42,7 @@
margin-left: calc(var(--spacer) / 4);
margin-right: calc(var(--spacer) / 4);
}
+
+.separator {
+ color: var(--color-secondary);
+}
diff --git a/src/components/organisms/AssetContent/index.tsx b/src/components/organisms/AssetContent/index.tsx
index 7a6e7ff1e..1e6e88d9c 100644
--- a/src/components/organisms/AssetContent/index.tsx
+++ b/src/components/organisms/AssetContent/index.tsx
@@ -3,7 +3,6 @@ import { graphql, useStaticQuery } from 'gatsby'
import Markdown from '../../atoms/Markdown'
import MetaFull from './MetaFull'
import MetaSecondary from './MetaSecondary'
-import styles from './index.module.css'
import AssetActions from '../AssetActions'
import { useUserPreferences } from '../../../providers/UserPreferences'
import Pricing from './Pricing'
@@ -12,10 +11,12 @@ import { useAsset } from '../../../providers/Asset'
import Alert from '../../atoms/Alert'
import Button from '../../atoms/Button'
import Edit from '../AssetActions/Edit'
+import EditComputeDataset from '../AssetActions/Edit/EditComputeDataset'
import DebugOutput from '../../atoms/DebugOutput'
import MetaMain from './MetaMain'
import EditHistory from './EditHistory'
import { useWeb3 } from '../../../providers/Web3'
+import styles from './index.module.css'
export interface AssetContentProps {
path?: string
@@ -46,7 +47,9 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
const { owner, isInPurgatory, purgatoryData } = useAsset()
const [showPricing, setShowPricing] = useState(false)
const [showEdit, setShowEdit] = useState()
+ const [showEditCompute, setShowEditCompute] = useState()
const { ddo, price, metadata } = useAsset()
+
const isOwner = accountId === owner
useEffect(() => {
@@ -60,8 +63,15 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
setShowEdit(true)
}
+ function handleEditComputeButton() {
+ window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
+ setShowEditCompute(true)
+ }
+
return showEdit ? (
+ ) : showEditCompute ? (
+
) : (
@@ -91,6 +101,18 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
+ {ddo.findServiceByType('compute') && (
+ <>
+ |
+
+ >
+ )}
)}
>
diff --git a/src/hooks/usePublish.ts b/src/hooks/usePublish.ts
index 6ef5cbda8..bf0192b3b 100644
--- a/src/hooks/usePublish.ts
+++ b/src/hooks/usePublish.ts
@@ -113,8 +113,9 @@ function usePublish(): UsePublish {
servers
)
const origComputePrivacy: ServiceComputePrivacy = {
- allowRawAlgorithm: true,
+ allowRawAlgorithm: false,
allowNetworkAccess: false,
+ allowAllPublishedAlgorithms: false,
publisherTrustedAlgorithms: []
}
const computeService = ocean.compute.createComputeService(
diff --git a/src/models/FormEditComputeDataset.ts b/src/models/FormEditComputeDataset.ts
new file mode 100644
index 000000000..6d517def8
--- /dev/null
+++ b/src/models/FormEditComputeDataset.ts
@@ -0,0 +1,30 @@
+import { ServiceComputePrivacy } from '@oceanprotocol/lib'
+import * as Yup from 'yup'
+
+export interface ComputePrivacyForm {
+ allowAllPublishedAlgorithms: boolean
+ publisherTrustedAlgorithms: string[]
+}
+
+export const validationSchema: Yup.SchemaOf = Yup.object().shape(
+ {
+ allowAllPublishedAlgorithms: Yup.boolean().nullable(),
+ publisherTrustedAlgorithms: Yup.array().nullable()
+ }
+)
+
+export function getInitialValues(
+ compute: ServiceComputePrivacy
+): ComputePrivacyForm {
+ // TODO: ocean.js needs allowAllAlgoritms setting
+ const { allowAllPublishedAlgorithms, publisherTrustedAlgorithms } = compute
+
+ const publisherTrustedAlgorithmsForForm = (
+ publisherTrustedAlgorithms || []
+ ).map((algo) => algo.did)
+
+ return {
+ allowAllPublishedAlgorithms,
+ publisherTrustedAlgorithms: publisherTrustedAlgorithmsForForm
+ }
+}
diff --git a/src/utils/aquarius.ts b/src/utils/aquarius.ts
index 0e88a7949..2aa4f277d 100644
--- a/src/utils/aquarius.ts
+++ b/src/utils/aquarius.ts
@@ -1,9 +1,18 @@
-import { DDO, DID, Logger } from '@oceanprotocol/lib'
+import {
+ Config,
+ DDO,
+ DID,
+ Logger,
+ publisherTrustedAlgorithm as PublisherTrustedAlgorithm
+} from '@oceanprotocol/lib/'
import {
QueryResult,
SearchQuery
} from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
+import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSelection'
import axios, { CancelToken, AxiosResponse } from 'axios'
+import web3 from 'web3'
+import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
// TODO: import directly from ocean.js somehow.
// Transforming Aquarius' direct response is needed for getting actual DDOs
@@ -97,3 +106,57 @@ export async function getAssetsNames(
}
}
}
+
+export async function getAlgorithmsForAssetSelection(
+ metadataCacheUri: string,
+ selectedAlgorithms?: PublisherTrustedAlgorithm[]
+): Promise {
+ const query = {
+ page: 1,
+ query: {
+ query_string: {
+ query: `(service.attributes.main.type:algorithm) -isInPurgatory:true`
+ }
+ },
+ sort: { created: -1 }
+ }
+ const source = axios.CancelToken.source()
+ const didList: string[] = []
+ const priceList: any = {}
+ const result = await queryMetadata(
+ query as any,
+ metadataCacheUri,
+ source.token
+ )
+ result?.results?.forEach((ddo: DDO) => {
+ const did: string = web3.utils
+ .toChecksumAddress(ddo.dataToken)
+ .replace('0x', 'did:op:')
+ didList.push(did)
+ priceList[did] = ddo.price.value
+ })
+ const ddoNames = await getAssetsNames(didList, metadataCacheUri, source.token)
+ const algorithmList: AssetSelectionAsset[] = []
+ didList?.forEach((did: string) => {
+ let selected = false
+ selectedAlgorithms?.forEach((algorithm: PublisherTrustedAlgorithm) => {
+ if (algorithm.did === did) {
+ selected = true
+ }
+ })
+ selected
+ ? algorithmList.unshift({
+ did: did,
+ name: ddoNames[did],
+ price: priceList[did],
+ checked: selected
+ })
+ : algorithmList.push({
+ did: did,
+ name: ddoNames[did],
+ price: priceList[did],
+ checked: selected
+ })
+ })
+ return algorithmList
+}
diff --git a/src/utils/compute.ts b/src/utils/compute.ts
new file mode 100644
index 000000000..c3483d916
--- /dev/null
+++ b/src/utils/compute.ts
@@ -0,0 +1,41 @@
+import {
+ DDO,
+ Ocean,
+ ServiceComputePrivacy,
+ publisherTrustedAlgorithm as PublisherTrustedAlgorithm
+} from '@oceanprotocol/lib'
+import { ComputePrivacyForm } from '../models/FormEditComputeDataset'
+
+export async function createTrustedAlgorithmList(
+ selectedAlgorithms: string[], // list of DIDs
+ ocean: Ocean
+): Promise {
+ const trustedAlgorithms = []
+
+ for (const selectedAlgorithm of selectedAlgorithms) {
+ const trustedAlgorithm = await ocean.compute.createPublisherTrustedAlgorithmfromDID(
+ selectedAlgorithm
+ )
+ trustedAlgorithms.push(trustedAlgorithm)
+ }
+ return trustedAlgorithms
+}
+
+export async function transformComputeFormToServiceComputePrivacy(
+ values: ComputePrivacyForm,
+ ocean: Ocean
+): Promise {
+ const { allowAllPublishedAlgorithms } = values
+ const publisherTrustedAlgorithms = values.allowAllPublishedAlgorithms
+ ? []
+ : await createTrustedAlgorithmList(values.publisherTrustedAlgorithms, ocean)
+
+ const privacy: ServiceComputePrivacy = {
+ allowNetworkAccess: false,
+ allowRawAlgorithm: false,
+ allowAllPublishedAlgorithms,
+ publisherTrustedAlgorithms
+ }
+
+ return privacy
+}