From fca9a30d36a5b081dd2e5d25ecf95a142cb93c9c Mon Sep 17 00:00:00 2001 From: kielbasa-elp Date: Wed, 13 Mar 2024 17:01:19 +0100 Subject: [PATCH] Add support for adding metadata in websockets --- .../pages/pipelines/RunPipelineProvider.tsx | 11 +- .../pages/pipelines/build/BuilderHeader.tsx | 7 +- .../pages/pipelines/build/Metadata.tsx | 146 ++++++++++++++++++ .../components/pages/pipelines/build/page.tsx | 2 +- .../pages/pipelines/usePipelineRun.tsx | 14 +- apps/web-remix/package.json | 2 +- apps/web-remix/pnpm-lock.yaml | 8 +- 7 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 apps/web-remix/app/components/pages/pipelines/build/Metadata.tsx diff --git a/apps/web-remix/app/components/pages/pipelines/RunPipelineProvider.tsx b/apps/web-remix/app/components/pages/pipelines/RunPipelineProvider.tsx index 525f2a80a..016f539c1 100644 --- a/apps/web-remix/app/components/pages/pipelines/RunPipelineProvider.tsx +++ b/apps/web-remix/app/components/pages/pipelines/RunPipelineProvider.tsx @@ -12,6 +12,8 @@ import { usePipelineRun } from "./usePipelineRun"; import { IBlockConfig, IExtendedPipeline } from "./pipeline.types"; import { errorToast } from "~/components/toasts/errorToast"; +export type Metadata = Record; + export interface IEvent { block: string; output: string; @@ -34,6 +36,8 @@ interface IRunPipelineContext { isValid: boolean; organizationId: number; pipelineId: number; + metadata: Metadata; + setMetadata: (value: Metadata) => void; } const RunPipelineContext = React.createContext( @@ -49,6 +53,7 @@ export const RunPipelineProvider: React.FC = ({ pipeline, alias, }) => { + const [metadata, setMetadata] = useState({}); const [errors, setErrors] = useState>({}); const [events, setEvents] = useState([]); const [blockStatuses, setBlockStatuses] = useState>( @@ -93,7 +98,7 @@ export const RunPipelineProvider: React.FC = ({ const handleStartRun = useCallback(async () => { setErrors({}); - await startRun([], alias); + await startRun({ initial_inputs: [], alias, metadata }); }, [startRun, alias]); const handlePush = useCallback( @@ -155,6 +160,8 @@ export const RunPipelineProvider: React.FC = ({ organizationId: pipeline.organization_id, pipelineId: pipeline.id, errors, + metadata, + setMetadata, }), [ errors, @@ -168,6 +175,8 @@ export const RunPipelineProvider: React.FC = ({ blockStatuses, blockValidations, isValid, + metadata, + setMetadata, ] ); return ( diff --git a/apps/web-remix/app/components/pages/pipelines/build/BuilderHeader.tsx b/apps/web-remix/app/components/pages/pipelines/build/BuilderHeader.tsx index 0d9594d46..a228a5ee6 100644 --- a/apps/web-remix/app/components/pages/pipelines/build/BuilderHeader.tsx +++ b/apps/web-remix/app/components/pages/pipelines/build/BuilderHeader.tsx @@ -4,11 +4,16 @@ import { RunPipelineButton } from "./RunPipelineButton"; import { useRunPipeline } from "../RunPipelineProvider"; import { IPipelineConfig } from "~/components/pages/pipelines/pipeline.types"; import { useDebounce, useIsFirstRender } from "usehooks-ts"; +import { MetadataField } from "./Metadata"; export const BuilderHeader: React.FC = ({ children }) => { return (
- +
+ + + +
{children} diff --git a/apps/web-remix/app/components/pages/pipelines/build/Metadata.tsx b/apps/web-remix/app/components/pages/pipelines/build/Metadata.tsx new file mode 100644 index 000000000..b80c3da79 --- /dev/null +++ b/apps/web-remix/app/components/pages/pipelines/build/Metadata.tsx @@ -0,0 +1,146 @@ +import React, { ButtonHTMLAttributes, PropsWithChildren, useRef } from "react"; +import { Icon } from "@elpassion/taco"; +import classNames from "classnames"; +import { useBoolean, useOnClickOutside } from "usehooks-ts"; +import { + useRunPipeline, + Metadata as IMetadata, +} from "~/components/pages/pipelines/RunPipelineProvider"; +import { z } from "zod"; +import { withZod } from "@remix-validated-form/with-zod"; +import { useFormContext, ValidatedForm } from "remix-validated-form"; +import { Field } from "~/components/form/fields/field.context"; +import { MonacoEditorField } from "~/components/form/fields/monacoEditor.field"; +import { SubmitButton } from "~/components/form/submit"; + +export const MetadataField = () => { + const { metadata, setMetadata } = useRunPipeline(); + + return ( + + + + ); +}; + +function Metadata({ children }: PropsWithChildren) { + const wrapperRef = useRef(null); + const { value: isShown, setFalse, toggle } = useBoolean(false); + + const hide = () => { + setFalse(); + }; + + useOnClickOutside(wrapperRef, hide); + + return ( +
+ + + {isShown && ( + + {children} + + )} +
+ ); +} + +const metadataSchema = z.object({ + value: z.string().transform((str, ctx) => { + try { + return JSON.parse(str); + } catch (e) { + ctx.addIssue({ code: "custom", message: "Invalid JSON" }); + return z.NEVER; + } + }), +}); + +type MetadataSchema = z.TypeOf; + +interface MetadataFormProps { + defaultValue: string; + onSubmit: (value: IMetadata) => void; +} +function MetadataForm({ defaultValue, onSubmit }: MetadataFormProps) { + const validator = React.useMemo(() => withZod(metadataSchema), []); + + const handleOnSubmit = (data: MetadataSchema) => { + if (!data.value) return onSubmit({}); + onSubmit(data.value); + }; + + return ( + + + + + +
+ Set +
+
+ ); +} + +function MetadataEditor() { + const { fieldErrors } = useFormContext(); + + return ( + + ); +} + +function MetadataTrigger({ + className, + ...rest +}: ButtonHTMLAttributes) { + return ( + + ); +} + +interface MetadataDropdownProps { + className?: string; +} + +function MetadataDropdown({ + children, + + className, +}: PropsWithChildren) { + return ( +
+ {children} +
+ ); +} diff --git a/apps/web-remix/app/components/pages/pipelines/build/page.tsx b/apps/web-remix/app/components/pages/pipelines/build/page.tsx index f0f995d84..c6dc00608 100644 --- a/apps/web-remix/app/components/pages/pipelines/build/page.tsx +++ b/apps/web-remix/app/components/pages/pipelines/build/page.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from "react"; -import { LinksFunction, MetaFunction } from "@remix-run/node"; +import { MetaFunction } from "@remix-run/node"; import { Outlet, useFetcher, diff --git a/apps/web-remix/app/components/pages/pipelines/usePipelineRun.tsx b/apps/web-remix/app/components/pages/pipelines/usePipelineRun.tsx index c613643fa..75b851833 100644 --- a/apps/web-remix/app/components/pages/pipelines/usePipelineRun.tsx +++ b/apps/web-remix/app/components/pages/pipelines/usePipelineRun.tsx @@ -1,6 +1,11 @@ import { useEffect, useRef, useState } from "react"; import { assert } from "~/utils/assert"; -import { BuildelRun, BuildelRunStatus, BuildelSocket } from "@buildel/buildel"; +import { + BuildelRun, + BuildelRunStatus, + BuildelSocket, + BuildelRunStartArgs, +} from "@buildel/buildel"; export function usePipelineRun( organizationId: number, @@ -18,12 +23,9 @@ export function usePipelineRun( const [status, setStatus] = useState("idle"); - const startRun = async ( - initialInputs: { name: string; value: string }[] = [], - alias?: string - ) => { + const startRun = async (args: BuildelRunStartArgs) => { assert(run.current); - await run.current.start(initialInputs, alias); + await run.current.start(args); }; const stopRun = async () => { assert(run.current); diff --git a/apps/web-remix/package.json b/apps/web-remix/package.json index 3ec3bb085..59c421471 100644 --- a/apps/web-remix/package.json +++ b/apps/web-remix/package.json @@ -14,7 +14,7 @@ "typecheck": "tsc" }, "dependencies": { - "@buildel/buildel": "^0.1.6", + "@buildel/buildel": "^0.2.1", "@elpassion/taco": "^0.6.0", "@fastify/early-hints": "^1.0.1", "@fastify/http-proxy": "^9.4.0", diff --git a/apps/web-remix/pnpm-lock.yaml b/apps/web-remix/pnpm-lock.yaml index 5a5d0c0d4..740b9190d 100644 --- a/apps/web-remix/pnpm-lock.yaml +++ b/apps/web-remix/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@buildel/buildel': - specifier: ^0.1.6 - version: 0.1.6 + specifier: ^0.2.1 + version: 0.2.1 '@elpassion/taco': specifier: ^0.6.0 version: 0.6.0(@tanstack/react-table@8.12.0)(@types/react-dom@18.2.19)(@types/react-modal@3.16.3)(@types/react@18.2.55)(classnames@2.5.1)(date-fns@2.30.0)(embla-carousel-react@7.1.0)(lodash.merge@4.6.2)(react-datepicker@4.25.0)(react-dom@18.2.0)(react-modal@3.16.1)(react-quill@2.0.0)(react-select@5.8.0)(react@18.2.0) @@ -709,8 +709,8 @@ packages: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - /@buildel/buildel@0.1.6: - resolution: {integrity: sha512-8sI7XEI852OMJeXOh8t50QCDtcAuwkP4kjhAG0O9fXVcetRcJp2lhK8Ww8tFWGpvlY70JKk6QXX8f0Ki/AvWQA==} + /@buildel/buildel@0.2.1: + resolution: {integrity: sha512-S9/kxB1ADUEXhjxMWk176pef8IMOIGOknPej59FPl/Kju0JfjFINcdwg06bfYv6cV3x24Ktv7RXjK2Y44rkDMA==} dependencies: phoenix: 1.7.11 uuid: 9.0.1