mirror of
https://github.com/oceanprotocol/market.git
synced 2024-11-15 01:34:57 +01:00
Merge pull request #384 from oceanprotocol/publish-algo
Compute: publish algorithm
This commit is contained in:
commit
e74d848397
@ -1,84 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Publish",
|
|
||||||
"description": "Highlight the important features of your data set to make it more discoverable and catch the interest of data consumers.",
|
|
||||||
"warning": "Given the beta status, publishing on Ropsten or Rinkeby first is strongly recommended. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms).",
|
|
||||||
"form": {
|
|
||||||
"title": "Publish",
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"label": "Title",
|
|
||||||
"placeholder": "e.g. Shapes of Desert Plants",
|
|
||||||
"help": "Enter a concise title.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "description",
|
|
||||||
"label": "Description",
|
|
||||||
"help": "Add a thorough description with as much detail as possible. You can use [Markdown](https://daringfireball.net/projects/markdown/basics).",
|
|
||||||
"type": "textarea",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "files",
|
|
||||||
"label": "File",
|
|
||||||
"placeholder": "e.g. https://file.com/file.json",
|
|
||||||
"help": "Please provide a URL to your data set file. This URL will be stored encrypted after publishing.",
|
|
||||||
"type": "files",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "links",
|
|
||||||
"label": "Sample file",
|
|
||||||
"placeholder": "e.g. https://file.com/samplefile.json",
|
|
||||||
"help": "Please provide a URL to a sample of your data set file. This file should reveal the data structure of your data set, e.g. by including the header and one line of a CSV file. This file URL will be publicly available after publishing.",
|
|
||||||
"type": "files"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "access",
|
|
||||||
"label": "Access Type",
|
|
||||||
"help": "Choose how you want your files to be accessible for the specified price.",
|
|
||||||
"type": "select",
|
|
||||||
"options": ["Download"],
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "timeout",
|
|
||||||
"label": "Timeout",
|
|
||||||
"help": "Define how long buyers should be able to download the data set again after the initial purchase.",
|
|
||||||
"type": "select",
|
|
||||||
"options": ["Forever", "1 day", "1 week", "1 month", "1 year"],
|
|
||||||
"sortOptions": false,
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "dataTokenOptions",
|
|
||||||
"label": "Datatoken Name & Symbol",
|
|
||||||
"type": "datatoken",
|
|
||||||
"help": "The datatoken for this data set will be created with this name & symbol.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "author",
|
|
||||||
"label": "Author",
|
|
||||||
"placeholder": "e.g. Jelly McJellyfish",
|
|
||||||
"help": "Give proper attribution for your data set.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "tags",
|
|
||||||
"label": "Tags",
|
|
||||||
"placeholder": "e.g. logistics, ai",
|
|
||||||
"help": "Separate tags with comma."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "termsAndConditions",
|
|
||||||
"label": "Terms & Conditions",
|
|
||||||
"type": "terms",
|
|
||||||
"options": ["I agree to these Terms and Conditions"],
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"success": "Asset Created!"
|
|
||||||
}
|
|
||||||
}
|
|
85
content/pages/publish/form-algorithm.json
Normal file
85
content/pages/publish/form-algorithm.json
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"title": "Publish an Algorithm",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"label": "Title",
|
||||||
|
"placeholder": "e.g. Shapes of Desert Plants",
|
||||||
|
"help": "Enter a concise title.",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"label": "Description",
|
||||||
|
"help": "Add a thorough description with as much detail as possible. You can use [Markdown](https://daringfireball.net/projects/markdown/basics).",
|
||||||
|
"type": "textarea",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "files",
|
||||||
|
"label": "File",
|
||||||
|
"placeholder": "e.g. https://file.com/file.json",
|
||||||
|
"help": "Please provide a URL to your algorith file. This URL will be stored encrypted after publishing.",
|
||||||
|
"type": "files",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dockerImage",
|
||||||
|
"label": "Docker Image",
|
||||||
|
"placeholder": "e.g. python3.7",
|
||||||
|
"help": "Please select a image to run your algorithm.",
|
||||||
|
"type": "select",
|
||||||
|
"options": ["node:pre-defined", "python:pre-defined", "custom image"],
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"label": "Image URL",
|
||||||
|
"placeholder": "e.g. oceanprotocol/algo_dockers or https://example.com/image_path",
|
||||||
|
"help": "Provide the name of a public docker image or the full url if you have it hosted in a 3rd party repo",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "containerTag",
|
||||||
|
"label": "Docker Image Tag",
|
||||||
|
"placeholder": "e.g. latest",
|
||||||
|
"help": "Provide the tag for your docker image.",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entrypoint",
|
||||||
|
"label": "Entrypoint",
|
||||||
|
"placeholder": "e.g. python $ALGO",
|
||||||
|
"help": "Provide the entrypoint for your algorithm.",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "algorithmPrivacy",
|
||||||
|
"label": "Algorithm Privacy",
|
||||||
|
"type": "checkbox",
|
||||||
|
"options": ["Keep my algorithm private"],
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "author",
|
||||||
|
"label": "Author",
|
||||||
|
"placeholder": "e.g. Jelly McJellyfish",
|
||||||
|
"help": "Give proper attribution for your algorith.",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tags",
|
||||||
|
"label": "Tags",
|
||||||
|
"placeholder": "e.g. logistics, ai",
|
||||||
|
"help": "Separate tags with comma."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "termsAndConditions",
|
||||||
|
"label": "Terms & Conditions",
|
||||||
|
"type": "terms",
|
||||||
|
"options": ["I agree to these Terms and Conditions"],
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"success": "Algorithm Published!"
|
||||||
|
}
|
79
content/pages/publish/form-dataset.json
Normal file
79
content/pages/publish/form-dataset.json
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"title": "Publish a Data Set",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"label": "Title",
|
||||||
|
"placeholder": "e.g. Shapes of Desert Plants",
|
||||||
|
"help": "Enter a concise title.",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"label": "Description",
|
||||||
|
"help": "Add a thorough description with as much detail as possible. You can use [Markdown](https://daringfireball.net/projects/markdown/basics).",
|
||||||
|
"type": "textarea",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "files",
|
||||||
|
"label": "File",
|
||||||
|
"placeholder": "e.g. https://file.com/file.json",
|
||||||
|
"help": "Please provide a URL to your data set file. This URL will be stored encrypted after publishing.",
|
||||||
|
"type": "files",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "links",
|
||||||
|
"label": "Sample file",
|
||||||
|
"placeholder": "e.g. https://file.com/samplefile.json",
|
||||||
|
"help": "Please provide a URL to a sample of your data set file. This file should reveal the data structure of your data set, e.g. by including the header and one line of a CSV file. This file URL will be publicly available after publishing.",
|
||||||
|
"type": "files"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "access",
|
||||||
|
"label": "Access Type",
|
||||||
|
"help": "Choose how you want your files to be accessible for the specified price.",
|
||||||
|
"type": "select",
|
||||||
|
"options": ["Download"],
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "timeout",
|
||||||
|
"label": "Timeout",
|
||||||
|
"help": "Define how long buyers should be able to download the data set again after the initial purchase.",
|
||||||
|
"type": "select",
|
||||||
|
"options": ["Forever", "1 day", "1 week", "1 month", "1 year"],
|
||||||
|
"sortOptions": false,
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dataTokenOptions",
|
||||||
|
"label": "Datatoken Name & Symbol",
|
||||||
|
"type": "datatoken",
|
||||||
|
"help": "The datatoken for this data set will be created with this name & symbol.",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "author",
|
||||||
|
"label": "Author",
|
||||||
|
"placeholder": "e.g. Jelly McJellyfish",
|
||||||
|
"help": "Give proper attribution for your data set.",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "tags",
|
||||||
|
"label": "Tags",
|
||||||
|
"placeholder": "e.g. logistics, ai",
|
||||||
|
"help": "Separate tags with comma."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "termsAndConditions",
|
||||||
|
"label": "Terms & Conditions",
|
||||||
|
"type": "terms",
|
||||||
|
"options": ["I agree to these Terms and Conditions"],
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"success": "Asset Created!"
|
||||||
|
}
|
5
content/pages/publish/index.json
Normal file
5
content/pages/publish/index.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"title": "Publish",
|
||||||
|
"description": "Highlight the important features of your data set or algorithm to make it more discoverable and catch the interest of data consumers.",
|
||||||
|
"warning": "Given the beta status, publishing on Ropsten or Rinkeby first is strongly recommended. Please familiarize yourself with [the market](https://oceanprotocol.com/technology/marketplaces), [the risks](https://blog.oceanprotocol.com/on-staking-on-data-in-ocean-market-3d8e09eb0a13), and the [Terms of Use](/terms)."
|
||||||
|
}
|
1
src/@types/Form.d.ts
vendored
1
src/@types/Form.d.ts
vendored
@ -5,6 +5,7 @@ export interface FormFieldProps {
|
|||||||
options?: string[]
|
options?: string[]
|
||||||
sortOptions?: boolean
|
sortOptions?: boolean
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
disabled?: boolean
|
||||||
help?: string
|
help?: string
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
pattern?: string
|
pattern?: string
|
||||||
|
18
src/@types/MetaData.d.ts
vendored
18
src/@types/MetaData.d.ts
vendored
@ -24,7 +24,7 @@ export interface PriceOptionsMarket extends PriceOptions {
|
|||||||
swapFee: number
|
swapFee: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MetadataPublishForm {
|
export interface MetadataPublishFormDataset {
|
||||||
// ---- required fields ----
|
// ---- required fields ----
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
@ -39,6 +39,22 @@ export interface MetadataPublishForm {
|
|||||||
links?: string | File[]
|
links?: string | File[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MetadataPublishFormAlgorithm {
|
||||||
|
// ---- required fields ----
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
files: string | File[]
|
||||||
|
author: string
|
||||||
|
dockerImage: string
|
||||||
|
algorithmPrivacy: boolean
|
||||||
|
termsAndConditions: boolean
|
||||||
|
// ---- optional fields ----
|
||||||
|
image: string
|
||||||
|
containerTag: string
|
||||||
|
entrypoint: string
|
||||||
|
tags?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface ServiceMetadataMarket extends ServiceMetadata {
|
export interface ServiceMetadataMarket extends ServiceMetadata {
|
||||||
attributes: MetadataMarket
|
attributes: MetadataMarket
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
|
background-color: var(--background-body);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
margin-right: -1px;
|
margin-right: -1px;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
@ -29,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab[aria-selected='true'] {
|
.tab[aria-selected='true'] {
|
||||||
background: var(--font-color-heading);
|
background-color: var(--font-color-heading);
|
||||||
color: var(--background-body);
|
color: var(--background-body);
|
||||||
border-color: var(--font-color-heading);
|
border-color: var(--font-color-heading);
|
||||||
}
|
}
|
||||||
|
@ -14,5 +14,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
width: .85rem
|
width: 0.85rem;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
.preview {
|
.preview {
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
|
margin-top: calc(var(--spacer) / 2);
|
||||||
margin-bottom: var(--spacer);
|
margin-bottom: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10,7 +11,14 @@
|
|||||||
.metaFull {
|
.metaFull {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacer);
|
gap: var(--spacer);
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.metaAlgorithm {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--spacer);
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
.previewTitle {
|
.previewTitle {
|
||||||
|
@ -5,7 +5,10 @@ import Tags from '../atoms/Tags'
|
|||||||
import MetaItem from '../organisms/AssetContent/MetaItem'
|
import MetaItem from '../organisms/AssetContent/MetaItem'
|
||||||
import styles from './MetadataPreview.module.css'
|
import styles from './MetadataPreview.module.css'
|
||||||
import File from '../atoms/File'
|
import File from '../atoms/File'
|
||||||
import { MetadataPublishForm } from '../../@types/MetaData'
|
import {
|
||||||
|
MetadataPublishFormDataset,
|
||||||
|
MetadataPublishFormAlgorithm
|
||||||
|
} from '../../@types/MetaData'
|
||||||
import Button from '../atoms/Button'
|
import Button from '../atoms/Button'
|
||||||
import { transformTags } from '../../utils/metadata'
|
import { transformTags } from '../../utils/metadata'
|
||||||
|
|
||||||
@ -42,7 +45,7 @@ function Description({ description }: { description: string }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function MetaFull({ values }: { values: Partial<MetadataPublishForm> }) {
|
function MetaFull({ values }: { values: Partial<MetadataPublishFormDataset> }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.metaFull}>
|
<div className={styles.metaFull}>
|
||||||
{Object.entries(values)
|
{Object.entries(values)
|
||||||
@ -56,6 +59,8 @@ function MetaFull({ values }: { values: Partial<MetadataPublishForm> }) {
|
|||||||
key.includes('links') ||
|
key.includes('links') ||
|
||||||
key.includes('termsAndConditions') ||
|
key.includes('termsAndConditions') ||
|
||||||
key.includes('dataTokenOptions') ||
|
key.includes('dataTokenOptions') ||
|
||||||
|
key.includes('dockerImage') ||
|
||||||
|
key.includes('algorithmPrivacy') ||
|
||||||
value === undefined ||
|
value === undefined ||
|
||||||
value === ''
|
value === ''
|
||||||
)
|
)
|
||||||
@ -82,10 +87,10 @@ function Sample({ url }: { url: string }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MetadataPreview({
|
export function MetadataPreview({
|
||||||
values
|
values
|
||||||
}: {
|
}: {
|
||||||
values: Partial<MetadataPublishForm>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className={styles.preview}>
|
<div className={styles.preview}>
|
||||||
@ -119,3 +124,47 @@ export default function MetadataPreview({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function MetadataAlgorithmPreview({
|
||||||
|
values
|
||||||
|
}: {
|
||||||
|
values: Partial<MetadataPublishFormAlgorithm>
|
||||||
|
}): ReactElement {
|
||||||
|
return (
|
||||||
|
<div className={styles.preview}>
|
||||||
|
<h2 className={styles.previewTitle}>Preview</h2>
|
||||||
|
<header>
|
||||||
|
{values.name && <h3 className={styles.title}>{values.name}</h3>}
|
||||||
|
{values.description && <Description description={values.description} />}
|
||||||
|
|
||||||
|
<div className={styles.asset}>
|
||||||
|
{values.files?.length > 0 && typeof values.files !== 'string' && (
|
||||||
|
<File
|
||||||
|
file={values.files[0] as FileMetadata}
|
||||||
|
className={styles.file}
|
||||||
|
small
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{values.tags && <Tags items={transformTags(values.tags)} />}
|
||||||
|
</header>
|
||||||
|
<div className={styles.metaAlgorithm}>
|
||||||
|
{values.dockerImage && (
|
||||||
|
<MetaItem
|
||||||
|
key="dockerImage"
|
||||||
|
title="Docker Image"
|
||||||
|
content={values.dockerImage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{values.algorithmPrivacy && (
|
||||||
|
<MetaItem
|
||||||
|
key="privateAlgorithm"
|
||||||
|
title="Private Algorithm"
|
||||||
|
content="Yes"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<MetaFull values={values} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
|
||||||
import { transformPublishFormToMetadata } from '../../../../utils/metadata'
|
import { transformPublishFormToMetadata } from '../../../../utils/metadata'
|
||||||
import DebugOutput from '../../../atoms/DebugOutput'
|
import DebugOutput from '../../../atoms/DebugOutput'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ export default function Debug({
|
|||||||
values,
|
values,
|
||||||
ddo
|
ddo
|
||||||
}: {
|
}: {
|
||||||
values: Partial<MetadataPublishForm>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
ddo: DDO
|
ddo: DDO
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const newDdo = {
|
const newDdo = {
|
||||||
|
@ -5,12 +5,12 @@ import Button from '../../../atoms/Button'
|
|||||||
import Input from '../../../atoms/Input'
|
import Input from '../../../atoms/Input'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import { FormFieldProps } from '../../../../@types/Form'
|
import { FormFieldProps } from '../../../../@types/Form'
|
||||||
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
|
||||||
import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata'
|
import { checkIfTimeoutInPredefinedValues } from '../../../../utils/metadata'
|
||||||
|
|
||||||
function handleTimeoutCustomOption(
|
function handleTimeoutCustomOption(
|
||||||
data: FormFieldProps[],
|
data: FormFieldProps[],
|
||||||
values: Partial<MetadataPublishForm>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
) {
|
) {
|
||||||
const timeoutFieldContent = data.filter(
|
const timeoutFieldContent = data.filter(
|
||||||
(field) => field.name === 'timeout'
|
(field) => field.name === 'timeout'
|
||||||
@ -51,14 +51,14 @@ export default function FormEditMetadata({
|
|||||||
data: FormFieldProps[]
|
data: FormFieldProps[]
|
||||||
setShowEdit: (show: boolean) => void
|
setShowEdit: (show: boolean) => void
|
||||||
setTimeoutStringValue: (value: string) => void
|
setTimeoutStringValue: (value: string) => void
|
||||||
values: Partial<MetadataPublishForm>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { ocean, accountId } = useOcean()
|
const { ocean, accountId } = useOcean()
|
||||||
const {
|
const {
|
||||||
isValid,
|
isValid,
|
||||||
validateField,
|
validateField,
|
||||||
setFieldValue
|
setFieldValue
|
||||||
}: FormikContextType<Partial<MetadataPublishForm>> = useFormikContext()
|
}: FormikContextType<Partial<MetadataPublishFormDataset>> = useFormikContext()
|
||||||
|
|
||||||
// Manually handle change events instead of using `handleChange` from Formik.
|
// Manually handle change events instead of using `handleChange` from Formik.
|
||||||
// Workaround for default `validateOnChange` not kicking in
|
// Workaround for default `validateOnChange` not kicking in
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import { Formik } from 'formik'
|
import { Formik } from 'formik'
|
||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../../@types/MetaData'
|
||||||
import {
|
import {
|
||||||
validationSchema,
|
validationSchema,
|
||||||
getInitialValues
|
getInitialValues
|
||||||
} from '../../../../models/FormEditMetadata'
|
} from '../../../../models/FormEditMetadata'
|
||||||
import { useAsset } from '../../../../providers/Asset'
|
import { useAsset } from '../../../../providers/Asset'
|
||||||
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../../providers/UserPreferences'
|
||||||
import MetadataPreview from '../../../molecules/MetadataPreview'
|
import { MetadataPreview } from '../../../molecules/MetadataPreview'
|
||||||
import Debug from './Debug'
|
import Debug from './Debug'
|
||||||
import Web3Feedback from '../../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../../molecules/Wallet/Feedback'
|
||||||
import FormEditMetadata from './FormEditMetadata'
|
import FormEditMetadata from './FormEditMetadata'
|
||||||
@ -66,7 +66,7 @@ export default function Edit({
|
|||||||
const hasFeedback = error || success
|
const hasFeedback = error || success
|
||||||
|
|
||||||
async function handleSubmit(
|
async function handleSubmit(
|
||||||
values: Partial<MetadataPublishForm>,
|
values: Partial<MetadataPublishFormDataset>,
|
||||||
resetForm: () => void
|
resetForm: () => void
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
@ -3,7 +3,6 @@ import styles from './index.module.css'
|
|||||||
import Compute from './Compute'
|
import Compute from './Compute'
|
||||||
import Consume from './Consume'
|
import Consume from './Consume'
|
||||||
import { Logger } from '@oceanprotocol/lib'
|
import { Logger } from '@oceanprotocol/lib'
|
||||||
import { ConfigHelperConfig } from '@oceanprotocol/lib/dist/node/utils/ConfigHelper'
|
|
||||||
import Tabs from '../../atoms/Tabs'
|
import Tabs from '../../atoms/Tabs'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
import compareAsBN from '../../../utils/compareAsBN'
|
import compareAsBN from '../../../utils/compareAsBN'
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
margin-top: var(--spacer);
|
margin-top: var(--spacer);
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacer);
|
gap: var(--spacer);
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.metaFull code {
|
.metaFull code {
|
||||||
|
@ -10,3 +10,8 @@
|
|||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ export default function MetaItem({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.metaItem}>
|
<div className={styles.metaItem}>
|
||||||
<h3 className={styles.title}>{title}</h3>
|
<h3 className={styles.title}>{title}</h3>
|
||||||
{content}
|
<div className={styles.content}>{content}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { MetadataPublishForm } from '../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../@types/MetaData'
|
||||||
import DebugOutput from '../../atoms/DebugOutput'
|
import DebugOutput from '../../atoms/DebugOutput'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { transformPublishFormToMetadata } from '../../../utils/metadata'
|
import { transformPublishFormToMetadata } from '../../../utils/metadata'
|
||||||
@ -7,7 +7,7 @@ import { transformPublishFormToMetadata } from '../../../utils/metadata'
|
|||||||
export default function Debug({
|
export default function Debug({
|
||||||
values
|
values
|
||||||
}: {
|
}: {
|
||||||
values: Partial<MetadataPublishForm>
|
values: Partial<MetadataPublishFormDataset>
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const ddo = {
|
const ddo = {
|
||||||
'@context': 'https://w3id.org/did/v1',
|
'@context': 'https://w3id.org/did/v1',
|
||||||
|
162
src/components/pages/Publish/FormAlgoPublish.tsx
Normal file
162
src/components/pages/Publish/FormAlgoPublish.tsx
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import React, {
|
||||||
|
ReactElement,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
FormEvent,
|
||||||
|
ChangeEvent
|
||||||
|
} from 'react'
|
||||||
|
import { useStaticQuery, graphql } from 'gatsby'
|
||||||
|
import styles from './FormPublish.module.css'
|
||||||
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
|
import { useFormikContext, Field, Form, FormikContextType } from 'formik'
|
||||||
|
import Input from '../../atoms/Input'
|
||||||
|
import Button from '../../atoms/Button'
|
||||||
|
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
||||||
|
import { MetadataPublishFormAlgorithm } from '../../../@types/MetaData'
|
||||||
|
import { initialValues as initialValuesAlgorithm } from '../../../models/FormAlgoPublish'
|
||||||
|
|
||||||
|
import stylesIndex from './index.module.css'
|
||||||
|
|
||||||
|
const query = graphql`
|
||||||
|
query {
|
||||||
|
content: allFile(
|
||||||
|
filter: { relativePath: { eq: "pages/publish/form-algorithm.json" } }
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childPublishJson {
|
||||||
|
title
|
||||||
|
data {
|
||||||
|
name
|
||||||
|
placeholder
|
||||||
|
label
|
||||||
|
help
|
||||||
|
type
|
||||||
|
required
|
||||||
|
sortOptions
|
||||||
|
options
|
||||||
|
}
|
||||||
|
warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function FormPublish(): ReactElement {
|
||||||
|
const data = useStaticQuery(query)
|
||||||
|
const content: FormContent = data.content.edges[0].node.childPublishJson
|
||||||
|
const { ocean, account } = useOcean()
|
||||||
|
const {
|
||||||
|
status,
|
||||||
|
setStatus,
|
||||||
|
isValid,
|
||||||
|
setErrors,
|
||||||
|
setTouched,
|
||||||
|
resetForm,
|
||||||
|
initialValues,
|
||||||
|
validateField,
|
||||||
|
setFieldValue
|
||||||
|
}: FormikContextType<MetadataPublishFormAlgorithm> = useFormikContext()
|
||||||
|
const [selectedDockerImage, setSelectedDockerImage] = useState<string>(
|
||||||
|
initialValues.dockerImage
|
||||||
|
)
|
||||||
|
// reset form validation on every mount
|
||||||
|
useEffect(() => {
|
||||||
|
setErrors({})
|
||||||
|
setTouched({})
|
||||||
|
|
||||||
|
// setSubmitting(false)
|
||||||
|
}, [setErrors, setTouched])
|
||||||
|
|
||||||
|
function handleImageSelectChange(imageSelected: string) {
|
||||||
|
switch (imageSelected) {
|
||||||
|
case 'node:pre-defined': {
|
||||||
|
setFieldValue('image', 'node')
|
||||||
|
setFieldValue('containerTag', '10')
|
||||||
|
setFieldValue('entrypoint', 'node $ALGO')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'python:pre-defined': {
|
||||||
|
setFieldValue('image', 'oceanprotocol/algo_dockers')
|
||||||
|
setFieldValue('containerTag', 'python-panda')
|
||||||
|
setFieldValue('entrypoint', 'python $ALGO')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
setFieldValue('image', '')
|
||||||
|
setFieldValue('containerTag', '')
|
||||||
|
setFieldValue('entrypoint', '')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually handle change events instead of using `handleChange` from Formik.
|
||||||
|
// Workaround for default `validateOnChange` not kicking in
|
||||||
|
function handleFieldChange(
|
||||||
|
e: ChangeEvent<HTMLInputElement>,
|
||||||
|
field: FormFieldProps
|
||||||
|
) {
|
||||||
|
const value =
|
||||||
|
field.type === 'checkbox' ? !JSON.parse(e.target.value) : e.target.value
|
||||||
|
if (field.name === 'dockerImage') {
|
||||||
|
setSelectedDockerImage(e.target.value)
|
||||||
|
handleImageSelectChange(e.target.value)
|
||||||
|
}
|
||||||
|
validateField(field.name)
|
||||||
|
setFieldValue(field.name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetFormAndClearStorage = (e: FormEvent<Element>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
resetForm({
|
||||||
|
values: initialValuesAlgorithm as MetadataPublishFormAlgorithm,
|
||||||
|
status: 'empty'
|
||||||
|
})
|
||||||
|
setStatus('empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
className={styles.form}
|
||||||
|
// do we need this?
|
||||||
|
onChange={() => status === 'empty' && setStatus(null)}
|
||||||
|
>
|
||||||
|
<h2 className={stylesIndex.formTitle}>{content.title}</h2>
|
||||||
|
{content.data.map(
|
||||||
|
(field: FormFieldProps) =>
|
||||||
|
((field.name !== 'entrypoint' &&
|
||||||
|
field.name !== 'image' &&
|
||||||
|
field.name !== 'containerTag') ||
|
||||||
|
selectedDockerImage === 'custom image') && (
|
||||||
|
<Field
|
||||||
|
key={field.name}
|
||||||
|
{...field}
|
||||||
|
component={Input}
|
||||||
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
handleFieldChange(e, field)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
<footer className={styles.actions}>
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
type="submit"
|
||||||
|
disabled={!ocean || !account || !isValid || status === 'empty'}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{status !== 'empty' && (
|
||||||
|
<Button style="text" size="small" onClick={resetFormAndClearStorage}>
|
||||||
|
Reset Form
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</footer>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
.form {
|
.form {
|
||||||
composes: box from '../../atoms/Box.module.css';
|
composes: box from '../../atoms/Box.module.css';
|
||||||
margin-bottom: var(--spacer);
|
margin-bottom: var(--spacer);
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
@ -1,17 +1,45 @@
|
|||||||
import React, { ReactElement, useEffect, FormEvent, ChangeEvent } from 'react'
|
import React, { ReactElement, useEffect, FormEvent, ChangeEvent } from 'react'
|
||||||
|
import { useStaticQuery, graphql } from 'gatsby'
|
||||||
import styles from './FormPublish.module.css'
|
import styles from './FormPublish.module.css'
|
||||||
import { useOcean } from '@oceanprotocol/react'
|
import { useOcean } from '@oceanprotocol/react'
|
||||||
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 Button from '../../atoms/Button'
|
import Button from '../../atoms/Button'
|
||||||
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
import { FormContent, FormFieldProps } from '../../../@types/Form'
|
||||||
import { MetadataPublishForm } from '../../../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../@types/MetaData'
|
||||||
|
import { initialValues as initialValuesDataset } from '../../../models/FormAlgoPublish'
|
||||||
|
import stylesIndex from './index.module.css'
|
||||||
|
|
||||||
export default function FormPublish({
|
const query = graphql`
|
||||||
content
|
query {
|
||||||
}: {
|
content: allFile(
|
||||||
content: FormContent
|
filter: { relativePath: { eq: "pages/publish/form-dataset.json" } }
|
||||||
}): ReactElement {
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
childPublishJson {
|
||||||
|
title
|
||||||
|
data {
|
||||||
|
name
|
||||||
|
placeholder
|
||||||
|
label
|
||||||
|
help
|
||||||
|
type
|
||||||
|
required
|
||||||
|
sortOptions
|
||||||
|
options
|
||||||
|
}
|
||||||
|
warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default function FormPublish(): ReactElement {
|
||||||
|
const data = useStaticQuery(query)
|
||||||
|
const content: FormContent = data.content.edges[0].node.childPublishJson
|
||||||
const { ocean, account } = useOcean()
|
const { ocean, account } = useOcean()
|
||||||
const {
|
const {
|
||||||
status,
|
status,
|
||||||
@ -23,7 +51,7 @@ export default function FormPublish({
|
|||||||
initialValues,
|
initialValues,
|
||||||
validateField,
|
validateField,
|
||||||
setFieldValue
|
setFieldValue
|
||||||
}: FormikContextType<MetadataPublishForm> = useFormikContext()
|
}: FormikContextType<MetadataPublishFormDataset> = useFormikContext()
|
||||||
|
|
||||||
// reset form validation on every mount
|
// reset form validation on every mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -45,7 +73,10 @@ export default function FormPublish({
|
|||||||
|
|
||||||
const resetFormAndClearStorage = (e: FormEvent<Element>) => {
|
const resetFormAndClearStorage = (e: FormEvent<Element>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
resetForm({ values: initialValues, status: 'empty' })
|
resetForm({
|
||||||
|
values: initialValuesDataset as MetadataPublishFormDataset,
|
||||||
|
status: 'empty'
|
||||||
|
})
|
||||||
setStatus('empty')
|
setStatus('empty')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +86,7 @@ export default function FormPublish({
|
|||||||
// do we need this?
|
// do we need this?
|
||||||
onChange={() => status === 'empty' && setStatus(null)}
|
onChange={() => status === 'empty' && setStatus(null)}
|
||||||
>
|
>
|
||||||
|
<h2 className={stylesIndex.formTitle}>{content.title}</h2>
|
||||||
{content.data.map((field: FormFieldProps) => (
|
{content.data.map((field: FormFieldProps) => (
|
||||||
<Field
|
<Field
|
||||||
key={field.name}
|
key={field.name}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
.tabElement,
|
|
||||||
button.tabElement,
|
|
||||||
.tabElement:hover,
|
|
||||||
.tabElement:active,
|
|
||||||
.tabElement:focus {
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
text-transform: uppercase;
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
margin-right: calc(var(--spacer) / 6);
|
|
||||||
margin-bottom: calc(var(--spacer) / 6);
|
|
||||||
color: var(--color-secondary);
|
|
||||||
background: var(--background-body);
|
|
||||||
|
|
||||||
/* the only thing not possible to overwrite button style="text" with more specifity of selectors, so sledgehammer */
|
|
||||||
padding: calc(var(--spacer) / 5) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabElement:hover,
|
|
||||||
.tabElement:focus {
|
|
||||||
color: var(--font-color-text);
|
|
||||||
background: inherit;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabElement.selected {
|
|
||||||
color: var(--background-body);
|
|
||||||
background: var(--font-color-text);
|
|
||||||
border-color: var(--background-body);
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect } from 'react'
|
|
||||||
import styles from './PublishType.module.css'
|
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
const publishTypes = [
|
|
||||||
{ display: 'data', value: 'data' },
|
|
||||||
{ display: 'algorithms', value: 'algorithms' }
|
|
||||||
]
|
|
||||||
|
|
||||||
export default function PublishType({
|
|
||||||
type,
|
|
||||||
setType
|
|
||||||
}: {
|
|
||||||
type: string
|
|
||||||
setType: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
}): ReactElement {
|
|
||||||
useEffect(() => {
|
|
||||||
setType(publishTypes[0].value)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{publishTypes.map((e, index) => {
|
|
||||||
const tabElement = cx({
|
|
||||||
[styles.selected]: e.value === type,
|
|
||||||
[styles.tabElement]: true
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
style="text"
|
|
||||||
key={index}
|
|
||||||
className={tabElement}
|
|
||||||
onClick={async () => {
|
|
||||||
setType(e.value)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{e.display}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,3 +1,16 @@
|
|||||||
|
.tabs ul[class*='tabList'] {
|
||||||
|
background-color: var(--background-content);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-top-left-radius: var(--border-radius);
|
||||||
|
border-top-right-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs div[class*='tabContent'] {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: calc(var(--spacer) * 1.5);
|
gap: calc(var(--spacer) * 1.5);
|
||||||
@ -16,8 +29,17 @@ div.alert {
|
|||||||
grid-template-columns: 1.618fr 1fr;
|
grid-template-columns: 1.618fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabs ul[class*='tabList'] {
|
||||||
|
/* fake the above 1.618fr column */
|
||||||
|
max-width: calc((100% / 1.618) - calc(var(--spacer) / 1.075));
|
||||||
|
}
|
||||||
|
|
||||||
.sticky {
|
.sticky {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: calc(var(--spacer) / 2);
|
top: calc(var(--spacer) / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.formTitle {
|
||||||
|
font-size: var(--font-size-h4);
|
||||||
|
}
|
||||||
|
@ -1,46 +1,111 @@
|
|||||||
import React, { ReactElement, useState } from 'react'
|
import React, { ReactElement, useState, useEffect } from 'react'
|
||||||
import { Formik } from 'formik'
|
import { Formik, FormikState } from 'formik'
|
||||||
import { usePublish, useOcean } from '@oceanprotocol/react'
|
import { usePublish, useOcean } from '@oceanprotocol/react'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import FormPublish from './FormPublish'
|
import FormPublish from './FormPublish'
|
||||||
import PublishType from './PublishType'
|
import FormAlgoPublish from './FormAlgoPublish'
|
||||||
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
import Web3Feedback from '../../molecules/Wallet/Feedback'
|
||||||
import { FormContent } from '../../../@types/Form'
|
import Tabs from '../../atoms/Tabs'
|
||||||
import { initialValues, validationSchema } from '../../../models/FormPublish'
|
import { initialValues, validationSchema } from '../../../models/FormPublish'
|
||||||
|
import {
|
||||||
|
initialValues as initialValuesAlgorithm,
|
||||||
|
validationSchema as validationSchemaAlgorithm
|
||||||
|
} from '../../../models/FormAlgoPublish'
|
||||||
import {
|
import {
|
||||||
transformPublishFormToMetadata,
|
transformPublishFormToMetadata,
|
||||||
mapTimeoutStringToSeconds
|
transformPublishAlgorithmFormToMetadata,
|
||||||
|
mapTimeoutStringToSeconds,
|
||||||
|
validateDockerImage
|
||||||
} from '../../../utils/metadata'
|
} from '../../../utils/metadata'
|
||||||
import MetadataPreview from '../../molecules/MetadataPreview'
|
import {
|
||||||
import { MetadataPublishForm } from '../../../@types/MetaData'
|
MetadataPreview,
|
||||||
|
MetadataAlgorithmPreview
|
||||||
|
} from '../../molecules/MetadataPreview'
|
||||||
|
import {
|
||||||
|
MetadataPublishFormDataset,
|
||||||
|
MetadataPublishFormAlgorithm
|
||||||
|
} from '../../../@types/MetaData'
|
||||||
import { useUserPreferences } from '../../../providers/UserPreferences'
|
import { useUserPreferences } from '../../../providers/UserPreferences'
|
||||||
import { Logger, Metadata } from '@oceanprotocol/lib'
|
import { Logger, Metadata, MetadataMain } from '@oceanprotocol/lib'
|
||||||
import { Persist } from '../../atoms/FormikPersist'
|
import { Persist } from '../../atoms/FormikPersist'
|
||||||
import Debug from './Debug'
|
import Debug from './Debug'
|
||||||
import Alert from '../../atoms/Alert'
|
import Alert from '../../atoms/Alert'
|
||||||
import MetadataFeedback from '../../molecules/MetadataFeedback'
|
import MetadataFeedback from '../../molecules/MetadataFeedback'
|
||||||
import Button from '../../atoms/Button'
|
|
||||||
|
|
||||||
const formName = 'ocean-publish-form'
|
const formNameDatasets = 'ocean-publish-form-datasets'
|
||||||
|
const formNameAlgorithms = 'ocean-publish-form-algorithms'
|
||||||
|
|
||||||
|
function TabContent({
|
||||||
|
publishType,
|
||||||
|
values
|
||||||
|
}: {
|
||||||
|
publishType: MetadataMain['type']
|
||||||
|
values: Partial<MetadataPublishFormAlgorithm | MetadataPublishFormDataset>
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<article className={styles.grid}>
|
||||||
|
{publishType === 'dataset' ? <FormPublish /> : <FormAlgoPublish />}
|
||||||
|
|
||||||
|
<aside>
|
||||||
|
<div className={styles.sticky}>
|
||||||
|
{publishType === 'dataset' ? (
|
||||||
|
<MetadataPreview values={values} />
|
||||||
|
) : (
|
||||||
|
<MetadataAlgorithmPreview values={values} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Web3Feedback />
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function PublishPage({
|
export default function PublishPage({
|
||||||
content
|
content
|
||||||
}: {
|
}: {
|
||||||
content: { warning: string; form: FormContent }
|
content: { warning: string }
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const { publish, publishError, isLoading, publishStepText } = usePublish()
|
const { publish, publishError, isLoading, publishStepText } = usePublish()
|
||||||
const { isInPurgatory, purgatoryData } = useOcean()
|
const { isInPurgatory, purgatoryData } = useOcean()
|
||||||
const [success, setSuccess] = useState<string>()
|
const [success, setSuccess] = useState<string>()
|
||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
|
const [title, setTitle] = useState<string>()
|
||||||
const [did, setDid] = useState<string>()
|
const [did, setDid] = useState<string>()
|
||||||
const [publishType, setPublishType] = useState<string>()
|
const [algoInitialValues, setAlgoInitialValues] = useState<
|
||||||
|
Partial<MetadataPublishFormAlgorithm>
|
||||||
|
>(
|
||||||
|
(localStorage.getItem('ocean-publish-form-algorithms') &&
|
||||||
|
(JSON.parse(localStorage.getItem('ocean-publish-form-algorithms'))
|
||||||
|
.initialValues as MetadataPublishFormAlgorithm)) ||
|
||||||
|
initialValuesAlgorithm
|
||||||
|
)
|
||||||
|
const [datasetInitialValues, setdatasetInitialValues] = useState<
|
||||||
|
Partial<MetadataPublishFormDataset>
|
||||||
|
>(
|
||||||
|
(localStorage.getItem('ocean-publish-form-datasets') &&
|
||||||
|
(JSON.parse(localStorage.getItem('ocean-publish-form-datasets'))
|
||||||
|
.initialValues as MetadataPublishFormDataset)) ||
|
||||||
|
initialValues
|
||||||
|
)
|
||||||
|
const [publishType, setPublishType] = useState<MetadataMain['type']>(
|
||||||
|
'dataset'
|
||||||
|
)
|
||||||
|
|
||||||
const hasFeedback = isLoading || error || success
|
const hasFeedback = isLoading || error || success
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
publishType === 'dataset'
|
||||||
|
? setTitle('Publishing Data Set')
|
||||||
|
: setTitle('Publishing Algorithm')
|
||||||
|
}, [publishType])
|
||||||
|
|
||||||
async function handleSubmit(
|
async function handleSubmit(
|
||||||
values: Partial<MetadataPublishForm>,
|
values: Partial<MetadataPublishFormDataset>,
|
||||||
resetForm: () => void
|
resetForm: (
|
||||||
|
nextState?: Partial<FormikState<Partial<MetadataPublishFormDataset>>>
|
||||||
|
) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const metadata = transformPublishFormToMetadata(values)
|
const metadata = transformPublishFormToMetadata(values)
|
||||||
const timeout = mapTimeoutStringToSeconds(values.timeout)
|
const timeout = mapTimeoutStringToSeconds(values.timeout)
|
||||||
@ -74,7 +139,53 @@ export default function PublishPage({
|
|||||||
setSuccess(
|
setSuccess(
|
||||||
'🎉 Successfully published. 🎉 Now create a price on your data set.'
|
'🎉 Successfully published. 🎉 Now create a price on your data set.'
|
||||||
)
|
)
|
||||||
resetForm()
|
resetForm({
|
||||||
|
values: initialValues as MetadataPublishFormDataset,
|
||||||
|
status: 'empty'
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
setError(error.message)
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAlgorithmSubmit(
|
||||||
|
values: Partial<MetadataPublishFormAlgorithm>,
|
||||||
|
resetForm: (
|
||||||
|
nextState?: Partial<FormikState<Partial<MetadataPublishFormAlgorithm>>>
|
||||||
|
) => void
|
||||||
|
): Promise<void> {
|
||||||
|
const metadata = transformPublishAlgorithmFormToMetadata(values)
|
||||||
|
const validDockerImage =
|
||||||
|
values.dockerImage === 'custom image'
|
||||||
|
? await validateDockerImage(values.image, values.containerTag)
|
||||||
|
: true
|
||||||
|
try {
|
||||||
|
if (validDockerImage) {
|
||||||
|
Logger.log('Publish Algorithm with ', metadata)
|
||||||
|
|
||||||
|
const ddo = await publish(
|
||||||
|
(metadata as unknown) as Metadata,
|
||||||
|
values.algorithmPrivacy === true ? 'compute' : 'access'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Publish failed
|
||||||
|
if (!ddo || publishError) {
|
||||||
|
setError(publishError || 'Publishing DDO failed.')
|
||||||
|
Logger.error(publishError || 'Publishing DDO failed.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish succeeded
|
||||||
|
setDid(ddo.id)
|
||||||
|
setSuccess(
|
||||||
|
'🎉 Successfully published. 🎉 Now create a price for your algorithm.'
|
||||||
|
)
|
||||||
|
resetForm({
|
||||||
|
values: initialValuesAlgorithm as MetadataPublishFormAlgorithm,
|
||||||
|
status: 'empty'
|
||||||
|
})
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(error.message)
|
setError(error.message)
|
||||||
Logger.error(error.message)
|
Logger.error(error.message)
|
||||||
@ -83,56 +194,83 @@ export default function PublishPage({
|
|||||||
|
|
||||||
return isInPurgatory && purgatoryData ? null : (
|
return isInPurgatory && purgatoryData ? null : (
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={
|
||||||
|
publishType === 'dataset' ? datasetInitialValues : algoInitialValues
|
||||||
|
}
|
||||||
initialStatus="empty"
|
initialStatus="empty"
|
||||||
validationSchema={validationSchema}
|
validationSchema={
|
||||||
|
publishType === 'dataset' ? validationSchema : validationSchemaAlgorithm
|
||||||
|
}
|
||||||
onSubmit={async (values, { resetForm }) => {
|
onSubmit={async (values, { resetForm }) => {
|
||||||
// move user's focus to top of screen
|
// move user's focus to top of screen
|
||||||
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
|
||||||
// kick off publishing
|
// kick off publishing
|
||||||
await handleSubmit(values, resetForm)
|
publishType === 'dataset'
|
||||||
|
? await handleSubmit(values, resetForm)
|
||||||
|
: await handleAlgorithmSubmit(values, resetForm)
|
||||||
}}
|
}}
|
||||||
|
enableReinitialize
|
||||||
>
|
>
|
||||||
{({ values }) => (
|
{({ values }) => {
|
||||||
<>
|
const tabs = [
|
||||||
<Persist name={formName} ignoreFields={['isSubmitting']} />
|
{
|
||||||
|
title: 'Data Set',
|
||||||
|
content: <TabContent values={values} publishType={publishType} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Algorithm',
|
||||||
|
content: <TabContent values={values} publishType={publishType} />
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
{hasFeedback ? (
|
return (
|
||||||
<MetadataFeedback
|
<>
|
||||||
title="Publishing Data Set"
|
<Persist
|
||||||
error={error}
|
name={
|
||||||
success={success}
|
publishType === 'dataset'
|
||||||
loading={publishStepText}
|
? formNameDatasets
|
||||||
setError={setError}
|
: formNameAlgorithms
|
||||||
successAction={{
|
}
|
||||||
name: 'Go to data set →',
|
ignoreFields={['isSubmitting']}
|
||||||
to: `/asset/${did}`
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<>
|
{hasFeedback ? (
|
||||||
<PublishType type={publishType} setType={setPublishType} />
|
<MetadataFeedback
|
||||||
<Alert
|
title={title}
|
||||||
text={content.warning}
|
error={error}
|
||||||
state="info"
|
success={success}
|
||||||
className={styles.alert}
|
loading={publishStepText}
|
||||||
|
setError={setError}
|
||||||
|
successAction={{
|
||||||
|
name: 'Go to data set →',
|
||||||
|
to: `/asset/${did}`
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<article className={styles.grid}>
|
) : (
|
||||||
<FormPublish content={content.form} />
|
<>
|
||||||
|
<Alert
|
||||||
|
text={content.warning}
|
||||||
|
state="info"
|
||||||
|
className={styles.alert}
|
||||||
|
/>
|
||||||
|
|
||||||
<aside>
|
<Tabs
|
||||||
<div className={styles.sticky}>
|
className={styles.tabs}
|
||||||
<MetadataPreview values={values} />
|
items={tabs}
|
||||||
<Web3Feedback />
|
handleTabChange={(title) => {
|
||||||
</div>
|
setPublishType(title.toLowerCase().replace(' ', '') as any)
|
||||||
</aside>
|
title === 'Algorithm'
|
||||||
</article>
|
? setdatasetInitialValues(values)
|
||||||
</>
|
: setAlgoInitialValues(values)
|
||||||
)}
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{debug === true && <Debug values={values} />}
|
{debug === true && <Debug values={values} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)
|
||||||
|
}}
|
||||||
</Formik>
|
</Formik>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ button.filter,
|
|||||||
color: var(--background-body);
|
color: var(--background-body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filterList:first-of-type {
|
||||||
|
margin-bottom: calc(var(--spacer) / 6);
|
||||||
|
}
|
||||||
|
|
||||||
.showClear {
|
.showClear {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
|
42
src/models/FormAlgoPublish.ts
Normal file
42
src/models/FormAlgoPublish.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { MetadataPublishFormAlgorithm } from '../@types/MetaData'
|
||||||
|
import { File as FileMetadata } from '@oceanprotocol/lib'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
export const validationSchema: Yup.SchemaOf<MetadataPublishFormAlgorithm> = Yup.object()
|
||||||
|
.shape({
|
||||||
|
// ---- required fields ----
|
||||||
|
name: Yup.string()
|
||||||
|
.min(4, (param) => `Title must be at least ${param.min} characters`)
|
||||||
|
.required('Required'),
|
||||||
|
description: Yup.string().min(10).required('Required'),
|
||||||
|
files: Yup.array<FileMetadata>().required('Required').nullable(),
|
||||||
|
dockerImage: Yup.string()
|
||||||
|
.matches(/node:pre-defined|python:pre-defined|custom image/g, {
|
||||||
|
excludeEmptyString: true
|
||||||
|
})
|
||||||
|
.required('Required'),
|
||||||
|
image: Yup.string().required('Required'),
|
||||||
|
containerTag: Yup.string().required('Required'),
|
||||||
|
entrypoint: Yup.string().required('Required'),
|
||||||
|
author: Yup.string().required('Required'),
|
||||||
|
termsAndConditions: Yup.boolean().required('Required'),
|
||||||
|
// ---- optional fields ----
|
||||||
|
algorithmPrivacy: Yup.boolean().nullable(),
|
||||||
|
tags: Yup.string().nullable(),
|
||||||
|
links: Yup.array<FileMetadata[]>().nullable()
|
||||||
|
})
|
||||||
|
.defined()
|
||||||
|
|
||||||
|
export const initialValues: Partial<MetadataPublishFormAlgorithm> = {
|
||||||
|
name: '',
|
||||||
|
author: '',
|
||||||
|
dockerImage: 'node:pre-defined',
|
||||||
|
image: 'node',
|
||||||
|
containerTag: '10',
|
||||||
|
entrypoint: 'node $ALGO',
|
||||||
|
files: '',
|
||||||
|
description: '',
|
||||||
|
algorithmPrivacy: false,
|
||||||
|
termsAndConditions: false,
|
||||||
|
tags: ''
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { MetadataMarket, MetadataPublishForm } from '../@types/MetaData'
|
import { MetadataMarket, MetadataPublishFormDataset } from '../@types/MetaData'
|
||||||
import { secondsToString } from '../utils/metadata'
|
import { secondsToString } from '../utils/metadata'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export const validationSchema = Yup.object().shape({
|
|||||||
export function getInitialValues(
|
export function getInitialValues(
|
||||||
metadata: MetadataMarket,
|
metadata: MetadataMarket,
|
||||||
timeout: number
|
timeout: number
|
||||||
): Partial<MetadataPublishForm> {
|
): Partial<MetadataPublishFormDataset> {
|
||||||
return {
|
return {
|
||||||
name: metadata.main.name,
|
name: metadata.main.name,
|
||||||
description: metadata.additionalInformation.description,
|
description: metadata.additionalInformation.description,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { MetadataPublishForm } from '../@types/MetaData'
|
import { MetadataPublishFormDataset } from '../@types/MetaData'
|
||||||
import { File as FileMetadata } from '@oceanprotocol/lib'
|
import { File as FileMetadata } from '@oceanprotocol/lib'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
export const validationSchema: Yup.SchemaOf<MetadataPublishForm> = Yup.object()
|
export const validationSchema: Yup.SchemaOf<MetadataPublishFormDataset> = Yup.object()
|
||||||
.shape({
|
.shape({
|
||||||
// ---- required fields ----
|
// ---- required fields ----
|
||||||
name: Yup.string()
|
name: Yup.string()
|
||||||
@ -29,7 +29,7 @@ export const validationSchema: Yup.SchemaOf<MetadataPublishForm> = Yup.object()
|
|||||||
})
|
})
|
||||||
.defined()
|
.defined()
|
||||||
|
|
||||||
export const initialValues: Partial<MetadataPublishForm> = {
|
export const initialValues: Partial<MetadataPublishFormDataset> = {
|
||||||
name: '',
|
name: '',
|
||||||
author: '',
|
author: '',
|
||||||
dataTokenOptions: {
|
dataTokenOptions: {
|
||||||
|
@ -4,7 +4,7 @@ import Page from '../components/templates/Page'
|
|||||||
import { graphql, PageProps } from 'gatsby'
|
import { graphql, PageProps } from 'gatsby'
|
||||||
|
|
||||||
export default function PageGatsbyPublish(props: PageProps): ReactElement {
|
export default function PageGatsbyPublish(props: PageProps): ReactElement {
|
||||||
const content = (props.data as any).content.edges[0].node.childPagesJson
|
const content = (props.data as any).content.edges[0].node.childPublishJson
|
||||||
const { title, description } = content
|
const { title, description } = content
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -16,27 +16,15 @@ export default function PageGatsbyPublish(props: PageProps): ReactElement {
|
|||||||
|
|
||||||
export const contentQuery = graphql`
|
export const contentQuery = graphql`
|
||||||
query PublishPageQuery {
|
query PublishPageQuery {
|
||||||
content: allFile(filter: { relativePath: { eq: "pages/publish.json" } }) {
|
content: allFile(
|
||||||
|
filter: { relativePath: { eq: "pages/publish/index.json" } }
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
childPagesJson {
|
childPublishJson {
|
||||||
title
|
title
|
||||||
description
|
description
|
||||||
warning
|
warning
|
||||||
form {
|
|
||||||
title
|
|
||||||
data {
|
|
||||||
name
|
|
||||||
placeholder
|
|
||||||
label
|
|
||||||
help
|
|
||||||
type
|
|
||||||
required
|
|
||||||
sortOptions
|
|
||||||
options
|
|
||||||
}
|
|
||||||
success
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import { MetadataMarket, MetadataPublishForm } from '../@types/MetaData'
|
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import isUrl from 'is-url-superb'
|
||||||
|
import {
|
||||||
|
MetadataMarket,
|
||||||
|
MetadataPublishFormDataset,
|
||||||
|
MetadataPublishFormAlgorithm
|
||||||
|
} from '../@types/MetaData'
|
||||||
import { toStringNoMS } from '.'
|
import { toStringNoMS } from '.'
|
||||||
import AssetModel from '../models/Asset'
|
import AssetModel from '../models/Asset'
|
||||||
import slugify from '@sindresorhus/slugify'
|
import slugify from '@sindresorhus/slugify'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO, MetadataAlgorithm, Logger } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
export function transformTags(value: string): string[] {
|
export function transformTags(value: string): string[] {
|
||||||
const originalTags = value?.split(',')
|
const originalTags = value?.split(',')
|
||||||
@ -66,6 +73,29 @@ export function checkIfTimeoutInPredefinedValues(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAlgoithComponent(
|
||||||
|
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 getAlgoithFileExtension(fileUrl: string): string {
|
||||||
|
const splitedFileUrl = fileUrl.split('.')
|
||||||
|
return splitedFileUrl[splitedFileUrl.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
export function transformPublishFormToMetadata(
|
export function transformPublishFormToMetadata(
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
@ -75,7 +105,7 @@ export function transformPublishFormToMetadata(
|
|||||||
links,
|
links,
|
||||||
termsAndConditions,
|
termsAndConditions,
|
||||||
files
|
files
|
||||||
}: Partial<MetadataPublishForm>,
|
}: Partial<MetadataPublishFormDataset>,
|
||||||
ddo?: DDO
|
ddo?: DDO
|
||||||
): MetadataMarket {
|
): MetadataMarket {
|
||||||
const currentTime = toStringNoMS(new Date())
|
const currentTime = toStringNoMS(new Date())
|
||||||
@ -100,3 +130,103 @@ export function transformPublishFormToMetadata(
|
|||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isDockerHubImageValid(
|
||||||
|
image: string,
|
||||||
|
tag: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`https://hub.docker.com/v2/repositories/${image}/tags/${tag}`
|
||||||
|
)
|
||||||
|
if (!response || response.status !== 200 || !response.data) {
|
||||||
|
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,
|
||||||
|
dockerImage,
|
||||||
|
image,
|
||||||
|
containerTag,
|
||||||
|
entrypoint,
|
||||||
|
termsAndConditions,
|
||||||
|
files
|
||||||
|
}: Partial<MetadataPublishFormAlgorithm>,
|
||||||
|
ddo?: DDO
|
||||||
|
): MetadataMarket {
|
||||||
|
const currentTime = toStringNoMS(new Date())
|
||||||
|
const fileUrl = typeof files !== 'string' && files[0].url
|
||||||
|
const algorithmLanguace = getAlgoithFileExtension(fileUrl)
|
||||||
|
const algorithm = getAlgoithComponent(
|
||||||
|
image,
|
||||||
|
containerTag,
|
||||||
|
entrypoint,
|
||||||
|
algorithmLanguace
|
||||||
|
)
|
||||||
|
const metadata: MetadataMarket = {
|
||||||
|
main: {
|
||||||
|
...AssetModel.main,
|
||||||
|
name,
|
||||||
|
type: 'algorithm',
|
||||||
|
author,
|
||||||
|
dateCreated: ddo ? ddo.created : currentTime,
|
||||||
|
files: typeof files !== 'string' && files,
|
||||||
|
license: 'https://market.oceanprotocol.com/terms',
|
||||||
|
algorithm: algorithm
|
||||||
|
},
|
||||||
|
additionalInformation: {
|
||||||
|
...AssetModel.additionalInformation,
|
||||||
|
description,
|
||||||
|
tags: transformTags(tags),
|
||||||
|
termsAndConditions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { MetadataPublishForm } from '../../../src/@types/MetaData'
|
import { MetadataPublishFormDataset } from '../../../src/@types/MetaData'
|
||||||
|
|
||||||
const testFormData: MetadataPublishForm = {
|
const testFormData: MetadataPublishFormDataset = {
|
||||||
author: '',
|
author: '',
|
||||||
files: [],
|
files: [],
|
||||||
dataTokenOptions: {
|
dataTokenOptions: {
|
||||||
|
@ -3,20 +3,19 @@ import { render } from '@testing-library/react'
|
|||||||
import { transformPublishFormToMetadata } from '../../../src/utils/metadata'
|
import { transformPublishFormToMetadata } from '../../../src/utils/metadata'
|
||||||
import {
|
import {
|
||||||
MetadataMarket,
|
MetadataMarket,
|
||||||
MetadataPublishForm
|
MetadataPublishFormDataset
|
||||||
} from '../../../src/@types/MetaData'
|
} from '../../../src/@types/MetaData'
|
||||||
import PublishForm from '../../../src/components/pages/Publish/FormPublish'
|
import PublishForm from '../../../src/components/pages/Publish/FormPublish'
|
||||||
import publishFormData from '../__fixtures__/testFormData'
|
import publishFormData from '../__fixtures__/testFormData'
|
||||||
import content from '../../../content/pages/publish.json'
|
|
||||||
|
|
||||||
describe('PublishForm', () => {
|
describe('PublishForm', () => {
|
||||||
it('renders without crashing', async () => {
|
it('renders without crashing', async () => {
|
||||||
const { container } = render(<PublishForm content={content.form} />)
|
const { container } = render(<PublishForm />)
|
||||||
expect(container.firstChild).toBeInTheDocument()
|
expect(container.firstChild).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
// it('Form data is correctly transformed to asset Metadata', () => {
|
// it('Form data is correctly transformed to asset Metadata', () => {
|
||||||
// const data: MetadataPublishForm = publishFormData
|
// const data: MetadataPublishFormDataset = publishFormData
|
||||||
// let metadata: MetadataMarket = transformPublishFormToMetadata(data)
|
// let metadata: MetadataMarket = transformPublishFormToMetadata(data)
|
||||||
|
|
||||||
// expect(metadata.additionalInformation).toBeDefined()
|
// expect(metadata.additionalInformation).toBeDefined()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render } from '@testing-library/react'
|
import { render } from '@testing-library/react'
|
||||||
import Publish from '../../../src/components/pages/Publish'
|
import Publish from '../../../src/components/pages/Publish'
|
||||||
import content from '../../../content/pages/publish.json'
|
import content from '../../../content/pages/publish/index.json'
|
||||||
|
|
||||||
describe('Home', () => {
|
describe('Home', () => {
|
||||||
it('renders without crashing', () => {
|
it('renders without crashing', () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user