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": {
"title": "Enter details",
"title": "Metadata",
"fields": [
{
"name": "name",
@ -38,7 +38,7 @@
]
},
"services": {
"title": "Create services",
"title": "Access",
"fields": [
{
"name": "dataTokenOptions",
@ -87,13 +87,12 @@
"label": "Custom Provider URL",
"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/).",
"placeholder": "https://provider.polygon.oceanprotocol.com/",
"advanced": true
"placeholder": "e.g. https://provider.polygon.oceanprotocol.com/"
}
]
},
"pricing": {
"title": "Create pricing schema",
"title": "Pricing",
"fields": [
{
"name": "dummy content, as content is defined under 'create' key in ../price.json"

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

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

View File

@ -7,6 +7,11 @@ import {
// declaring into global scope to be able to use this as
// ambiant types despite the above imports
declare global {
interface DdoMarket {
metadata: any
services: any[]
}
interface AdditionalInformationMarket extends AdditionalInformation {
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
handleFieldChange: (
e: ChangeEvent<HTMLInputElement>,
field: FormFieldProps
field: FormFieldContent
) => void
}): ReactElement {
const [showAdvancedSettings, setShowAdvancedSettings] =
@ -30,7 +30,7 @@ export default function AdvancedSettings(prop: {
</Button>
{showAdvancedSettings &&
prop.content.fields.map(
(field: FormFieldProps) =>
(field: FormFieldContent) =>
field.advanced === true && (
<Field
key={field.name}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,18 +2,45 @@ import Input from '@shared/Form/Input'
import { Field } from 'formik'
import React, { ReactElement } from 'react'
import content from '../../../../../content/publish/form.json'
import { getFieldContent } from '../../_utils'
export default function MetadataFields(): ReactElement {
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}
key={`metadata-${field.name}`}
component={Input}
name={`metadata.${field.name}`}
/>
))}
))} */}
</>
)
}

View File

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

View File

@ -4,6 +4,7 @@ import React, { ReactElement } from 'react'
import IconDownload from '@images/download.svg'
import IconCompute from '@images/compute.svg'
import content from '../../../../../content/publish/form.json'
import { getFieldContent } from '../../_utils'
const accessTypeOptions = [
{
@ -21,8 +22,45 @@ const accessTypeOptions = [
export default function ServicesFields(): ReactElement {
return (
<>
{content.services.fields.map(
(field: FormFieldProps) =>
<Field
{...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
{...field}
@ -34,7 +72,7 @@ export default function ServicesFields(): ReactElement {
}
/>
)
)}
)} */}
</>
)
}

View File

@ -1,5 +1,5 @@
import { DataTokenOptions } from '@hooks/usePublish'
import { EditableMetadataLinks } from '@oceanprotocol/lib'
import { EditableMetadataLinks, File } from '@oceanprotocol/lib'
export interface FormPublishService {
files: string | File[]
@ -7,6 +7,9 @@ export interface FormPublishService {
timeout: string
dataTokenOptions: DataTokenOptions
access: 'Download' | 'Compute' | string
image?: string
containerTag?: string
entrypoint?: 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 { usePublish } from '@hooks/usePublish'
import { initialValues, validationSchema } from './_constants'
// import {
// transformPublishFormToMetadata,
// mapTimeoutStringToSeconds,
// validateDockerImage
// } from '@utils/metadata'
import { validateDockerImage } from '@utils/docker'
import { Logger, Metadata } from '@oceanprotocol/lib'
import { useAccountPurgatory } from '@hooks/useAccountPurgatory'
import { useWeb3 } from '@context/Web3'
import { FormPublishData } from './_types'
import { transformPublishFormToDdo } from './_utils'
import PageHeader from '@shared/Page/PageHeader'
import Title from './Title'
import styles from './index.module.css'