From 338e3aaa950cf718d0a9919627a47b4c1f2e2cf1 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Thu, 5 Dec 2024 17:06:45 -0800 Subject: [PATCH 01/17] Register fields with form to enable complete error checking --- .../examples/nextjs/package-lock.json | 4 +-- .../src/components/InternalField.tsx | 9 ++++-- .../connect-react/src/hooks/form-context.tsx | 29 +++++++++++++++++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/connect-react/examples/nextjs/package-lock.json b/packages/connect-react/examples/nextjs/package-lock.json index d4fed8c62dcb0..5d9f1e0430988 100644 --- a/packages/connect-react/examples/nextjs/package-lock.json +++ b/packages/connect-react/examples/nextjs/package-lock.json @@ -23,10 +23,10 @@ }, "../..": { "name": "@pipedream/connect-react", - "version": "1.0.0-preview.6", + "version": "1.0.0-preview.7", "license": "MIT", "dependencies": { - "@pipedream/sdk": "^1.0.6", + "@pipedream/sdk": "workspace:^", "@tanstack/react-query": "^5.59.16", "lodash.isequal": "^4.5.0", "react-markdown": "^9.0.1", diff --git a/packages/connect-react/src/components/InternalField.tsx b/packages/connect-react/src/components/InternalField.tsx index a3bee58dac710..2ad1fc661ba0b 100644 --- a/packages/connect-react/src/components/InternalField.tsx +++ b/packages/connect-react/src/components/InternalField.tsx @@ -3,6 +3,7 @@ import { FormFieldContext } from "../hooks/form-field-context"; import { useFormContext } from "../hooks/form-context"; import { Field } from "./Field"; import { useApp } from "../hooks/use-app"; +import { useEffect } from "react"; type FieldInternalProps = { prop: T; @@ -14,7 +15,7 @@ export function InternalField({ }: FieldInternalProps) { const formCtx = useFormContext(); const { - id: formId, configuredProps, setConfiguredProp, + id: formId, configuredProps, registerField, setConfiguredProp, } = formCtx; const appSlug = prop.type === "app" && "app" in prop @@ -44,7 +45,11 @@ export function InternalField({ app, // XXX fix ts }, }; - + useEffect(() => registerField(fieldCtx), [ + app, + configuredProps, + prop, + ]) return ( diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index b853561bd66c4..9818f8dd604e0 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -8,6 +8,7 @@ import type { } from "@pipedream/sdk"; import { useFrontendClient } from "./frontend-client-context"; import type { ComponentFormProps } from "../components/ComponentForm"; +import type { FormFieldContext } from "./form-field-context"; export type DynamicProps = { id: string; configurable_props: T; }; // TODO @@ -17,12 +18,14 @@ export type FormContext = { configuredProps: ConfiguredProps; dynamicProps?: DynamicProps; // lots of calls require dynamicProps?.id, so need to expose dynamicPropsQueryIsFetching?: boolean; + fields: Record>; id: string; isValid: boolean; optionalPropIsEnabled: (prop: ConfigurableProp) => boolean; optionalPropSetEnabled: (prop: ConfigurableProp, enabled: boolean) => void; props: ComponentFormProps; queryDisabledIdx?: number; + registerField: (field: FormFieldContext) => void; setConfiguredProp: (idx: number, value: unknown) => void; // XXX type safety for value (T will rarely be static right?) setSubmitting: (submitting: boolean) => void; submitting: boolean; @@ -64,6 +67,10 @@ export const FormContextProvider = ({ queryDisabledIdx, setQueryDisabledIdx, ] = useState(0); + const [ + fields, + setFields, + ] = useState>>({}); const [ submitting, setSubmitting, @@ -173,7 +180,14 @@ export const FormContextProvider = ({ errs.push("not a string"); } } else if (prop.type === "app") { - // TODO need to know about auth type + const field = fields[prop.name] + if (!field.extra.app) { + errs.push("app field not registered") + } else if (!value) { + errs.push("no app configured") + } else if (typeof value === "object" && "authProvisionId" in value && !value.authProvisionId) { + errs.push("no auth provision configured") + } } return errs; }; @@ -241,7 +255,10 @@ export const FormContextProvider = ({ ]); // clear all props on user change - const [prevUserId, setPrevUserId] = useState(userId) + const [ + prevUserId, + setPrevUserId, + ] = useState(userId) useEffect(() => { if (prevUserId !== userId) { updateConfiguredProps({}); @@ -299,6 +316,11 @@ export const FormContextProvider = ({ setEnabledOptionalProps(newEnabledOptionalProps); }; + const registerField = (field: FormFieldContext) => { + fields[field.prop.name] = field + setFields(fields); + }; + // console.log("***", configurableProps, configuredProps) const value: FormContext = { id, @@ -310,9 +332,12 @@ export const FormContextProvider = ({ configuredProps, dynamicProps, dynamicPropsQueryIsFetching, + errors, + fields, optionalPropIsEnabled, optionalPropSetEnabled, queryDisabledIdx, + registerField, setConfiguredProp, setSubmitting, submitting, From 94f75d3547ae0e1f081bd6fc0097ea1473d90757 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Mon, 9 Dec 2024 14:52:58 -0800 Subject: [PATCH 02/17] Wire up the submit button to propsNeedConfiguring --- .../examples/nextjs/src/app/page.tsx | 6 ++ .../src/components/ControlSubmit.tsx | 18 +++-- .../connect-react/src/hooks/form-context.tsx | 41 +++++++++--- packages/connect-react/src/hooks/use-app.tsx | 65 ++++++++++++++++++- 4 files changed, 114 insertions(+), 16 deletions(-) diff --git a/packages/connect-react/examples/nextjs/src/app/page.tsx b/packages/connect-react/examples/nextjs/src/app/page.tsx index 283e783623c1f..7686506ed3060 100644 --- a/packages/connect-react/examples/nextjs/src/app/page.tsx +++ b/packages/connect-react/examples/nextjs/src/app/page.tsx @@ -30,6 +30,12 @@ export default function Home() { componentKey="slack-send-message" configuredProps={configuredProps} onUpdateConfiguredProps={setConfiguredProps} + onSubmit={async () => { + await client.actionRun({ + userId, + actionId: "slack-send-message", + configuredProps, + })}} /> diff --git a/packages/connect-react/src/components/ControlSubmit.tsx b/packages/connect-react/src/components/ControlSubmit.tsx index 83e6e3f789769..a25f39c7f8ae8 100644 --- a/packages/connect-react/src/components/ControlSubmit.tsx +++ b/packages/connect-react/src/components/ControlSubmit.tsx @@ -8,16 +8,22 @@ export type ControlSubmitProps = { export function ControlSubmit(props: ControlSubmitProps) { const { form } = props; - const { submitting } = form; + const { + propsNeedConfiguring, submitting, + } = form; const { getProps, theme, } = useCustomize(); - const baseStyles: CSSProperties = { + const baseStyles = (disabled: boolean): CSSProperties => ({ width: "fit-content", textTransform: "capitalize", - backgroundColor: theme.colors.primary, - color: theme.colors.neutral0, + backgroundColor: disabled + ? theme.colors.neutral10 + : theme.colors.primary, + color: disabled + ? theme.colors.neutral40 + : theme.colors.neutral0, padding: `${theme.spacing.baseUnit * 1.75}px ${ theme.spacing.baseUnit * 16 }px`, @@ -29,9 +35,9 @@ export function ControlSubmit(props: ControlSubmitProps) { ? 0.5 : undefined, margin: "0.5rem 0 0 0", - }; + }); return ; + : "Submit"} {...getProps("controlSubmit", baseStyles(propsNeedConfiguring.length || submitting), props)} disabled={propsNeedConfiguring.length || submitting} />; } diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 9818f8dd604e0..2bf5d250f0829 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -4,11 +4,12 @@ import { import isEqual from "lodash.isequal"; import { useQuery } from "@tanstack/react-query"; import type { - ComponentReloadPropsOpts, ConfigurableProp, ConfigurableProps, ConfiguredProps, V1Component, + ComponentReloadPropsOpts, ConfigurableProp, ConfigurableProps, ConfiguredProps, V1Component, PropValue, } from "@pipedream/sdk"; import { useFrontendClient } from "./frontend-client-context"; import type { ComponentFormProps } from "../components/ComponentForm"; import type { FormFieldContext } from "./form-field-context"; +import { appPropError } from "./use-app"; export type DynamicProps = { id: string; configurable_props: T; }; // TODO @@ -24,6 +25,7 @@ export type FormContext = { optionalPropIsEnabled: (prop: ConfigurableProp) => boolean; optionalPropSetEnabled: (prop: ConfigurableProp, enabled: boolean) => void; props: ComponentFormProps; + propsNeedConfiguring: string[]; queryDisabledIdx?: number; registerField: (field: FormFieldContext) => void; setConfiguredProp: (idx: number, value: unknown) => void; // XXX type safety for value (T will rarely be static right?) @@ -136,6 +138,16 @@ export const FormContextProvider = ({ enabled: reloadPropIdx != null, // TODO or props.dynamicPropsId && !dynamicProps }); + const [ + propsNeedConfiguring, + setPropsNeedConfiguring, + ] = useState([]); + useEffect(() => { + checkPropsNeedConfiguring() + }, [ + configuredProps, + ]); + // XXX fix types of dynamicProps, props.component so this type decl not needed let configurableProps: T = dynamicProps?.configurable_props || formProps.component.configurable_props || []; if (propNames?.length) { @@ -154,7 +166,7 @@ export const FormContextProvider = ({ // these validations are necessary because they might override PropInput for number case for instance // so can't rely on that base control form validation - const propErrors = (prop: ConfigurableProp, value: unknown): string[] => { + const propErrors = (prop: ConfigurableProp, value: unknown): string[] => { const errs: string[] = []; if (value === undefined) { if (!prop.optional) { @@ -181,13 +193,9 @@ export const FormContextProvider = ({ } } else if (prop.type === "app") { const field = fields[prop.name] - if (!field.extra.app) { - errs.push("app field not registered") - } else if (!value) { - errs.push("no app configured") - } else if (typeof value === "object" && "authProvisionId" in value && !value.authProvisionId) { - errs.push("no auth provision configured") - } + const app = field.extra.app + const err = appPropError(prop, value, app) + if (err) errs.push(err) } return errs; }; @@ -316,6 +324,20 @@ export const FormContextProvider = ({ setEnabledOptionalProps(newEnabledOptionalProps); }; + const checkPropsNeedConfiguring = () => { + const _propsNeedConfiguring = [] + for (const prop of configurableProps) { + if (!prop || prop.optional || prop.hidden) continue + const value = configuredProps[prop.name as keyof ConfiguredProps] + const errors = propErrors(prop, value) + if (errors.length) { + _propsNeedConfiguring.push(prop.name) + } + } + // propsNeedConfiguring.splice(0, propsNeedConfiguring.length, ..._propsNeedConfiguring) + setPropsNeedConfiguring(_propsNeedConfiguring) + } + const registerField = (field: FormFieldContext) => { fields[field.prop.name] = field setFields(fields); @@ -336,6 +358,7 @@ export const FormContextProvider = ({ fields, optionalPropIsEnabled, optionalPropSetEnabled, + propsNeedConfiguring, queryDisabledIdx, registerField, setConfiguredProp, diff --git a/packages/connect-react/src/hooks/use-app.tsx b/packages/connect-react/src/hooks/use-app.tsx index a41252d615f12..59aa369cdd0ce 100644 --- a/packages/connect-react/src/hooks/use-app.tsx +++ b/packages/connect-react/src/hooks/use-app.tsx @@ -2,7 +2,10 @@ import { useQuery, type UseQueryOptions, } from "@tanstack/react-query"; import { useFrontendClient } from "./frontend-client-context"; -import type { AppRequestResponse } from "@pipedream/sdk"; +import type { + AppRequestResponse, AppResponse, ConfigurablePropApp, + PropValue, +} from "@pipedream/sdk"; /** * Get details about an app @@ -23,3 +26,63 @@ export const useApp = (slug: string, opts?:{ useQueryOpts?: Omit & { + oauth_access_token?: string +} + +function getCustomFields(app: AppResponse): AppCustomField[] { + const isOauth = app.auth_type === "oauth" + const userDefinedCustomFields = JSON.parse(app.custom_fields_json || "[]") + if ("extracted_custom_fields_names" in app && app.extracted_custom_fields_names) { + const extractedCustomFields = ((app as AppResponseWithExtractedCustomFields).extracted_custom_fields_names || []).map( + (name) => ({ + name, + }), + ) + userDefinedCustomFields.push(...extractedCustomFields) + } + return userDefinedCustomFields.map((cf: AppCustomField) => { + return { + ...cf, + // if oauth, treat all as optional (they are usually needed for getting access token) + optional: cf.optional || isOauth, + } + }) +} + +export function appPropError(prop: ConfigurablePropApp, value: unknown, app: AppResponse | undefined): string | undefined { + console.log("appPropError", prop, value, app) + if (!app) { + return "app field not registered" + } + if (!value) { + return "no app configured" + } + if (typeof value !== "object") { + return "not an app" + } + const _value = value as PropValue<"app"> + if ("authProvisionId" in _value && !_value.authProvisionId) { + if (app.auth_type) { + if (app.auth_type === "oauth" && !(_value as OauthAppPropValue).oauth_access_token) { + return "missing oauth token" + } + for (const cf of getCustomFields(app)) { + if (!cf.optional && !_value[cf.name]) { + return "missing custom field" + } + } + return "no auth provision configured" + } + } +} From ab8d310fceb4acca2867268290069d900090f6fe Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Mon, 9 Dec 2024 15:05:00 -0800 Subject: [PATCH 03/17] Update packages/connect-react/src/hooks/form-context.tsx Co-authored-by: Moshe Grunwald <34072688+TheBestMoshe@users.noreply.github.com> --- packages/connect-react/src/hooks/form-context.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 2bf5d250f0829..42657fd76c92f 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -339,8 +339,10 @@ export const FormContextProvider = ({ } const registerField = (field: FormFieldContext) => { - fields[field.prop.name] = field - setFields(fields); + setFields((fields) => { + fields[field.prop.name] = field + return fields + }); }; // console.log("***", configurableProps, configuredProps) From d0c6e7a089761c360e7288f4eebf300963677d92 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Mon, 9 Dec 2024 15:10:23 -0800 Subject: [PATCH 04/17] Fix auth_type check and console.log --- packages/connect-react/src/hooks/use-app.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/connect-react/src/hooks/use-app.tsx b/packages/connect-react/src/hooks/use-app.tsx index 59aa369cdd0ce..77843b7fc71cd 100644 --- a/packages/connect-react/src/hooks/use-app.tsx +++ b/packages/connect-react/src/hooks/use-app.tsx @@ -61,7 +61,6 @@ function getCustomFields(app: AppResponse): AppCustomField[] { } export function appPropError(prop: ConfigurablePropApp, value: unknown, app: AppResponse | undefined): string | undefined { - console.log("appPropError", prop, value, app) if (!app) { return "app field not registered" } @@ -77,9 +76,11 @@ export function appPropError(prop: ConfigurablePropApp, value: unknown, app: App if (app.auth_type === "oauth" && !(_value as OauthAppPropValue).oauth_access_token) { return "missing oauth token" } - for (const cf of getCustomFields(app)) { - if (!cf.optional && !_value[cf.name]) { - return "missing custom field" + if (app.auth_type === "oauth" || app.auth_type === "keys") { + for (const cf of getCustomFields(app)) { + if (!cf.optional && !_value[cf.name]) { + return "missing custom field" + } } } return "no auth provision configured" From 689a49771518ef5e22a6c3b189031cdff236dab5 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Mon, 9 Dec 2024 15:21:29 -0800 Subject: [PATCH 05/17] Versioning --- packages/connect-react/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/connect-react/CHANGELOG.md b/packages/connect-react/CHANGELOG.md index 50e954c04223e..bb4b1a2a29f80 100644 --- a/packages/connect-react/CHANGELOG.md +++ b/packages/connect-react/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog +# [1.0.0-preview.8] - 2024-12-09 + +- Disabled submit button when form is incomplete + # [1.0.0-preview.7] - 2024-12-05 - Use proper casing for `stringOptions` now that configure prop is properly async From a22d1f5b33bdc8263291271bedf2b6c49cde2930 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Mon, 9 Dec 2024 15:22:56 -0800 Subject: [PATCH 06/17] lockfile --- pnpm-lock.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62738145368dd..98520635d6ebd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30647,8 +30647,6 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) - transitivePeerDependencies: - - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: From 0387ab4bbe2c05e190236ce5fb468ac1dca8833b Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Tue, 10 Dec 2024 10:33:29 -0800 Subject: [PATCH 07/17] Update packages/connect-react/examples/nextjs/src/app/page.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../examples/nextjs/src/app/page.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/connect-react/examples/nextjs/src/app/page.tsx b/packages/connect-react/examples/nextjs/src/app/page.tsx index 7686506ed3060..7aa59910fa952 100644 --- a/packages/connect-react/examples/nextjs/src/app/page.tsx +++ b/packages/connect-react/examples/nextjs/src/app/page.tsx @@ -31,11 +31,17 @@ export default function Home() { configuredProps={configuredProps} onUpdateConfiguredProps={setConfiguredProps} onSubmit={async () => { - await client.actionRun({ - userId, - actionId: "slack-send-message", - configuredProps, - })}} + try { + await client.actionRun({ + userId, + actionId: "slack-send-message", + configuredProps, + }); + } catch (error) { + console.error('Action run failed:', error); + // Consider showing user-friendly error message + } + }} /> From 927a7d317fef84a6a516b52f7f7d66f74212fc49 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Tue, 10 Dec 2024 10:36:34 -0800 Subject: [PATCH 08/17] Update packages/connect-react/src/hooks/form-context.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- packages/connect-react/src/hooks/form-context.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 6acf2ea6fe173..8c6f427d3ce35 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -193,9 +193,13 @@ export const FormContextProvider = ({ } } else if (prop.type === "app") { const field = fields[prop.name] - const app = field.extra.app - const err = appPropError(prop, value, app) - if (err) errs.push(err) + if (field) { + const app = field.extra.app + const err = appPropError(prop, value, app) + if (err) errs.push(err) + } else { + errs.push("field not registered") + } } return errs; }; From ea8eba029e4c92652d2610f4e8767cc52d5720a9 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Tue, 10 Dec 2024 10:44:23 -0800 Subject: [PATCH 09/17] Fix PR comment --- .../connect-react/src/components/InternalField.tsx | 4 +--- pnpm-lock.yaml | 10 ++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/connect-react/src/components/InternalField.tsx b/packages/connect-react/src/components/InternalField.tsx index 2ad1fc661ba0b..047d714914891 100644 --- a/packages/connect-react/src/components/InternalField.tsx +++ b/packages/connect-react/src/components/InternalField.tsx @@ -46,9 +46,7 @@ export function InternalField({ }, }; useEffect(() => registerField(fieldCtx), [ - app, - configuredProps, - prop, + fieldCtx, ]) return ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 98520635d6ebd..42b6bd3cce5b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24474,22 +24474,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} @@ -30647,6 +30647,8 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: From 55725f6acbb304a95a40e25fcdd1ccb8cc2bae19 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Tue, 10 Dec 2024 10:57:49 -0800 Subject: [PATCH 10/17] Fix typing issue --- packages/connect-react/examples/nextjs/package-lock.json | 2 +- packages/connect-react/src/hooks/form-context.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/connect-react/examples/nextjs/package-lock.json b/packages/connect-react/examples/nextjs/package-lock.json index 5d9f1e0430988..0351814ee6464 100644 --- a/packages/connect-react/examples/nextjs/package-lock.json +++ b/packages/connect-react/examples/nextjs/package-lock.json @@ -23,7 +23,7 @@ }, "../..": { "name": "@pipedream/connect-react", - "version": "1.0.0-preview.7", + "version": "1.0.0-preview.8", "license": "MIT", "dependencies": { "@pipedream/sdk": "workspace:^", diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 8c6f427d3ce35..bd251c13b103e 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -360,7 +360,6 @@ export const FormContextProvider = ({ configuredProps, dynamicProps, dynamicPropsQueryIsFetching, - errors, fields, optionalPropIsEnabled, optionalPropSetEnabled, From 3fbd7eb9e5e961049a9bb177d7e5a2949f9ccdfb Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Tue, 10 Dec 2024 11:09:43 -0800 Subject: [PATCH 11/17] PR feedback --- packages/connect-react/src/hooks/form-context.tsx | 2 +- packages/connect-react/src/hooks/use-app.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index bd251c13b103e..374203ca4d8fd 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -195,7 +195,7 @@ export const FormContextProvider = ({ const field = fields[prop.name] if (field) { const app = field.extra.app - const err = appPropError(prop, value, app) + const err = appPropError({ value, app }) if (err) errs.push(err) } else { errs.push("field not registered") diff --git a/packages/connect-react/src/hooks/use-app.tsx b/packages/connect-react/src/hooks/use-app.tsx index 77843b7fc71cd..4dca554fb3246 100644 --- a/packages/connect-react/src/hooks/use-app.tsx +++ b/packages/connect-react/src/hooks/use-app.tsx @@ -60,7 +60,8 @@ function getCustomFields(app: AppResponse): AppCustomField[] { }) } -export function appPropError(prop: ConfigurablePropApp, value: unknown, app: AppResponse | undefined): string | undefined { +export function appPropError(opts: { value: any, app: AppResponse | undefined }): string | undefined { + const { app, value } = opts if (!app) { return "app field not registered" } From 6b3d1d444ed0d075da12eaf5f579d108f5c4688e Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Tue, 10 Dec 2024 11:19:19 -0800 Subject: [PATCH 12/17] linting --- packages/connect-react/examples/nextjs/src/app/page.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/connect-react/examples/nextjs/src/app/page.tsx b/packages/connect-react/examples/nextjs/src/app/page.tsx index 7aa59910fa952..6de7fa73bebd0 100644 --- a/packages/connect-react/examples/nextjs/src/app/page.tsx +++ b/packages/connect-react/examples/nextjs/src/app/page.tsx @@ -38,8 +38,7 @@ export default function Home() { configuredProps, }); } catch (error) { - console.error('Action run failed:', error); - // Consider showing user-friendly error message + console.error("Action run failed:", error); } }} /> From 52031cf8f5ed4b2a8c723e542ec6fd757523243f Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Thu, 12 Dec 2024 12:07:54 -0800 Subject: [PATCH 13/17] Enforce string length requirements --- packages/connect-react/src/hooks/form-context.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 374203ca4d8fd..fa844c576eed2 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -188,8 +188,14 @@ export const FormContextProvider = ({ errs.push("not a boolean"); } } else if (prop.type === "string") { + const { min = 1, max } = prop as unknown as { min?: number, max?: number } if (typeof value !== "string") { errs.push("not a string"); + } else { + if (value.length < min) + errs.push("string too short"); + if (max && value.length > max) + errs.push("string too long"); } } else if (prop.type === "app") { const field = fields[prop.name] From 6e17239fe3fc9d3943c4b07ac81d1cced1c17ebc Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Thu, 12 Dec 2024 12:11:18 -0800 Subject: [PATCH 14/17] Versioning --- packages/connect-react/CHANGELOG.md | 4 ++++ packages/connect-react/package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/connect-react/CHANGELOG.md b/packages/connect-react/CHANGELOG.md index 15083925250d9..dc9225596572a 100644 --- a/packages/connect-react/CHANGELOG.md +++ b/packages/connect-react/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog +# [1.0.0-preview.10] - 2024-12-12 + +- Enforce string length limits + # [1.0.0-preview.9] - 2024-12-10 - Disabled submit button when form is incomplete diff --git a/packages/connect-react/package.json b/packages/connect-react/package.json index 8b8207854ccd8..d6d7388c8d590 100644 --- a/packages/connect-react/package.json +++ b/packages/connect-react/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/connect-react", - "version": "1.0.0-preview.9", + "version": "1.0.0-preview.10", "description": "Pipedream Connect library for React", "files": [ "dist" From 3924afc51037c40744792714c383a0f1034c254a Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Thu, 12 Dec 2024 12:23:08 -0800 Subject: [PATCH 15/17] Update packages/connect-react/src/hooks/form-context.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../connect-react/src/hooks/form-context.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index fa844c576eed2..2c0f9dbfcd5ad 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -188,14 +188,21 @@ export const FormContextProvider = ({ errs.push("not a boolean"); } } else if (prop.type === "string") { - const { min = 1, max } = prop as unknown as { min?: number, max?: number } + interface StringProp extends ConfigurableProp { + min?: number; + max?: number; + } + const { min = 0, max } = prop as StringProp; if (typeof value !== "string") { errs.push("not a string"); } else { - if (value.length < min) - errs.push("string too short"); - if (max && value.length > max) - errs.push("string too long"); + if (value.length < min) { + errs.push(`string length must be at least ${min} characters`); + } + if (max && value.length > max) { + errs.push(`string length must not exceed ${max} characters`); + } + } } } else if (prop.type === "app") { const field = fields[prop.name] From 44fe957e448100b780a11183fa93d4a7bf8ac423 Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Thu, 12 Dec 2024 12:32:41 -0800 Subject: [PATCH 16/17] Fix coderabbit suggestion --- packages/connect-react/src/hooks/form-context.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 2c0f9dbfcd5ad..72a76a4f9a51d 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -188,11 +188,13 @@ export const FormContextProvider = ({ errs.push("not a boolean"); } } else if (prop.type === "string") { - interface StringProp extends ConfigurableProp { + type StringProp = ConfigurableProp & { min?: number; max?: number; } - const { min = 0, max } = prop as StringProp; + const { + min = 1, max, + } = prop as StringProp; if (typeof value !== "string") { errs.push("not a string"); } else { @@ -203,12 +205,14 @@ export const FormContextProvider = ({ errs.push(`string length must not exceed ${max} characters`); } } - } } else if (prop.type === "app") { const field = fields[prop.name] if (field) { const app = field.extra.app - const err = appPropError({ value, app }) + const err = appPropError({ + value, + app, + }) if (err) errs.push(err) } else { errs.push("field not registered") From 9c3edea676946d68a3487260331321da1148c5ec Mon Sep 17 00:00:00 2001 From: adolfo-pd Date: Thu, 12 Dec 2024 12:39:42 -0800 Subject: [PATCH 17/17] Type errors --- packages/connect-react/src/hooks/form-context.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/connect-react/src/hooks/form-context.tsx b/packages/connect-react/src/hooks/form-context.tsx index 72a76a4f9a51d..a13acc54e74b6 100644 --- a/packages/connect-react/src/hooks/form-context.tsx +++ b/packages/connect-react/src/hooks/form-context.tsx @@ -4,7 +4,7 @@ import { import isEqual from "lodash.isequal"; import { useQuery } from "@tanstack/react-query"; import type { - ComponentReloadPropsOpts, ConfigurableProp, ConfigurableProps, ConfiguredProps, V1Component, PropValue, + ComponentReloadPropsOpts, ConfigurableProp, ConfigurableProps, ConfiguredProps, V1Component, } from "@pipedream/sdk"; import { useFrontendClient } from "./frontend-client-context"; import type { ComponentFormProps } from "../components/ComponentForm"; @@ -166,7 +166,7 @@ export const FormContextProvider = ({ // these validations are necessary because they might override PropInput for number case for instance // so can't rely on that base control form validation - const propErrors = (prop: ConfigurableProp, value: unknown): string[] => { + const propErrors = (prop: ConfigurableProp, value: unknown): string[] => { const errs: string[] = []; if (value === undefined) { if (!prop.optional) {