From a313f39494d280f8e9f5e01e47fd2481d10ee91e Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Tue, 23 Nov 2021 15:34:43 +0000 Subject: [PATCH] file input validation fixes --- content/publish/form.json | 7 ++- .../@shared/FormFields/FilesInput/index.tsx | 8 ++- .../FormFields/URLInput/Input.module.css | 9 +++ .../@shared/FormFields/URLInput/Input.tsx | 55 +++++++++++++------ src/components/Publish/_constants.tsx | 23 ++++++-- src/components/Publish/_types.ts | 16 +++--- src/components/Publish/_utils.ts | 22 ++++---- 7 files changed, 94 insertions(+), 46 deletions(-) diff --git a/content/publish/form.json b/content/publish/form.json index c28acd1f5..b95e6a8e5 100644 --- a/content/publish/form.json +++ b/content/publish/form.json @@ -92,10 +92,11 @@ }, { "name": "providerUrl", - "label": "Custom Provider URL", + "label": "Provider URL", "type": "providerUrl", - "help": "Enter the URL for your custom provider or leave blank to use the default provider. [Learn more](https://github.com/oceanprotocol/provider/).", - "placeholder": "e.g. https://provider.polygon.oceanprotocol.com/" + "help": "Enter the URL for your custom [provider](https://github.com/oceanprotocol/provider/) or leave as is to use the default one. If you change your provider URL after adding your file, please add & validate your file again.", + "placeholder": "e.g. https://provider.oceanprotocol.com/", + "required": true }, { "name": "files", diff --git a/src/components/@shared/FormFields/FilesInput/index.tsx b/src/components/@shared/FormFields/FilesInput/index.tsx index 1e377b7f8..0cd4da1c4 100644 --- a/src/components/@shared/FormFields/FilesInput/index.tsx +++ b/src/components/@shared/FormFields/FilesInput/index.tsx @@ -2,7 +2,7 @@ import React, { ReactElement, useState, useEffect } from 'react' import { useField } from 'formik' import { toast } from 'react-toastify' import FileInfo from './Info' -import CustomInput from '../URLInput/Input' +import UrlInput from '../URLInput/Input' import { InputProps } from '@shared/FormInput' import { getFileInfo } from '@utils/provider' import { useWeb3 } from '@context/Web3' @@ -61,13 +61,15 @@ export default function FilesInput(props: InputProps): ReactElement { return ( <> - {field?.value && field.value[0] && typeof field.value === 'object' ? ( + {field?.value && field.value[0].url !== '' && field.value[0].valid ? ( ) : ( - diff --git a/src/components/@shared/FormFields/URLInput/Input.module.css b/src/components/@shared/FormFields/URLInput/Input.module.css index ac1350d31..7cc9b6283 100644 --- a/src/components/@shared/FormFields/URLInput/Input.module.css +++ b/src/components/@shared/FormFields/URLInput/Input.module.css @@ -1,3 +1,12 @@ .input { composes: input from '@shared/FormInput/InputElement.module.css'; } + +.hasError { + color: var(--brand-alert-red); + border-color: var(--brand-alert-red); +} + +.error { + composes: error from '@shared/FormInput/index.module.css'; +} diff --git a/src/components/@shared/FormFields/URLInput/Input.tsx b/src/components/@shared/FormFields/URLInput/Input.tsx index 3be11a425..e87af81f0 100644 --- a/src/components/@shared/FormFields/URLInput/Input.tsx +++ b/src/components/@shared/FormFields/URLInput/Input.tsx @@ -1,40 +1,59 @@ import React, { ReactElement } from 'react' import Button from '@shared/atoms/Button' -import { FieldInputProps, useField } from 'formik' +import { ErrorMessage, FieldInputProps, useField } from 'formik' import Loader from '@shared/atoms/Loader' import styles from './Input.module.css' import InputGroup from '@shared/FormInput/InputGroup' +import InputElement from '@shared/FormInput/InputElement' export default function URLInput({ submitText, handleButtonClick, isLoading, + name, + value, ...props }: { submitText: string handleButtonClick(e: React.SyntheticEvent, data: string): void isLoading: boolean + name: string + value: string }): ReactElement { - const [field, meta] = useField(props as FieldInputProps) + const [field, meta] = useField(name) const isButtonDisabled = !field.value || field.value.length === 0 || field.value === '' return ( - - handleButtonClick(e, field.value)} - /> - - + <> + + + + + + {meta.touched && meta.error && ( +
+ +
+ )} + ) } diff --git a/src/components/Publish/_constants.tsx b/src/components/Publish/_constants.tsx index a9ad45959..1cc408920 100644 --- a/src/components/Publish/_constants.tsx +++ b/src/components/Publish/_constants.tsx @@ -44,7 +44,6 @@ export const initialValues: FormPublishData = { author: '', description: '', tags: '', - links: [], termsAndConditions: false, dockerImage: '', dockerImageCustom: '', @@ -53,7 +52,8 @@ export const initialValues: FormPublishData = { }, services: [ { - files: undefined, + files: [{ url: '' }], + links: [{ url: '' }], dataTokenOptions: { name: '', symbol: '' }, timeout: '', access: '', @@ -94,10 +94,23 @@ const validationMetadata = { } const validationService = { - files: Yup.array() - .required('Enter a valid URL and click "ADD FILE"') + files: Yup.array<{ url: string; valid: boolean }[]>() + .of( + Yup.object().shape({ + url: Yup.string().required('Required'), + valid: Yup.boolean().isTrue().required('File must be valid.') + }) + ) + .min(1, (param) => `At least one file is required`) + .required('Enter a valid URL and click "ADD FILE"'), + links: Yup.array<{ url: string; valid: boolean }[]>() + .of( + Yup.object().shape({ + url: Yup.string().required('Required'), + valid: Yup.boolean().isTrue().required('File must be valid.') + }) + ) .nullable(), - links: Yup.array().nullable(), dataTokenOptions: Yup.object().shape({ name: Yup.string(), symbol: Yup.string() diff --git a/src/components/Publish/_types.ts b/src/components/Publish/_types.ts index f65dd20e3..aadb94eda 100644 --- a/src/components/Publish/_types.ts +++ b/src/components/Publish/_types.ts @@ -2,13 +2,16 @@ import { DataTokenOptions } from '@utils/datatokens' import { NftOptions } from '@utils/nft' import { ReactElement } from 'react' +interface FileMetadata { + url: string + valid?: boolean + contentLength?: string + contentType?: string +} + export interface FormPublishService { - files: { - url: string - valid: boolean - contentLength: string - contentType: string - }[] + files: FileMetadata[] + links?: FileMetadata[] timeout: string dataTokenOptions: DataTokenOptions access: 'Download' | 'Compute' | string @@ -30,7 +33,6 @@ export interface FormPublishData { author: string termsAndConditions: boolean tags?: string - links?: string[] dockerImage?: string dockerImageCustom?: string dockerImageCustomTag?: string diff --git a/src/components/Publish/_utils.ts b/src/components/Publish/_utils.ts index e45af0115..2fd710488 100644 --- a/src/components/Publish/_utils.ts +++ b/src/components/Publish/_utils.ts @@ -42,21 +42,17 @@ export async function transformPublishFormToDdo( name, description, tags, - links, author, termsAndConditions, dockerImageCustom, dockerImageCustomTag, dockerImageCustomEntrypoint } = metadata - const { access, files, providerUrl, timeout } = services[0] + const { access, files, links, providerUrl, timeout } = services[0] - const filesTransformed = files?.length && files[0].valid && [...files[0].url] - - const filesEncrypted = - files?.length && - files[0].valid && - (await getEncryptedFileUrls(filesTransformed, providerUrl, did, accountId)) + // Transform from files[0].url to string[] assuming only 1 file + const filesTransformed = files?.length && files[0].valid && [files[0].url] + const linksTransformed = links?.length && links[0].valid && [links[0].url] const newMetadata: Metadata = { created: currentTime, @@ -67,7 +63,7 @@ export async function transformPublishFormToDdo( tags: transformTags(tags), author, license: 'https://market.oceanprotocol.com/terms', - links, + links: linksTransformed, additionalInformation: { termsAndConditions }, @@ -85,9 +81,15 @@ export async function transformPublishFormToDdo( }) } + // Encypt just created string[] of urls + const filesEncrypted = + files?.length && + files[0].valid && + (await getEncryptedFileUrls(filesTransformed, providerUrl, did, accountId)) + const newService: Service = { type: access, - files: filesEncrypted, + files: filesEncrypted || '', datatokenAddress, serviceEndpoint: providerUrl, timeout: mapTimeoutStringToSeconds(timeout),