mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Merge branch 'main' into feature/multinetwork
This commit is contained in:
commit
30174e1a84
@ -10,4 +10,6 @@ GATSBY_NETWORK="rinkeby"
|
||||
#GATSBY_ANALYTICS_ID="xxx"
|
||||
#GATSBY_PORTIS_ID="xxx"
|
||||
#GATSBY_ALLOW_FIXED_PRICING="true"
|
||||
#GATSBY_ALLOW_DYNAMIC_PRICING="true"
|
||||
#GATSBY_ALLOW_DYNAMIC_PRICING="true"
|
||||
#GATSBY_ALLOW_ADVANCED_SETTINGS="true"
|
||||
#GATSBY_CREDENTIAL_TYPE="address"
|
||||
|
@ -50,5 +50,9 @@ module.exports = {
|
||||
// Used to show or hide the fixed and dynamic price options
|
||||
// tab to publishers during the price creation.
|
||||
allowFixedPricing: process.env.GATSBY_ALLOW_FIXED_PRICING || 'true',
|
||||
allowDynamicPricing: process.env.GATSBY_ALLOW_DYNAMIC_PRICING || 'true'
|
||||
allowDynamicPricing: process.env.GATSBY_ALLOW_DYNAMIC_PRICING || 'true',
|
||||
|
||||
// Used to show or hide advanced settings button in asset details page
|
||||
allowAdvancedSettings: process.env.GATSBY_ALLOW_ADVANCED_SETTINGS || 'false',
|
||||
credentialType: process.env.GATSBY_CREDENTIAL_TYPE || 'address'
|
||||
}
|
||||
|
31
content/pages/editAdvancedSettings.json
Normal file
31
content/pages/editAdvancedSettings.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"description": "Update advanced settings of this data set. Updating these settings will create an on-chain transaction you have to approve in your wallet.",
|
||||
"form": {
|
||||
"success": "🎉 Successfully updated. 🎉",
|
||||
"successAction": "Close",
|
||||
"error": "Updating DDO failed.",
|
||||
"data": [
|
||||
{
|
||||
"name": "allow",
|
||||
"label": "Allow ETH Address",
|
||||
"placeholder": "e.g. 0x12345678901234567890abcd",
|
||||
"help": "Enter ETH address and click ADD button to append the list. Only ETH address in allow list can consume this asset. If the list is empty means anyone can download or compute this asset",
|
||||
"type": "credentials"
|
||||
},
|
||||
{
|
||||
"name": "deny",
|
||||
"label": "Deny ETH Address",
|
||||
"placeholder": "e.g. 0x12345678901234567890abcd",
|
||||
"help": "Enter ETH address and click ADD button to append the list. If ETH address is fall under deny list, download or compute of this asset is denied",
|
||||
"type": "credentials"
|
||||
},
|
||||
{
|
||||
"name": "isOrderDisabled",
|
||||
"label": "Disable Consumption",
|
||||
"help": "Disable dataset being download or compute when dataset undergoing maintenance.",
|
||||
"type": "checkbox",
|
||||
"options": ["Disable"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
7
package-lock.json
generated
7
package-lock.json
generated
@ -43525,7 +43525,6 @@
|
||||
"node-abort-controller": "^2.0.0",
|
||||
"save-file": "^2.3.1",
|
||||
"uuid": "^8.3.2",
|
||||
"web3": "^1.3.5",
|
||||
"web3-eth-contract": "^1.3.6"
|
||||
}
|
||||
},
|
||||
@ -43600,7 +43599,6 @@
|
||||
"integrity": "sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@oclif/config": "^1.15.1",
|
||||
"@oclif/errors": "^1.3.3",
|
||||
"@oclif/parser": "^3.8.3",
|
||||
"@oclif/plugin-help": "^3",
|
||||
@ -65003,11 +65001,6 @@
|
||||
"clsx": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"reactjs-popup": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.4.tgz",
|
||||
"integrity": "sha512-G5jTXL2JkClKAYAdqedf+K9QvbNsFWvdbrXW1/vWiyanuCU/d7DtQzQux+uKOz2HeNVRsFQHvs7abs0Z7VLAhg=="
|
||||
},
|
||||
"read": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
|
||||
|
@ -12,6 +12,7 @@ import classNames from 'classnames/bind'
|
||||
import AssetSelection, {
|
||||
AssetSelectionAsset
|
||||
} from '../../molecules/FormFields/AssetSelection'
|
||||
import Credentials from '../../molecules/FormFields/Credential'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
@ -137,6 +138,8 @@ export default function InputElement({
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
case 'credentials':
|
||||
return <Credentials name={name} {...field} {...props} />
|
||||
default:
|
||||
return prefix || postfix ? (
|
||||
<div className={`${prefix ? styles.prefixGroup : styles.postfixGroup}`}>
|
||||
|
59
src/components/molecules/AssetComputeList.module.css
Normal file
59
src/components/molecules/AssetComputeList.module.css
Normal file
@ -0,0 +1,59 @@
|
||||
.display {
|
||||
composes: selection from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.display [class*='loaderWrap'] {
|
||||
margin: calc(var(--spacer) / 3);
|
||||
}
|
||||
|
||||
.scroll {
|
||||
composes: scroll from './FormFields/AssetSelection.module.css';
|
||||
margin-top: 0;
|
||||
border-top: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row {
|
||||
composes: row from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.row:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.row:hover {
|
||||
background-color: var(--background-content);
|
||||
}
|
||||
|
||||
.info {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
composes: title from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.hover:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.price {
|
||||
composes: price from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.price [class*='symbol'] {
|
||||
font-size: calc(var(--font-size-small) / 1.2) !important;
|
||||
}
|
||||
|
||||
.did {
|
||||
composes: did from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.empty {
|
||||
composes: empty from './FormFields/AssetSelection.module.css';
|
||||
}
|
49
src/components/molecules/AssetComputeList.tsx
Normal file
49
src/components/molecules/AssetComputeList.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import { Link } from 'gatsby'
|
||||
import PriceUnit from '../atoms/Price/PriceUnit'
|
||||
import Loader from '../atoms/Loader'
|
||||
import styles from './AssetComputeList.module.css'
|
||||
import { AssetSelectionAsset } from './FormFields/AssetSelection'
|
||||
|
||||
function Empty() {
|
||||
return <div className={styles.empty}>No assets found.</div>
|
||||
}
|
||||
|
||||
export default function AssetComputeSelection({
|
||||
assets
|
||||
}: {
|
||||
assets: AssetSelectionAsset[]
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className={styles.display}>
|
||||
<div className={styles.scroll}>
|
||||
{!assets ? (
|
||||
<Loader />
|
||||
) : assets && !assets.length ? (
|
||||
<Empty />
|
||||
) : (
|
||||
assets.map((asset: AssetSelectionAsset) => (
|
||||
<Link
|
||||
to={`/asset/${asset.did}`}
|
||||
className={styles.row}
|
||||
key={asset.did}
|
||||
>
|
||||
<div className={styles.info}>
|
||||
<h3 className={styles.title}>
|
||||
<Dotdotdot clamp={1} tagName="span">
|
||||
{asset.name}
|
||||
</Dotdotdot>
|
||||
</h3>
|
||||
<Dotdotdot clamp={1} tagName="code" className={styles.did}>
|
||||
{asset.symbol} | {asset.did}
|
||||
</Dotdotdot>
|
||||
</div>
|
||||
<PriceUnit price={asset.price} small className={styles.price} />
|
||||
</Link>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
.chip {
|
||||
border: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.buttonWrapper {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.crossButton {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.crossButton svg {
|
||||
display: inline-block;
|
||||
width: var(--font-size-large);
|
||||
height: var(--font-size-large);
|
||||
fill: var(--brand-pink);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
border-top: 1px solid var(--border-color);
|
||||
min-height: fit-content;
|
||||
max-height: 200px;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.credential {
|
||||
padding: 0;
|
||||
border: 1px solid var(--border-color);
|
||||
background-color: var(--background-highlight);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: var(--font-size-small);
|
||||
min-height: 200px;
|
||||
}
|
81
src/components/molecules/FormFields/Credential/index.tsx
Normal file
81
src/components/molecules/FormFields/Credential/index.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import { useField } from 'formik'
|
||||
import { InputProps } from '../../../atoms/Input'
|
||||
import React, { useState, ChangeEvent, FormEvent, useEffect } from 'react'
|
||||
import InputGroup from '../../../atoms/Input/InputGroup'
|
||||
import Button from '../../../atoms/Button'
|
||||
import styles from './Credential.module.css'
|
||||
import { isAddress } from 'web3-utils'
|
||||
import { toast } from 'react-toastify'
|
||||
import { ReactComponent as Cross } from '../../../../images/cross.svg'
|
||||
import InputElement from '../../../atoms/Input/InputElement'
|
||||
|
||||
export default function Credentials(props: InputProps) {
|
||||
const [field, meta, helpers] = useField(props.name)
|
||||
const [arrayInput, setArrayInput] = useState<string[]>(field.value || [])
|
||||
const [value, setValue] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
helpers.setValue(arrayInput)
|
||||
}, [arrayInput])
|
||||
|
||||
function handleDeleteChip(value: string) {
|
||||
const newInput = arrayInput.filter((input) => input !== value)
|
||||
setArrayInput(newInput)
|
||||
helpers.setValue(newInput)
|
||||
}
|
||||
|
||||
function handleAddValue(e: FormEvent<HTMLButtonElement>) {
|
||||
e.preventDefault()
|
||||
if (!isAddress(value)) {
|
||||
toast.error('Wallet address is invalid')
|
||||
return
|
||||
}
|
||||
if (arrayInput.includes(value)) {
|
||||
toast.error('Wallet address already added into list')
|
||||
return
|
||||
}
|
||||
setArrayInput((arrayInput) => [...arrayInput, value])
|
||||
setValue('')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.credential}>
|
||||
<InputGroup>
|
||||
<InputElement
|
||||
type="text"
|
||||
name="address"
|
||||
size="default"
|
||||
placeholder={props.placeholder}
|
||||
value={value}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(e.target.value)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
onClick={(e: FormEvent<HTMLButtonElement>) => handleAddValue(e)}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</InputGroup>
|
||||
<div className={styles.scroll}>
|
||||
{arrayInput &&
|
||||
arrayInput.map((value) => {
|
||||
return (
|
||||
<div className={styles.chip} key={value}>
|
||||
<code>{value}</code>
|
||||
<span className={styles.buttonWrapper}>
|
||||
<Button
|
||||
className={styles.crossButton}
|
||||
style="text"
|
||||
onClick={(even) => handleDeleteChip(value)}
|
||||
>
|
||||
<Cross />
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -28,7 +28,6 @@ import {
|
||||
ComputeAlgorithm,
|
||||
ComputeOutput
|
||||
} from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||
import { SearchQuery } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||
import axios from 'axios'
|
||||
import FormStartComputeDataset from './FormComputeDataset'
|
||||
@ -36,6 +35,8 @@ import styles from './index.module.css'
|
||||
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
||||
import Button from '../../../atoms/Button'
|
||||
import { secondsToString } from '../../../../utils/metadata'
|
||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
|
||||
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
||||
|
||||
const SuccessAction = () => (
|
||||
@ -361,6 +362,8 @@ export default function Compute({
|
||||
await checkPreviousOrders(ddo)
|
||||
setIsPublished(true)
|
||||
} catch (error) {
|
||||
await checkPreviousOrders(selectedAlgorithmAsset)
|
||||
await checkPreviousOrders(ddo)
|
||||
setError('Failed to start job!')
|
||||
Logger.error('[compute] Failed to start job: ', error.message)
|
||||
} finally {
|
||||
@ -376,10 +379,13 @@ export default function Compute({
|
||||
</div>
|
||||
|
||||
{type === 'algorithm' ? (
|
||||
<Alert
|
||||
text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!"
|
||||
state="info"
|
||||
/>
|
||||
<>
|
||||
<Alert
|
||||
text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!"
|
||||
state="info"
|
||||
/>
|
||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} />
|
||||
</>
|
||||
) : (
|
||||
<Formik
|
||||
initialValues={getInitialValues()}
|
||||
|
@ -5,7 +5,10 @@
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
width: auto;
|
||||
margin-left: -2rem;
|
||||
margin-right: -2rem;
|
||||
padding: 0 calc(var(--spacer)) 0 calc(var(--spacer));
|
||||
}
|
||||
|
||||
.filewrapper {
|
||||
|
@ -3,10 +3,8 @@ import { toast } from 'react-toastify'
|
||||
import { File as FileMetadata, DDO } from '@oceanprotocol/lib'
|
||||
import File from '../../atoms/File'
|
||||
import Price from '../../atoms/Price'
|
||||
import styles from './Consume.module.css'
|
||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||
import { useAsset } from '../../../providers/Asset'
|
||||
import { secondsToString } from '../../../utils/metadata'
|
||||
import { gql, useQuery } from '@apollo/client'
|
||||
import { OrdersData } from '../../../@types/apollo/OrdersData'
|
||||
import BigNumber from 'bignumber.js'
|
||||
@ -15,6 +13,9 @@ import { useWeb3 } from '../../../providers/Web3'
|
||||
import { usePricing } from '../../../hooks/usePricing'
|
||||
import { useConsume } from '../../../hooks/useConsume'
|
||||
import ButtonBuy from '../../atoms/ButtonBuy'
|
||||
import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute'
|
||||
import Web3Feedback from '../../molecules/Web3Feedback'
|
||||
import styles from './Consume.module.css'
|
||||
|
||||
const previousOrderQuery = gql`
|
||||
query PreviousOrder($id: String!, $account: String!) {
|
||||
@ -51,12 +52,11 @@ export default function Consume({
|
||||
const { isInPurgatory, price, type, isAssetNetwork } = useAsset()
|
||||
const { buyDT, pricingStepText, pricingError, pricingIsLoading } =
|
||||
usePricing()
|
||||
const { consumeStepText, consume, consumeError } = useConsume()
|
||||
const { consumeStepText, consume, consumeError, isLoading } = useConsume()
|
||||
const [isDisabled, setIsDisabled] = useState(true)
|
||||
const [hasDatatoken, setHasDatatoken] = useState(false)
|
||||
const [isConsumable, setIsConsumable] = useState(true)
|
||||
const [assetTimeout, setAssetTimeout] = useState('')
|
||||
|
||||
const { data } = useQuery<OrdersData>(previousOrderQuery, {
|
||||
variables: {
|
||||
id: ddo.dataToken?.toLowerCase(),
|
||||
@ -87,7 +87,7 @@ export default function Consume({
|
||||
|
||||
useEffect(() => {
|
||||
const { timeout } = ddo.findServiceByType('access').attributes.main
|
||||
setAssetTimeout(secondsToString(timeout))
|
||||
setAssetTimeout(timeout.toString())
|
||||
}, [ddo])
|
||||
|
||||
useEffect(() => {
|
||||
@ -125,22 +125,28 @@ export default function Consume({
|
||||
])
|
||||
|
||||
async function handleConsume() {
|
||||
!hasPreviousOrder && !hasDatatoken && (await buyDT('1', price, ddo))
|
||||
await consume(
|
||||
if (!hasPreviousOrder && !hasDatatoken) {
|
||||
const tx = await buyDT('1', price, ddo)
|
||||
if (tx === undefined) return
|
||||
}
|
||||
const error = await consume(
|
||||
ddo.id,
|
||||
ddo.dataToken,
|
||||
'access',
|
||||
appConfig.marketFeeAddress,
|
||||
previousOrderId
|
||||
)
|
||||
setHasPreviousOrder(true)
|
||||
error || setHasPreviousOrder(true)
|
||||
}
|
||||
|
||||
// Output errors in UI
|
||||
useEffect(() => {
|
||||
consumeError && toast.error(consumeError)
|
||||
}, [consumeError])
|
||||
|
||||
useEffect(() => {
|
||||
pricingError && toast.error(pricingError)
|
||||
}, [consumeError, pricingError])
|
||||
}, [pricingError])
|
||||
|
||||
const PurchaseButton = () => (
|
||||
<ButtonBuy
|
||||
@ -154,7 +160,7 @@ export default function Consume({
|
||||
assetTimeout={assetTimeout}
|
||||
assetType={type}
|
||||
stepText={consumeStepText || pricingStepText}
|
||||
isLoading={pricingIsLoading}
|
||||
isLoading={pricingIsLoading || isLoading}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -169,6 +175,12 @@ export default function Consume({
|
||||
{!isInPurgatory && <PurchaseButton />}
|
||||
</div>
|
||||
</div>
|
||||
{type === 'algorithm' && (
|
||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} />
|
||||
)}
|
||||
<footer className={styles.feedback}>
|
||||
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
|
||||
</footer>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
import { DDO, Credentials, CredentialType } from '@oceanprotocol/lib'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import { AdvancedSettingsForm } from '../../../../models/FormEditCredential'
|
||||
import { useOcean } from '../../../../providers/Ocean'
|
||||
import DebugOutput from '../../../atoms/DebugOutput'
|
||||
|
||||
export interface AdvancedSettings {
|
||||
credentail: Credentials
|
||||
isOrderDisabled: boolean
|
||||
}
|
||||
|
||||
export default function DebugEditCredential({
|
||||
values,
|
||||
ddo,
|
||||
credentialType
|
||||
}: {
|
||||
values: AdvancedSettingsForm
|
||||
ddo: DDO
|
||||
credentialType: CredentialType
|
||||
}): ReactElement {
|
||||
const { ocean } = useOcean()
|
||||
const [advancedSettings, setAdvancedSettings] = useState<AdvancedSettings>()
|
||||
|
||||
useEffect(() => {
|
||||
if (!ocean) return
|
||||
|
||||
async function transformValues() {
|
||||
const newDdo = await ocean.assets.updateCredentials(
|
||||
ddo,
|
||||
credentialType,
|
||||
values.allow,
|
||||
values.deny
|
||||
)
|
||||
setAdvancedSettings({
|
||||
credentail: newDdo.credentials,
|
||||
isOrderDisabled: values.isOrderDisabled
|
||||
})
|
||||
}
|
||||
transformValues()
|
||||
}, [values, ddo, ocean])
|
||||
|
||||
return (
|
||||
<>
|
||||
<DebugOutput title="Collected Form Values" output={values} />
|
||||
<DebugOutput title="Transformed Form Values" output={advancedSettings} />
|
||||
</>
|
||||
)
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
import { Formik } from 'formik'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import { useAsset } from '../../../../providers/Asset'
|
||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||
import styles from './index.module.css'
|
||||
import { Logger, CredentialType, DDO } from '@oceanprotocol/lib'
|
||||
import MetadataFeedback from '../../../molecules/MetadataFeedback'
|
||||
import { graphql, useStaticQuery } from 'gatsby'
|
||||
import { useWeb3 } from '../../../../providers/Web3'
|
||||
import { useOcean } from '../../../../providers/Ocean'
|
||||
import FormAdvancedSettings from './FormAdvancedSettings'
|
||||
import {
|
||||
AdvancedSettingsForm,
|
||||
getInitialValues,
|
||||
validationSchema
|
||||
} from '../../../../models/FormEditCredential'
|
||||
import DebugEditCredential from './DebugEditAdvancedSettings'
|
||||
import { useSiteMetadata } from '../../../../hooks/useSiteMetadata'
|
||||
|
||||
const contentQuery = graphql`
|
||||
query EditAvanceSettingsQuery {
|
||||
content: allFile(
|
||||
filter: { relativePath: { eq: "pages/editAdvancedSettings.json" } }
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
childPagesJson {
|
||||
description
|
||||
form {
|
||||
success
|
||||
successAction
|
||||
error
|
||||
data {
|
||||
name
|
||||
placeholder
|
||||
label
|
||||
help
|
||||
type
|
||||
options
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
function getDefaultCredentialType(credentialType: string): CredentialType {
|
||||
switch (credentialType) {
|
||||
case 'address':
|
||||
return CredentialType.address
|
||||
case 'credential3Box':
|
||||
return CredentialType.credential3Box
|
||||
default:
|
||||
return CredentialType.address
|
||||
}
|
||||
}
|
||||
|
||||
export default function EditAdvancedSettings({
|
||||
setShowEdit
|
||||
}: {
|
||||
setShowEdit: (show: boolean) => void
|
||||
}): ReactElement {
|
||||
const data = useStaticQuery(contentQuery)
|
||||
const content = data.content.edges[0].node.childPagesJson
|
||||
|
||||
const { debug } = useUserPreferences()
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean } = useOcean()
|
||||
const { metadata, ddo, refreshDdo } = useAsset()
|
||||
const [success, setSuccess] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const { appConfig } = useSiteMetadata()
|
||||
|
||||
const hasFeedback = error || success
|
||||
|
||||
const credentialType = getDefaultCredentialType(appConfig.credentialType)
|
||||
|
||||
async function handleSubmit(
|
||||
values: Partial<AdvancedSettingsForm>,
|
||||
resetForm: () => void
|
||||
) {
|
||||
try {
|
||||
let newDdo: DDO
|
||||
newDdo = await ocean.assets.updateCredentials(
|
||||
ddo,
|
||||
credentialType,
|
||||
values.allow,
|
||||
values.deny
|
||||
)
|
||||
|
||||
newDdo = await ocean.assets.editMetadata(newDdo, {
|
||||
status: {
|
||||
isOrderDisabled: values.isOrderDisabled
|
||||
}
|
||||
})
|
||||
|
||||
const storedddo = await ocean.assets.updateMetadata(newDdo, accountId)
|
||||
|
||||
if (!storedddo) {
|
||||
setError(content.form.error)
|
||||
Logger.error(content.form.error)
|
||||
return
|
||||
} else {
|
||||
setSuccess(content.form.success)
|
||||
resetForm()
|
||||
}
|
||||
} catch (error) {
|
||||
Logger.error(error.message)
|
||||
setError(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={getInitialValues(ddo, credentialType)}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={async (values, { resetForm }) => {
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||
await handleSubmit(values, resetForm)
|
||||
}}
|
||||
>
|
||||
{({ isSubmitting, values }) =>
|
||||
isSubmitting || hasFeedback ? (
|
||||
<MetadataFeedback
|
||||
title="Updating Data Set"
|
||||
error={error}
|
||||
success={success}
|
||||
setError={setError}
|
||||
successAction={{
|
||||
name: content.form.successAction,
|
||||
onClick: async () => {
|
||||
await refreshDdo()
|
||||
setShowEdit(false)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<p className={styles.description}>{content.description}</p>
|
||||
<article className={styles.grid}>
|
||||
<FormAdvancedSettings
|
||||
data={content.form.data}
|
||||
setShowEdit={setShowEdit}
|
||||
/>
|
||||
</article>
|
||||
|
||||
{debug === true && (
|
||||
<div className={styles.grid}>
|
||||
<DebugEditCredential
|
||||
values={values}
|
||||
ddo={ddo}
|
||||
credentialType={credentialType}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Formik>
|
||||
)
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
import React, { ChangeEvent, ReactElement } from 'react'
|
||||
import styles from './FormEditMetadata.module.css'
|
||||
import { Field, Form, FormikContextType, useFormikContext } from 'formik'
|
||||
import Button from '../../../atoms/Button'
|
||||
import Input from '../../../atoms/Input'
|
||||
import { FormFieldProps } from '../../../../@types/Form'
|
||||
import { useOcean } from '../../../../providers/Ocean'
|
||||
import { useWeb3 } from '../../../../providers/Web3'
|
||||
import { AdvancedSettingsForm } from '../../../../models/FormEditCredential'
|
||||
|
||||
export default function FormAdvancedSettings({
|
||||
data,
|
||||
setShowEdit
|
||||
}: {
|
||||
data: FormFieldProps[]
|
||||
setShowEdit: (show: boolean) => void
|
||||
}): ReactElement {
|
||||
const { accountId } = useWeb3()
|
||||
const { ocean, config } = useOcean()
|
||||
const {
|
||||
isValid,
|
||||
validateField,
|
||||
setFieldValue
|
||||
}: FormikContextType<Partial<AdvancedSettingsForm>> = useFormikContext()
|
||||
|
||||
function handleFieldChange(
|
||||
e: ChangeEvent<HTMLInputElement>,
|
||||
field: FormFieldProps
|
||||
) {
|
||||
validateField(field.name)
|
||||
if (e.target.type === 'checkbox')
|
||||
setFieldValue(field.name, e.target.checked)
|
||||
else setFieldValue(field.name, e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Form className={styles.form}>
|
||||
{data.map((field: FormFieldProps) => (
|
||||
<Field
|
||||
key={field.name}
|
||||
{...field}
|
||||
component={Input}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
handleFieldChange(e, field)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
|
||||
<footer className={styles.actions}>
|
||||
<Button style="primary" disabled={!ocean || !accountId || !isValid}>
|
||||
Submit
|
||||
</Button>
|
||||
<Button style="text" onClick={() => setShowEdit(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</footer>
|
||||
</Form>
|
||||
)
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
.datasetsContainer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
margin-left: -2rem;
|
||||
margin-right: -2rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.datasetsContainer div[class*='AssetSelection-module--selection'] {
|
||||
width: 100%;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.datasetsContainer .text {
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
text-align: center;
|
||||
color: var(--font-color-text);
|
||||
font-size: var(--font-size-base);
|
||||
font-family: var(--font-family-heading);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import styles from './AlgorithmDatasetsListForCompute.module.css'
|
||||
import { getAlgorithmDatasetsForCompute } from '../../../utils/aquarius'
|
||||
import { AssetSelectionAsset } from '../../molecules/FormFields/AssetSelection'
|
||||
import AssetComputeList from '../../molecules/AssetComputeList'
|
||||
import { useOcean } from '../../../providers/Ocean'
|
||||
import { useAsset } from '../../../providers/Asset'
|
||||
|
||||
export default function AlgorithmDatasetsListForCompute({
|
||||
algorithmDid
|
||||
}: {
|
||||
algorithmDid: string
|
||||
}): ReactElement {
|
||||
const { config } = useOcean()
|
||||
const { type } = useAsset()
|
||||
const [datasetsForCompute, setDatasetsForCompute] =
|
||||
useState<AssetSelectionAsset[]>()
|
||||
|
||||
useEffect(() => {
|
||||
async function getDatasetsAllowedForCompute() {
|
||||
const datasets = await getAlgorithmDatasetsForCompute(
|
||||
algorithmDid,
|
||||
config.metadataCacheUri
|
||||
)
|
||||
setDatasetsForCompute(datasets)
|
||||
}
|
||||
type === 'algorithm' && getDatasetsAllowedForCompute()
|
||||
}, [type])
|
||||
|
||||
return (
|
||||
<div className={styles.datasetsContainer}>
|
||||
<h3 className={styles.text}>Datasets algorithm is allowed to run on</h3>
|
||||
<AssetComputeList assets={datasetsForCompute} />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -6,12 +6,10 @@ import { useAsset } from '../../../providers/Asset'
|
||||
|
||||
export default function MetaFull(): ReactElement {
|
||||
const { ddo, metadata, isInPurgatory, type } = useAsset()
|
||||
const { algorithm } = ddo.findServiceByType('metadata').attributes.main
|
||||
|
||||
function DockerImage() {
|
||||
const algorithmContainer =
|
||||
ddo.findServiceByType('metadata').attributes.main.algorithm.container
|
||||
const { image } = algorithmContainer
|
||||
const { tag } = algorithmContainer
|
||||
const { image, tag } = algorithm.container
|
||||
return <span>{`${image}:${tag}`}</span>
|
||||
}
|
||||
|
||||
@ -25,7 +23,7 @@ export default function MetaFull(): ReactElement {
|
||||
content={<Publisher account={ddo?.publicKey[0].owner} />}
|
||||
/>
|
||||
|
||||
{type === 'algorithm' && (
|
||||
{type === 'algorithm' && algorithm && (
|
||||
<MetaItem title="Docker Image" content={<DockerImage />} />
|
||||
)}
|
||||
<MetaItem title="DID" content={<code>{ddo?.id}</code>} />
|
||||
|
@ -17,6 +17,8 @@ import MetaMain from './MetaMain'
|
||||
import EditHistory from './EditHistory'
|
||||
import { useWeb3 } from '../../../providers/Web3'
|
||||
import styles from './index.module.css'
|
||||
import EditAdvancedSettings from '../AssetActions/Edit/EditAdvancedSettings'
|
||||
import { useSiteMetadata } from '../../../hooks/useSiteMetadata'
|
||||
|
||||
export interface AssetContentProps {
|
||||
path?: string
|
||||
@ -48,8 +50,11 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
||||
const [showPricing, setShowPricing] = useState(false)
|
||||
const [showEdit, setShowEdit] = useState<boolean>()
|
||||
const [showEditCompute, setShowEditCompute] = useState<boolean>()
|
||||
const [showEditAdvancedSettings, setShowEditAdvancedSettings] =
|
||||
useState<boolean>()
|
||||
const [isOwner, setIsOwner] = useState(false)
|
||||
const { ddo, price, metadata, type } = useAsset()
|
||||
const { appConfig } = useSiteMetadata()
|
||||
|
||||
useEffect(() => {
|
||||
if (!accountId || !owner) return
|
||||
@ -70,10 +75,17 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
||||
setShowEditCompute(true)
|
||||
}
|
||||
|
||||
function handleEditAdvancedSettingsButton() {
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||
setShowEditAdvancedSettings(true)
|
||||
}
|
||||
|
||||
return showEdit ? (
|
||||
<Edit setShowEdit={setShowEdit} />
|
||||
) : showEditCompute ? (
|
||||
<EditComputeDataset setShowEdit={setShowEditCompute} />
|
||||
) : showEditAdvancedSettings ? (
|
||||
<EditAdvancedSettings setShowEdit={setShowEditAdvancedSettings} />
|
||||
) : (
|
||||
<article className={styles.grid}>
|
||||
<div>
|
||||
@ -103,6 +115,18 @@ export default function AssetContent(props: AssetContentProps): ReactElement {
|
||||
<Button style="text" size="small" onClick={handleEditButton}>
|
||||
Edit Metadata
|
||||
</Button>
|
||||
{appConfig.allowAdvancedSettings === 'true' && (
|
||||
<>
|
||||
<span className={styles.separator}>|</span>
|
||||
<Button
|
||||
style="text"
|
||||
size="small"
|
||||
onClick={handleEditAdvancedSettingsButton}
|
||||
>
|
||||
Edit Advanced Settings
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{ddo.findServiceByType('compute') && type === 'dataset' && (
|
||||
<>
|
||||
<span className={styles.separator}>|</span>
|
||||
|
@ -159,6 +159,8 @@ export default function PublishPage({
|
||||
values: initialValues as MetadataPublishFormDataset,
|
||||
status: 'empty'
|
||||
})
|
||||
// move user's focus to top of screen
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||
} catch (error) {
|
||||
setError(error.message)
|
||||
Logger.error(error.message)
|
||||
@ -173,14 +175,10 @@ export default function PublishPage({
|
||||
): Promise<void> {
|
||||
const metadata = transformPublishAlgorithmFormToMetadata(values)
|
||||
const timeout = mapTimeoutStringToSeconds(values.timeout)
|
||||
|
||||
// TODO: put back check once #572 is resolved
|
||||
// https://github.com/oceanprotocol/market/issues/572
|
||||
const validDockerImage = true
|
||||
// const validDockerImage =
|
||||
// values.dockerImage === 'custom image'
|
||||
// ? await validateDockerImage(values.image, values.containerTag)
|
||||
// : true
|
||||
const validDockerImage =
|
||||
values.dockerImage === 'custom image'
|
||||
? await validateDockerImage(values.image, values.containerTag)
|
||||
: true
|
||||
try {
|
||||
if (validDockerImage) {
|
||||
Logger.log('Publish algorithm with ', metadata, values.dataTokenOptions)
|
||||
@ -208,6 +206,10 @@ export default function PublishPage({
|
||||
values: initialValuesAlgorithm as MetadataPublishFormAlgorithm,
|
||||
status: 'empty'
|
||||
})
|
||||
// move user's focus to top of screen
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||
} else {
|
||||
document.getElementById('image').scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error.message)
|
||||
@ -228,8 +230,6 @@ export default function PublishPage({
|
||||
: validationSchemaAlgorithm
|
||||
}
|
||||
onSubmit={async (values, { resetForm }) => {
|
||||
// move user's focus to top of screen
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||
// kick off publishing
|
||||
publishType === 'dataset'
|
||||
? await handleSubmit(values, resetForm)
|
||||
|
@ -11,7 +11,7 @@ export default function PageTemplateAssetDetails({
|
||||
}: {
|
||||
uri: string
|
||||
}): ReactElement {
|
||||
const { ddo, title, error, isInPurgatory } = useAsset()
|
||||
const { ddo, title, error, isInPurgatory, loading } = useAsset()
|
||||
const [pageTitle, setPageTitle] = useState<string>()
|
||||
|
||||
useEffect(() => {
|
||||
@ -23,7 +23,7 @@ export default function PageTemplateAssetDetails({
|
||||
setPageTitle(isInPurgatory ? '' : title)
|
||||
}, [ddo, error, isInPurgatory, title])
|
||||
|
||||
return ddo && pageTitle !== undefined ? (
|
||||
return ddo && pageTitle !== undefined && !loading ? (
|
||||
<Page title={pageTitle} uri={uri}>
|
||||
<Router basepath="/asset">
|
||||
<AssetContent path=":did" />
|
||||
|
@ -11,7 +11,7 @@ interface UseConsume {
|
||||
serviceType: ServiceType,
|
||||
marketFeeAddress: string,
|
||||
orderId?: string
|
||||
) => Promise<void>
|
||||
) => Promise<string>
|
||||
consumeStep?: number
|
||||
consumeStepText?: string
|
||||
consumeError?: string
|
||||
@ -37,7 +37,7 @@ function useConsume(): UseConsume {
|
||||
serviceType: ServiceType = 'access',
|
||||
marketFeeAddress: string,
|
||||
orderId?: string
|
||||
): Promise<void> {
|
||||
): Promise<string> {
|
||||
if (!ocean || !account || !accountId) return
|
||||
|
||||
setIsLoading(true)
|
||||
@ -53,19 +53,25 @@ function useConsume(): UseConsume {
|
||||
)
|
||||
if (parseFloat(userOwnedTokens) < 1) {
|
||||
setConsumeError('Not enough datatokens')
|
||||
return 'Not enough datatokens'
|
||||
} else {
|
||||
setStep(1)
|
||||
orderId = await ocean.assets.order(
|
||||
did as string,
|
||||
serviceType,
|
||||
accountId,
|
||||
undefined,
|
||||
marketFeeAddress,
|
||||
undefined,
|
||||
false
|
||||
)
|
||||
Logger.log('order created', orderId)
|
||||
setStep(2)
|
||||
try {
|
||||
orderId = await ocean.assets.order(
|
||||
did as string,
|
||||
serviceType,
|
||||
accountId,
|
||||
undefined,
|
||||
marketFeeAddress,
|
||||
undefined,
|
||||
false
|
||||
)
|
||||
Logger.log('order created', orderId)
|
||||
setStep(2)
|
||||
} catch (error) {
|
||||
setConsumeError(error.message)
|
||||
return error.message
|
||||
}
|
||||
}
|
||||
}
|
||||
setStep(3)
|
||||
@ -81,11 +87,13 @@ function useConsume(): UseConsume {
|
||||
} catch (error) {
|
||||
setConsumeError(error.message)
|
||||
Logger.error(error)
|
||||
return error.message
|
||||
} finally {
|
||||
setConsumeStep(undefined)
|
||||
setConsumeStepText(undefined)
|
||||
setIsLoading(false)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
return { consume, consumeStep, consumeStepText, consumeError, isLoading }
|
||||
|
@ -29,6 +29,8 @@ interface UseSiteMetadata {
|
||||
portisId: string
|
||||
allowFixedPricing: string
|
||||
allowDynamicPricing: string
|
||||
allowAdvancedSettings: string
|
||||
credentialType: string
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,6 +65,8 @@ const query = graphql`
|
||||
portisId
|
||||
allowFixedPricing
|
||||
allowDynamicPricing
|
||||
allowAdvancedSettings
|
||||
credentialType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
src/images/cross.svg
Normal file
3
src/images/cross.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 364 B |
72
src/models/FormEditCredential.ts
Normal file
72
src/models/FormEditCredential.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import {
|
||||
CredentialAction,
|
||||
Credential,
|
||||
Credentials,
|
||||
CredentialType,
|
||||
DDO
|
||||
} from '@oceanprotocol/lib'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
export interface AdvancedSettingsForm {
|
||||
allow: string[]
|
||||
deny: string[]
|
||||
isOrderDisabled: boolean
|
||||
}
|
||||
|
||||
export const validationSchema: Yup.SchemaOf<AdvancedSettingsForm> =
|
||||
Yup.object().shape({
|
||||
allow: Yup.array().nullable(),
|
||||
deny: Yup.array().nullable(),
|
||||
isOrderDisabled: Yup.boolean().nullable()
|
||||
})
|
||||
|
||||
function getCredentialList(
|
||||
credential: Credential[],
|
||||
credentialType: CredentialType
|
||||
): string[] {
|
||||
const credentialByType = credential.find(
|
||||
(credential) => credential.type === credentialType
|
||||
)
|
||||
return credentialByType.value && credentialByType.value.length > 0
|
||||
? credentialByType.value
|
||||
: []
|
||||
}
|
||||
|
||||
function getAssetCredentials(
|
||||
credentials: Credentials,
|
||||
credentialType: CredentialType,
|
||||
credentialAction: CredentialAction
|
||||
): string[] {
|
||||
if (!credentials) return []
|
||||
|
||||
if (credentialAction === 'allow') {
|
||||
return credentials.allow
|
||||
? getCredentialList(credentials.allow, credentialType)
|
||||
: []
|
||||
}
|
||||
return credentials.deny
|
||||
? getCredentialList(credentials.deny, credentialType)
|
||||
: []
|
||||
}
|
||||
|
||||
export function getInitialValues(
|
||||
ddo: DDO,
|
||||
credentailType: CredentialType
|
||||
): AdvancedSettingsForm {
|
||||
const allowCredential = getAssetCredentials(
|
||||
ddo.credentials,
|
||||
credentailType,
|
||||
'allow'
|
||||
)
|
||||
const denyCredential = getAssetCredentials(
|
||||
ddo.credentials,
|
||||
credentailType,
|
||||
'deny'
|
||||
)
|
||||
const metadata = ddo.findServiceByType('metadata')
|
||||
return {
|
||||
allow: allowCredential,
|
||||
deny: denyCredential,
|
||||
isOrderDisabled: metadata.attributes?.status?.isOrderDisabled || false
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ interface AssetProviderValue {
|
||||
error?: string
|
||||
refreshInterval: number
|
||||
isAssetNetwork: boolean
|
||||
loading: boolean
|
||||
refreshDdo: (token?: CancelToken) => Promise<void>
|
||||
}
|
||||
|
||||
@ -59,10 +60,12 @@ function AssetProvider({
|
||||
const [owner, setOwner] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const [type, setType] = useState<MetadataMain['type']>()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [isAssetNetwork, setIsAssetNetwork] = useState<boolean>()
|
||||
|
||||
const fetchDdo = async (token?: CancelToken) => {
|
||||
Logger.log('[asset] Init asset, get DDO')
|
||||
setLoading(true)
|
||||
const ddo = await retrieveDDO(
|
||||
asset as string,
|
||||
appConfig.metadataCacheUri,
|
||||
@ -76,13 +79,16 @@ function AssetProvider({
|
||||
} else {
|
||||
setError(undefined)
|
||||
}
|
||||
setLoading(false)
|
||||
return ddo
|
||||
}
|
||||
|
||||
const refreshDdo = async (token?: CancelToken) => {
|
||||
setLoading(true)
|
||||
const ddo = await fetchDdo(token)
|
||||
Logger.debug('[asset] Got DDO', ddo)
|
||||
setDDO(ddo)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
//
|
||||
@ -123,7 +129,7 @@ function AssetProvider({
|
||||
|
||||
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
||||
if (!ddo) return
|
||||
|
||||
setLoading(true)
|
||||
const returnedPrice = await getPrice(ddo)
|
||||
setPrice({ ...returnedPrice })
|
||||
|
||||
@ -137,6 +143,7 @@ function AssetProvider({
|
||||
|
||||
setIsInPurgatory(ddo.isInPurgatory === 'true')
|
||||
await setPurgatory(ddo.id)
|
||||
setLoading(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@ -169,6 +176,7 @@ function AssetProvider({
|
||||
isInPurgatory,
|
||||
purgatoryData,
|
||||
refreshInterval,
|
||||
loading,
|
||||
refreshDdo,
|
||||
isAssetNetwork
|
||||
} as AssetProviderValue
|
||||
|
@ -14,6 +14,17 @@ import { PriceList, getAssetsPriceList } from './subgraph'
|
||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||
import { DDO_TEMPORARY } from '../providers/Ocean'
|
||||
|
||||
function getQueryForAlgorithmDatasets(algorithmDid: string) {
|
||||
return {
|
||||
query: {
|
||||
query_string: {
|
||||
query: `service.attributes.main.privacy.publisherTrustedAlgorithms.did:${algorithmDid}`
|
||||
}
|
||||
},
|
||||
sort: { created: -1 }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: import directly from ocean.js somehow.
|
||||
// Transforming Aquarius' direct response is needed for getting actual DDOs
|
||||
// and not just strings of DDOs. For now, taken from
|
||||
@ -151,3 +162,33 @@ export async function transformDDOToAssetSelection(
|
||||
})
|
||||
return algorithmList
|
||||
}
|
||||
|
||||
export async function getAlgorithmDatasetsForCompute(
|
||||
algorithmId: string,
|
||||
metadataCacheUri: string
|
||||
): Promise<AssetSelectionAsset[]> {
|
||||
const source = axios.CancelToken.source()
|
||||
const computeDatasets = await queryMetadata(
|
||||
getQueryForAlgorithmDatasets(algorithmId),
|
||||
metadataCacheUri,
|
||||
source.token
|
||||
)
|
||||
const computeDatasetsForCurrentAlgorithm: DDO[] = []
|
||||
computeDatasets.results.forEach((data: DDO) => {
|
||||
const algorithm = data
|
||||
.findServiceByType('compute')
|
||||
.attributes.main.privacy.publisherTrustedAlgorithms.find(
|
||||
(algo) => algo.did === algorithmId
|
||||
)
|
||||
algorithm && computeDatasetsForCurrentAlgorithm.push(data)
|
||||
})
|
||||
if (computeDatasetsForCurrentAlgorithm.length === 0) {
|
||||
return []
|
||||
}
|
||||
const datasets = await transformDDOToAssetSelection(
|
||||
computeDatasetsForCurrentAlgorithm,
|
||||
metadataCacheUri,
|
||||
[]
|
||||
)
|
||||
return datasets
|
||||
}
|
||||
|
@ -136,10 +136,18 @@ async function isDockerHubImageValid(
|
||||
tag: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://hub.docker.com/v2/repositories/${image}/tags/${tag}`
|
||||
const response = await axios.post(
|
||||
`https://dockerhub-proxy.oceanprotocol.com`,
|
||||
{
|
||||
image,
|
||||
tag
|
||||
}
|
||||
)
|
||||
if (!response || response.status !== 200 || !response.data) {
|
||||
if (
|
||||
!response ||
|
||||
response.status !== 200 ||
|
||||
response.data.status !== 'success'
|
||||
) {
|
||||
toast.error(
|
||||
'Could not fetch docker hub image info. Please check image name and tag and try again'
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user