1
0
mirror of https://github.com/oceanprotocol/market.git synced 2024-12-02 05:57:29 +01:00

declarative metadata & services form setup, prepare transformPublishFormToDdo method

This commit is contained in:
Matthias Kretschmann 2021-10-28 10:38:40 +01:00
parent cdbe413765
commit 66adc097b6
Signed by: m
GPG Key ID: 606EEEF3C479A91F
27 changed files with 351 additions and 334 deletions

View File

@ -1,6 +1,6 @@
{ {
"metadata": { "metadata": {
"title": "Enter details", "title": "Metadata",
"fields": [ "fields": [
{ {
"name": "name", "name": "name",
@ -38,7 +38,7 @@
] ]
}, },
"services": { "services": {
"title": "Create services", "title": "Access",
"fields": [ "fields": [
{ {
"name": "dataTokenOptions", "name": "dataTokenOptions",
@ -87,13 +87,12 @@
"label": "Custom Provider URL", "label": "Custom Provider URL",
"type": "providerUri", "type": "providerUri",
"help": "Enter the URL for your custom provider or leave blank to use the default provider. [Learn more](https://github.com/oceanprotocol/provider/).", "help": "Enter the URL for your custom provider or leave blank to use the default provider. [Learn more](https://github.com/oceanprotocol/provider/).",
"placeholder": "https://provider.polygon.oceanprotocol.com/", "placeholder": "e.g. https://provider.polygon.oceanprotocol.com/"
"advanced": true
} }
] ]
}, },
"pricing": { "pricing": {
"title": "Create pricing schema", "title": "Pricing",
"fields": [ "fields": [
{ {
"name": "dummy content, as content is defined under 'create' key in ../price.json" "name": "dummy content, as content is defined under 'create' key in ../price.json"

16
src/@types/Form.d.ts vendored
View File

@ -1,13 +1,8 @@
import { AssetSelectionAsset } from '@shared/Form/FormFields/AssetSelection' interface FormFieldContent {
// declaring into global scope to be able to use this as
// ambiant types despite the above imports
declare global {
interface FormFieldProps {
label: string label: string
name: string name: string
type?: string type?: string
options?: string[] | AssetSelectionAsset[] options?: string[]
sortOptions?: boolean sortOptions?: boolean
required?: boolean required?: boolean
multiple?: boolean multiple?: boolean
@ -19,11 +14,10 @@ declare global {
disclaimer?: string disclaimer?: string
disclaimerValues?: string[] disclaimerValues?: string[]
advanced?: boolean advanced?: boolean
} }
interface FormStepContent { interface FormStepContent {
title: string title: string
description?: string description?: string
fields: FormFieldProps[] fields: FormFieldContent[]
}
} }

View File

@ -7,6 +7,11 @@ import {
// declaring into global scope to be able to use this as // declaring into global scope to be able to use this as
// ambiant types despite the above imports // ambiant types despite the above imports
declare global { declare global {
interface DdoMarket {
metadata: any
services: any[]
}
interface AdditionalInformationMarket extends AdditionalInformation { interface AdditionalInformationMarket extends AdditionalInformation {
termsAndConditions: boolean termsAndConditions: boolean
} }

97
src/@utils/ddo.ts Normal file
View File

@ -0,0 +1,97 @@
import axios from 'axios'
import { toast } from 'react-toastify'
import isUrl from 'is-url-superb'
import slugify from 'slugify'
import { MetadataAlgorithm, Logger } from '@oceanprotocol/lib'
export function dateToStringNoMS(date: Date): string {
return date.toISOString().replace(/\.[0-9]{3}Z/, 'Z')
}
export function transformTags(value: string): string[] {
const originalTags = value?.split(',')
const transformedTags = originalTags?.map((tag) => slugify(tag).toLowerCase())
return transformedTags
}
export function mapTimeoutStringToSeconds(timeout: string): number {
switch (timeout) {
case 'Forever':
return 0
case '1 day':
return 86400
case '1 week':
return 604800
case '1 month':
return 2630000
case '1 year':
return 31556952
default:
return 0
}
}
function numberEnding(number: number): string {
return number > 1 ? 's' : ''
}
export function secondsToString(numberOfSeconds: number): string {
if (numberOfSeconds === 0) return 'Forever'
const years = Math.floor(numberOfSeconds / 31536000)
const months = Math.floor((numberOfSeconds %= 31536000) / 2630000)
const weeks = Math.floor((numberOfSeconds %= 31536000) / 604800)
const days = Math.floor((numberOfSeconds %= 604800) / 86400)
const hours = Math.floor((numberOfSeconds %= 86400) / 3600)
const minutes = Math.floor((numberOfSeconds %= 3600) / 60)
const seconds = numberOfSeconds % 60
return years
? `${years} year${numberEnding(years)}`
: months
? `${months} month${numberEnding(months)}`
: weeks
? `${weeks} week${numberEnding(weeks)}`
: days
? `${days} day${numberEnding(days)}`
: hours
? `${hours} hour${numberEnding(hours)}`
: minutes
? `${minutes} minute${numberEnding(minutes)}`
: seconds
? `${seconds} second${numberEnding(seconds)}`
: 'less than a second'
}
export function checkIfTimeoutInPredefinedValues(
timeout: string,
timeoutOptions: string[]
): boolean {
if (timeoutOptions.indexOf(timeout) > -1) {
return true
}
return false
}
export function getAlgorithmComponent(
image: string,
containerTag: string,
entrypoint: string,
algorithmLanguage: string
): MetadataAlgorithm {
return {
language: algorithmLanguage,
format: 'docker-image',
version: '0.1',
container: {
entrypoint: entrypoint,
image: image,
tag: containerTag
}
}
}
export function getAlgorithmFileExtension(fileUrl: string): string {
const splitedFileUrl = fileUrl.split('.')
return splitedFileUrl[splitedFileUrl.length - 1]
}

63
src/@utils/docker.ts Normal file
View File

@ -0,0 +1,63 @@
import { Logger } from '@oceanprotocol/lib'
import axios from 'axios'
import isUrl from 'is-url-superb'
import { toast } from 'react-toastify'
async function isDockerHubImageValid(
image: string,
tag: string
): Promise<boolean> {
try {
const response = await axios.post(
`https://dockerhub-proxy.oceanprotocol.com`,
{ image, tag }
)
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'
)
return false
}
return true
} catch (error) {
Logger.error(error.message)
toast.error(
'Could not fetch docker hub image info. Please check image name and tag and try again'
)
return false
}
}
async function is3rdPartyImageValid(imageURL: string): Promise<boolean> {
try {
const response = await axios.head(imageURL)
if (!response || response.status !== 200) {
toast.error(
'Could not fetch docker image info. Please check URL and try again'
)
return false
}
return true
} catch (error) {
Logger.error(error.message)
toast.error(
'Could not fetch docker image info. Please check URL and try again'
)
return false
}
}
export async function validateDockerImage(
dockerImage: string,
tag: string
): Promise<boolean> {
const isValid = isUrl(dockerImage)
? await is3rdPartyImageValid(dockerImage)
: await isDockerHubImageValid(dockerImage, tag)
return isValid
}

View File

@ -1,237 +0,0 @@
import axios from 'axios'
import { toast } from 'react-toastify'
import isUrl from 'is-url-superb'
import slugify from 'slugify'
import { DDO, MetadataAlgorithm, Logger } from '@oceanprotocol/lib'
import { FormPublishData } from '../components/Publish/_types'
export function dateToStringNoMS(date: Date): string {
return date.toISOString().replace(/\.[0-9]{3}Z/, 'Z')
}
export function transformTags(value: string): string[] {
const originalTags = value?.split(',')
const transformedTags = originalTags?.map((tag) => slugify(tag).toLowerCase())
return transformedTags
}
export function mapTimeoutStringToSeconds(timeout: string): number {
switch (timeout) {
case 'Forever':
return 0
case '1 day':
return 86400
case '1 week':
return 604800
case '1 month':
return 2630000
case '1 year':
return 31556952
default:
return 0
}
}
function numberEnding(number: number): string {
return number > 1 ? 's' : ''
}
export function secondsToString(numberOfSeconds: number): string {
if (numberOfSeconds === 0) return 'Forever'
const years = Math.floor(numberOfSeconds / 31536000)
const months = Math.floor((numberOfSeconds %= 31536000) / 2630000)
const weeks = Math.floor((numberOfSeconds %= 31536000) / 604800)
const days = Math.floor((numberOfSeconds %= 604800) / 86400)
const hours = Math.floor((numberOfSeconds %= 86400) / 3600)
const minutes = Math.floor((numberOfSeconds %= 3600) / 60)
const seconds = numberOfSeconds % 60
return years
? `${years} year${numberEnding(years)}`
: months
? `${months} month${numberEnding(months)}`
: weeks
? `${weeks} week${numberEnding(weeks)}`
: days
? `${days} day${numberEnding(days)}`
: hours
? `${hours} hour${numberEnding(hours)}`
: minutes
? `${minutes} minute${numberEnding(minutes)}`
: seconds
? `${seconds} second${numberEnding(seconds)}`
: 'less than a second'
}
export function checkIfTimeoutInPredefinedValues(
timeout: string,
timeoutOptions: string[]
): boolean {
if (timeoutOptions.indexOf(timeout) > -1) {
return true
}
return false
}
function getAlgorithmComponent(
image: string,
containerTag: string,
entrypoint: string,
algorithmLanguace: string
): MetadataAlgorithm {
return {
language: algorithmLanguace,
format: 'docker-image',
version: '0.1',
container: {
entrypoint: entrypoint,
image: image,
tag: containerTag
}
}
}
function getAlgorithmFileExtension(fileUrl: string): string {
const splitedFileUrl = fileUrl.split('.')
return splitedFileUrl[splitedFileUrl.length - 1]
}
// export function transformPublishFormToMetadata(
// {
// name,
// author,
// description,
// tags,
// links,
// termsAndConditions,
// files
// }: Partial<FormPublishData>,
// ddo?: DDO
// ): MetadataMarket {
// const currentTime = dateToStringNoMS(new Date())
// const metadata: MetadataMarket = {
// main: {
// name,
// author,
// dateCreated: ddo ? ddo.created : currentTime,
// datePublished: '',
// files: typeof files !== 'string' && files,
// license: 'https://market.oceanprotocol.com/terms'
// },
// additionalInformation: {
// description,
// tags: transformTags(tags),
// links: typeof links !== 'string' ? links : [],
// termsAndConditions
// }
// }
// return metadata
// }
// async function isDockerHubImageValid(
// image: string,
// tag: string
// ): Promise<boolean> {
// try {
// const response = await axios.post(
// `https://dockerhub-proxy.oceanprotocol.com`,
// {
// image,
// tag
// }
// )
// 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'
// )
// return false
// }
// return true
// } catch (error) {
// Logger.error(error.message)
// toast.error(
// 'Could not fetch docker hub image info. Please check image name and tag and try again'
// )
// return false
// }
// }
async function is3rdPartyImageValid(imageURL: string): Promise<boolean> {
try {
const response = await axios.head(imageURL)
if (!response || response.status !== 200) {
toast.error(
'Could not fetch docker image info. Please check URL and try again'
)
return false
}
return true
} catch (error) {
Logger.error(error.message)
toast.error(
'Could not fetch docker image info. Please check URL and try again'
)
return false
}
}
// export async function validateDockerImage(
// dockerImage: string,
// tag: string
// ): Promise<boolean> {
// const isValid = isUrl(dockerImage)
// ? await is3rdPartyImageValid(dockerImage)
// : await isDockerHubImageValid(dockerImage, tag)
// return isValid
// }
// export function transformPublishAlgorithmFormToMetadata(
// {
// name,
// author,
// description,
// tags,
// image,
// containerTag,
// entrypoint,
// termsAndConditions,
// files
// }: Partial<FormPublishData>,
// ddo?: DDO
// ): MetadataMarket {
// const currentTime = dateToStringNoMS(new Date())
// const fileUrl = typeof files !== 'string' && files[0].url
// const algorithmLanguage = getAlgorithmFileExtension(fileUrl)
// const algorithm = getAlgorithmComponent(
// image,
// containerTag,
// entrypoint,
// algorithmLanguage
// )
// const metadata: MetadataMarket = {
// main: {
// name,
// type: 'algorithm',
// author,
// dateCreated: ddo ? ddo.created : currentTime,
// files: typeof files !== 'string' && files,
// license: 'https://market.oceanprotocol.com/terms',
// algorithm
// },
// additionalInformation: {
// description,
// tags: transformTags(tags),
// termsAndConditions
// }
// }
// return metadata
// }

View File

@ -8,7 +8,7 @@ export default function AdvancedSettings(prop: {
content: FormStepContent content: FormStepContent
handleFieldChange: ( handleFieldChange: (
e: ChangeEvent<HTMLInputElement>, e: ChangeEvent<HTMLInputElement>,
field: FormFieldProps field: FormFieldContent
) => void ) => void
}): ReactElement { }): ReactElement {
const [showAdvancedSettings, setShowAdvancedSettings] = const [showAdvancedSettings, setShowAdvancedSettings] =
@ -30,7 +30,7 @@ export default function AdvancedSettings(prop: {
</Button> </Button>
{showAdvancedSettings && {showAdvancedSettings &&
prop.content.fields.map( prop.content.fields.map(
(field: FormFieldProps) => (field: FormFieldContent) =>
field.advanced === true && ( field.advanced === true && (
<Field <Field
key={field.name} key={field.name}

View File

@ -47,7 +47,7 @@ export default function BoxSelection({
type="radio" type="radio"
className={styleClassesInput} className={styleClassesInput}
defaultChecked={value.checked} defaultChecked={value.checked}
onChange={(event) => handleChange(event)} // onChange={(event) => handleChange(event)}
{...props} {...props}
disabled={disabled} disabled={disabled}
value={value.name} value={value.name}

View File

@ -15,10 +15,10 @@ export default function Datatoken(props: InputProps): ReactElement {
// Generate new DT name & symbol on first mount // Generate new DT name & symbol on first mount
useEffect(() => { useEffect(() => {
if (field.value.name !== '') return if (field.value?.name !== '') return
generateName() generateName()
}, [field.value.name]) }, [field.value?.name])
return ( return (
<div className={styles.datatoken}> <div className={styles.datatoken}>

View File

@ -6,7 +6,7 @@ import styles from './Terms.module.css'
export default function Terms(props: InputProps): ReactElement { export default function Terms(props: InputProps): ReactElement {
const termsProps: InputProps = { const termsProps: InputProps = {
...props, ...props,
defaultChecked: props.value.toString() === 'true' defaultChecked: props.value?.toString() === 'true'
} }
return ( return (

View File

@ -1,11 +1,4 @@
import React, { import React, { ReactElement, ReactNode, useEffect, useState } from 'react'
FormEvent,
ChangeEvent,
ReactElement,
ReactNode,
useEffect,
useState
} from 'react'
import InputElement from './InputElement' import InputElement from './InputElement'
import Label from './Label' import Label from './Label'
import styles from './index.module.css' import styles from './index.module.css'
@ -17,8 +10,7 @@ import Markdown from '@shared/Markdown'
const cx = classNames.bind(styles) const cx = classNames.bind(styles)
export interface InputProps { export interface InputProps extends FieldInputProps<any> {
name: string
label?: string | ReactNode label?: string | ReactNode
placeholder?: string placeholder?: string
required?: boolean required?: boolean
@ -28,23 +20,7 @@ export interface InputProps {
options?: string[] options?: string[]
sortOptions?: boolean sortOptions?: boolean
additionalComponent?: ReactElement additionalComponent?: ReactElement
value?: string
onChange?(
e:
| FormEvent<HTMLInputElement>
| ChangeEvent<HTMLInputElement>
| ChangeEvent<HTMLSelectElement>
| ChangeEvent<HTMLTextAreaElement>
): void
onKeyPress?(
e:
| React.KeyboardEvent<HTMLInputElement>
| React.KeyboardEvent<HTMLInputElement>
| React.KeyboardEvent<HTMLSelectElement>
| React.KeyboardEvent<HTMLTextAreaElement>
): void
rows?: number rows?: number
multiple?: boolean
pattern?: string pattern?: string
min?: string min?: string
max?: string max?: string
@ -58,7 +34,6 @@ export interface InputProps {
defaultChecked?: boolean defaultChecked?: boolean
size?: 'mini' | 'small' | 'large' | 'default' size?: 'mini' | 'small' | 'large' | 'default'
className?: string className?: string
checked?: boolean
disclaimer?: string disclaimer?: string
disclaimerValues?: string[] disclaimerValues?: string[]
} }

View File

@ -127,7 +127,7 @@ export default function FormStartCompute({
return ( return (
<Form className={styles.form}> <Form className={styles.form}>
{content.form.data.map((field: FormFieldProps) => ( {content.form.data.map((field: FormFieldContent) => (
<Field <Field
key={field.name} key={field.name}
{...field} {...field}

View File

@ -30,9 +30,9 @@ import axios from 'axios'
import FormStartComputeDataset from './FormComputeDataset' import FormStartComputeDataset from './FormComputeDataset'
import styles from './index.module.css' import styles from './index.module.css'
import SuccessConfetti from '@shared/SuccessConfetti' import SuccessConfetti from '@shared/SuccessConfetti'
import { secondsToString } from '@utils/metadata' import { secondsToString } from '@utils/ddo'
import { AssetSelectionAsset } from '@shared/Form/FormFields/AssetSelection' import { AssetSelectionAsset } from '@shared/Form/FormFields/AssetSelection'
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute' import AlgorithmDatasetsListForCompute from './AlgorithmDatasetsListForCompute'
import { getPreviousOrders, getPrice } from '@utils/subgraph' import { getPreviousOrders, getPrice } from '@utils/subgraph'
import AssetActionHistoryTable from '../AssetActionHistoryTable' import AssetActionHistoryTable from '../AssetActionHistoryTable'
import ComputeJobs from '../../../Profile/History/ComputeJobs' import ComputeJobs from '../../../Profile/History/ComputeJobs'

View File

@ -14,8 +14,8 @@ import { useWeb3 } from '@context/Web3'
import { usePricing } from '@hooks/usePricing' import { usePricing } from '@hooks/usePricing'
import { useConsume } from '@hooks/useConsume' import { useConsume } from '@hooks/useConsume'
import ButtonBuy from '@shared/ButtonBuy' import ButtonBuy from '@shared/ButtonBuy'
import { secondsToString } from '@utils/metadata' import { secondsToString } from '@utils/ddo'
import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute' import AlgorithmDatasetsListForCompute from './Compute/AlgorithmDatasetsListForCompute'
import styles from './Consume.module.css' import styles from './Consume.module.css'
import { useIsMounted } from '@hooks/useIsMounted' import { useIsMounted } from '@hooks/useIsMounted'

View File

@ -1,6 +1,6 @@
import React, { ReactElement, useEffect, useState } from 'react' import React, { ReactElement, useEffect, useState } from 'react'
import { Field, Form, FormikContextType, useFormikContext } from 'formik' import { Field, Form, FormikContextType, useFormikContext } from 'formik'
import Input from '@shared/Form/Input' import Input, { InputProps } from '@shared/Form/Input'
import { AssetSelectionAsset } from '@shared/Form/FormFields/AssetSelection' import { AssetSelectionAsset } from '@shared/Form/FormFields/AssetSelection'
import stylesIndex from './index.module.css' import stylesIndex from './index.module.css'
import styles from './FormEditMetadata.module.css' import styles from './FormEditMetadata.module.css'
@ -22,7 +22,7 @@ export default function FormEditComputeDataset({
title, title,
setShowEdit setShowEdit
}: { }: {
data: FormFieldProps[] data: InputProps[]
title: string title: string
setShowEdit: (show: boolean) => void setShowEdit: (show: boolean) => void
}): ReactElement { }): ReactElement {
@ -64,7 +64,7 @@ export default function FormEditComputeDataset({
return ( return (
<Form className={styles.form}> <Form className={styles.form}>
<h3 className={stylesIndex.title}>{title}</h3> <h3 className={stylesIndex.title}>{title}</h3>
{data.map((field: FormFieldProps) => ( {data.map((field: InputProps) => (
<Field <Field
key={field.name} key={field.name}
{...field} {...field}

View File

@ -1,14 +1,14 @@
import React, { ChangeEvent, ReactElement } from 'react' import React, { ChangeEvent, ReactElement } from 'react'
import { Field, Form, FormikContextType, useFormikContext } from 'formik' import { Field, Form, FormikContextType, useFormikContext } from 'formik'
import { useOcean } from '@context/Ocean' import { useOcean } from '@context/Ocean'
import Input from '@shared/Form/Input' import Input, { InputProps } from '@shared/Form/Input'
import { checkIfTimeoutInPredefinedValues } from '@utils/metadata' import { checkIfTimeoutInPredefinedValues } from '@utils/ddo'
import FormActions from './FormActions' import FormActions from './FormActions'
import styles from './FormEditMetadata.module.css' import styles from './FormEditMetadata.module.css'
import { FormPublishData } from '../../../Publish/_types' import { FormPublishData } from '../../../Publish/_types'
// function handleTimeoutCustomOption( // function handleTimeoutCustomOption(
// data: FormFieldProps[], // data: FormFieldContent[],
// values: Partial<FormPublishData> // values: Partial<FormPublishData>
// ) { // ) {
// const timeoutFieldContent = data.filter( // const timeoutFieldContent = data.filter(
@ -48,7 +48,7 @@ export default function FormEditMetadata({
showPrice, showPrice,
isComputeDataset isComputeDataset
}: { }: {
data: FormFieldProps[] data: InputProps[]
setShowEdit: (show: boolean) => void setShowEdit: (show: boolean) => void
setTimeoutStringValue: (value: string) => void setTimeoutStringValue: (value: string) => void
values: Partial<FormPublishData> values: Partial<FormPublishData>
@ -65,7 +65,7 @@ export default function FormEditMetadata({
// Workaround for default `validateOnChange` not kicking in // Workaround for default `validateOnChange` not kicking in
function handleFieldChange( function handleFieldChange(
e: ChangeEvent<HTMLInputElement>, e: ChangeEvent<HTMLInputElement>,
field: FormFieldProps field: InputProps
) { ) {
validateField(field.name) validateField(field.name)
setFieldValue(field.name, e.target.value) setFieldValue(field.name, e.target.value)
@ -89,7 +89,7 @@ export default function FormEditMetadata({
return ( return (
<Form className={styles.form}> <Form className={styles.form}>
{data.map( {data.map(
(field: FormFieldProps) => (field: InputProps) =>
(!showPrice && field.name === 'price') || ( (!showPrice && field.name === 'price') || (
<Field <Field
key={field.name} key={field.name}

View File

@ -1,4 +1,4 @@
import { secondsToString } from '@utils/metadata' import { secondsToString } from '@utils/ddo'
import { EditableMetadataLinks } from '@oceanprotocol/lib' import { EditableMetadataLinks } from '@oceanprotocol/lib'
import * as Yup from 'yup' import * as Yup from 'yup'

View File

@ -6,7 +6,7 @@ import { useUserPreferences } from '@context/UserPreferences'
// import Debug from './DebugEditMetadata' // import Debug from './DebugEditMetadata'
import Web3Feedback from '@shared/Web3Feedback' import Web3Feedback from '@shared/Web3Feedback'
import FormEditMetadata from './FormEditMetadata' import FormEditMetadata from './FormEditMetadata'
import { mapTimeoutStringToSeconds } from '@utils/metadata' import { mapTimeoutStringToSeconds } from '@utils/ddo'
import styles from './index.module.css' import styles from './index.module.css'
import { Logger } from '@oceanprotocol/lib' import { Logger } from '@oceanprotocol/lib'
import { useWeb3 } from '@context/Web3' import { useWeb3 } from '@context/Web3'

View File

@ -8,7 +8,7 @@ import React, {
// import { useStaticQuery, graphql } from 'gatsby' // import { useStaticQuery, graphql } from 'gatsby'
// import { useFormikContext, Field, Form, FormikContextType } from 'formik' // import { useFormikContext, Field, Form, FormikContextType } from 'formik'
// import Input from '../../atoms/Input' // import Input from '../../atoms/Input'
// import { FormContent, FormFieldProps } from '../../../@types/Form' // import { FormContent, FormFieldContent } from '../../../@types/Form'
// import { MetadataPublishFormAlgorithm } from '../../../@types/MetaData' // import { MetadataPublishFormAlgorithm } from '../../../@types/MetaData'
// import { initialValues as initialValuesAlgorithm } from '../../../@types/FormAlgoPublish' // import { initialValues as initialValuesAlgorithm } from '../../../@types/FormAlgoPublish'
// import AdvancedSettings from '../../molecules/FormFields/AdvancedSettings' // import AdvancedSettings from '../../molecules/FormFields/AdvancedSettings'
@ -116,7 +116,7 @@ import React, {
// // Workaround for default `validateOnChange` not kicking in // // Workaround for default `validateOnChange` not kicking in
// function handleFieldChange( // function handleFieldChange(
// e: ChangeEvent<HTMLInputElement>, // e: ChangeEvent<HTMLInputElement>,
// field: FormFieldProps // field: InputProps
// ) { // ) {
// const value = // const value =
// field.type === 'checkbox' || field.type === 'terms' // field.type === 'checkbox' || field.type === 'terms'
@ -148,7 +148,7 @@ import React, {
// <FormTitle title={content.title} /> // <FormTitle title={content.title} />
// {content.data.map( // {content.data.map(
// (field: FormFieldProps) => // (field: InputProps) =>
// field.advanced !== true && // field.advanced !== true &&
// ((field.name !== 'entrypoint' && // ((field.name !== 'entrypoint' &&
// field.name !== 'image' && // field.name !== 'image' &&

View File

@ -2,18 +2,45 @@ import Input from '@shared/Form/Input'
import { Field } from 'formik' import { Field } from 'formik'
import React, { ReactElement } from 'react' import React, { ReactElement } from 'react'
import content from '../../../../../content/publish/form.json' import content from '../../../../../content/publish/form.json'
import { getFieldContent } from '../../_utils'
export default function MetadataFields(): ReactElement { export default function MetadataFields(): ReactElement {
return ( return (
<> <>
{content.metadata.fields.map((field: FormFieldProps) => ( <Field
{...getFieldContent('name', content.metadata.fields)}
component={Input}
name="metadata.name"
/>
<Field
{...getFieldContent('description', content.metadata.fields)}
component={Input}
name="metadata.description"
/>
<Field
{...getFieldContent('author', content.metadata.fields)}
component={Input}
name="metadata.author"
/>
<Field
{...getFieldContent('tags', content.metadata.fields)}
component={Input}
name="metadata.tags"
/>
<Field
{...getFieldContent('termsAndConditions', content.metadata.fields)}
component={Input}
name="metadata.termsAndConditions"
/>
{/* {content.metadata.fields.map((field: FormFieldContent) => (
<Field <Field
{...field} {...field}
key={`metadata-${field.name}`} key={`metadata-${field.name}`}
component={Input} component={Input}
name={`metadata.${field.name}`} name={`metadata.${field.name}`}
/> />
))} ))} */}
</> </>
) )
} }

View File

@ -5,7 +5,7 @@ import Tags from '@shared/atoms/Tags'
import MetaItem from '../../../Asset/AssetContent/MetaItem' import MetaItem from '../../../Asset/AssetContent/MetaItem'
import FileIcon from '@shared/FileIcon' import FileIcon from '@shared/FileIcon'
import Button from '@shared/atoms/Button' import Button from '@shared/atoms/Button'
import { transformTags } from '@utils/metadata' import { transformTags } from '@utils/ddo'
import NetworkName from '@shared/NetworkName' import NetworkName from '@shared/NetworkName'
import { useWeb3 } from '@context/Web3' import { useWeb3 } from '@context/Web3'
import styles from './MetadataPreview.module.css' import styles from './MetadataPreview.module.css'

View File

@ -4,6 +4,7 @@ import React, { ReactElement } from 'react'
import IconDownload from '@images/download.svg' import IconDownload from '@images/download.svg'
import IconCompute from '@images/compute.svg' import IconCompute from '@images/compute.svg'
import content from '../../../../../content/publish/form.json' import content from '../../../../../content/publish/form.json'
import { getFieldContent } from '../../_utils'
const accessTypeOptions = [ const accessTypeOptions = [
{ {
@ -21,8 +22,45 @@ const accessTypeOptions = [
export default function ServicesFields(): ReactElement { export default function ServicesFields(): ReactElement {
return ( return (
<> <>
{content.services.fields.map( <Field
(field: FormFieldProps) => {...getFieldContent('dataTokenOptions', content.services.fields)}
component={Input}
name="services[0].dataTokenOptions"
/>
<Field
{...getFieldContent('files', content.services.fields)}
component={Input}
name="services[0].files"
/>
<Field
{...getFieldContent('links', content.services.fields)}
component={Input}
name="services[0].links"
/>
<Field
{...getFieldContent('links', content.services.fields)}
component={Input}
name="services[0].links"
/>
<Field
{...getFieldContent('access', content.services.fields)}
component={Input}
name="services[0].access"
options={accessTypeOptions}
/>
<Field
{...getFieldContent('timeout', content.services.fields)}
component={Input}
name="services[0].timeout"
/>
<Field
{...getFieldContent('providerUri', content.services.fields)}
component={Input}
name="services[0].providerUri"
/>
{/* {content.services.fields.map(
(field: FormFieldContent) =>
field.advanced !== true && ( field.advanced !== true && (
<Field <Field
{...field} {...field}
@ -34,7 +72,7 @@ export default function ServicesFields(): ReactElement {
} }
/> />
) )
)} )} */}
</> </>
) )
} }

View File

@ -1,5 +1,5 @@
import { DataTokenOptions } from '@hooks/usePublish' import { DataTokenOptions } from '@hooks/usePublish'
import { EditableMetadataLinks } from '@oceanprotocol/lib' import { EditableMetadataLinks, File } from '@oceanprotocol/lib'
export interface FormPublishService { export interface FormPublishService {
files: string | File[] files: string | File[]
@ -7,6 +7,9 @@ export interface FormPublishService {
timeout: string timeout: string
dataTokenOptions: DataTokenOptions dataTokenOptions: DataTokenOptions
access: 'Download' | 'Compute' | string access: 'Download' | 'Compute' | string
image?: string
containerTag?: string
entrypoint?: string
providerUri?: string providerUri?: string
} }

View File

@ -0,0 +1,56 @@
import {
dateToStringNoMS,
transformTags,
getAlgorithmComponent,
getAlgorithmFileExtension
} from '@utils/ddo'
import { FormPublishData } from './_types'
export function getFieldContent(
fieldName: string,
fields: FormFieldContent[]
): FormFieldContent {
return fields.filter((field: FormFieldContent) => field.name === fieldName)[0]
}
export function transformPublishFormToDdo(
data: Partial<FormPublishData>,
ddo?: DdoMarket
): DdoMarket {
const currentTime = dateToStringNoMS(new Date())
const { type } = data
const { name, description, tags, author, termsAndConditions } = data.metadata
const { files, links, image, containerTag, entrypoint, providerUri } =
data.services[0]
const fileUrl = typeof files !== 'string' && files[0].url
const algorithmLanguage = getAlgorithmFileExtension(fileUrl)
const algorithm = getAlgorithmComponent(
image,
containerTag,
entrypoint,
algorithmLanguage
)
const service = {
files: typeof files !== 'string' && files,
links: typeof links !== 'string' ? links : [],
...(type === 'algorithm' && { ...algorithm })
}
const newDdo: DdoMarket = {
metadata: {
name,
description,
tags: transformTags(tags),
author,
dateCreated: ddo ? ddo.metadata.dateCreated : currentTime,
datePublished: '',
termsAndConditions,
license: 'https://market.oceanprotocol.com/terms'
},
services: [service]
}
return newDdo
}

View File

@ -2,15 +2,12 @@ import React, { ReactElement, useState, useEffect } from 'react'
import { Formik, FormikState } from 'formik' import { Formik, FormikState } from 'formik'
import { usePublish } from '@hooks/usePublish' import { usePublish } from '@hooks/usePublish'
import { initialValues, validationSchema } from './_constants' import { initialValues, validationSchema } from './_constants'
// import { import { validateDockerImage } from '@utils/docker'
// transformPublishFormToMetadata,
// mapTimeoutStringToSeconds,
// validateDockerImage
// } from '@utils/metadata'
import { Logger, Metadata } from '@oceanprotocol/lib' import { Logger, Metadata } from '@oceanprotocol/lib'
import { useAccountPurgatory } from '@hooks/useAccountPurgatory' import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
import { useWeb3 } from '@context/Web3' import { useWeb3 } from '@context/Web3'
import { FormPublishData } from './_types' import { FormPublishData } from './_types'
import { transformPublishFormToDdo } from './_utils'
import PageHeader from '@shared/Page/PageHeader' import PageHeader from '@shared/Page/PageHeader'
import Title from './Title' import Title from './Title'
import styles from './index.module.css' import styles from './index.module.css'