diff --git a/package.json b/package.json index 8a9eb07..fd99f13 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "homepage": "https://circleci-public.github.io/visual-config-editor/", "dependencies": { - "@circleci/circleci-config-sdk": "0.5.0-alpha.11", + "@circleci/circleci-config-sdk": "0.6.0-alpha.2", "@craco/craco": "^6.3.0", "@monaco-editor/react": "^4.3.1", "@testing-library/jest-dom": "^5.11.4", diff --git a/src/components/atoms/Definition.tsx b/src/components/atoms/Definition.tsx index 3bd5770..8279017 100644 --- a/src/components/atoms/Definition.tsx +++ b/src/components/atoms/Definition.tsx @@ -1,8 +1,9 @@ +import { Generable } from '@circleci/circleci-config-sdk/dist/src/lib/Components'; import ComponentMapping from '../../mappings/ComponentMapping'; import { useStoreActions } from '../../state/Hooks'; import { InspectorDefinitionMenuNav } from '../menus/definitions/InspectorDefinitionMenu'; -const Definition = (props: { data: any; type: ComponentMapping }) => { +const Definition = (props: { data: Generable; type: ComponentMapping }) => { const Summary = props.type.components.summary; const navigateTo = useStoreActions((actions) => actions.navigateTo); const setDragging = useStoreActions((actions) => actions.setDragging); @@ -20,9 +21,16 @@ const Definition = (props: { data: any; type: ComponentMapping }) => { } }} onClick={(e) => { + // this generated object should always have a single key + const generated = props.data.generate() as { [key: string]: object }; + const flattened = Object.entries(generated).map(([key, value]) => ({ + name: key, + ...value, + }))[0]; + navigateTo({ component: InspectorDefinitionMenuNav, - props: { editing: true, values: props.data, dataType: props.type }, + props: { editing: true, values: flattened, dataType: props.type }, }); }} > diff --git a/src/components/atoms/form/ListProperty.tsx b/src/components/atoms/form/ListProperty.tsx index d693931..ee062ba 100644 --- a/src/components/atoms/form/ListProperty.tsx +++ b/src/components/atoms/form/ListProperty.tsx @@ -1,4 +1,4 @@ -import { FieldArray, useField } from 'formik'; +import { ArrayHelpers, FieldArray, useField } from 'formik'; import { ReactElement } from 'react'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import DeleteItemIcon from '../../../icons/ui/DeleteItemIcon'; @@ -14,6 +14,47 @@ export type ListPropertyProps = InspectorFieldProps & { emptyText?: string; }; +export type ListItemProps = { + index: number; + name: string; + arrayHelper: ArrayHelpers; +}; + +const ListItem = ({ index, name, arrayHelper }: ListItemProps) => { + return ( + + {(provided, snapshot) => ( +
+ +
+ +
+ +
+ )} +
+ ); +}; + +// TODO: Refactor const ListProperty = ({ label, values, @@ -51,48 +92,11 @@ const ListProperty = ({ className="p-2 pr-0" > {values?.map((cmd: any, index: number) => ( - - {(provided, snapshot) => ( -
- -
- -
- -
- )} -
+ arrayHelper={arrayHelper} + /> ))} {provided.placeholder} diff --git a/src/components/atoms/summaries/ExecutorSummary.tsx b/src/components/atoms/summaries/ExecutorSummary.tsx index 49840bf..1c302a3 100644 --- a/src/components/atoms/summaries/ExecutorSummary.tsx +++ b/src/components/atoms/summaries/ExecutorSummary.tsx @@ -1,4 +1,4 @@ -import { ReusableExecutor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Executor'; +import { ReusableExecutor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Reusable'; import ExecutorIcon from '../../../icons/components/ExecutorIcon'; const ExecutorSummary: React.FunctionComponent<{ data: ReusableExecutor }> = ( diff --git a/src/components/containers/DefinitionsContainer.tsx b/src/components/containers/DefinitionsContainer.tsx index 23689fb..79b51f1 100644 --- a/src/components/containers/DefinitionsContainer.tsx +++ b/src/components/containers/DefinitionsContainer.tsx @@ -42,12 +42,12 @@ const DefinitionsContainer = (props: DefinitionsProps) => { props: { typePage: props.type.subtypes?.component, menuPage: InspectorDefinitionMenu, - menuProps: { dataType: props.type }, + menuProps: { dataType: props.type, flatten: true }, }, } : { component: InspectorDefinitionMenuNav, - props: { dataType: props.type }, + props: { dataType: props.type, flatten: true }, }, ) } diff --git a/src/components/containers/inspector/CommandInspector.tsx b/src/components/containers/inspector/CommandInspector.tsx index dd34413..8639fb1 100644 --- a/src/components/containers/inspector/CommandInspector.tsx +++ b/src/components/containers/inspector/CommandInspector.tsx @@ -8,11 +8,37 @@ import StepPropertiesMenu from '../../menus/definitions/StepDefinitionMenu'; import StepTypePageNav from '../../menus/definitions/subtypes/StepTypePage'; import SubTypeMenuNav from '../../menus/SubTypeMenu'; -const CommandInspector = ( - props: FormikValues & { definitions: DefinitionModel }, +const NewButton = ( + props: FormikValues & { + definitions: DefinitionModel; + }, ) => { const navigateTo = useStoreActions((actions) => actions.navigateTo); + return ( + + ); +}; +const CommandInspector = ( + props: FormikValues & { definitions: DefinitionModel }, +) => { return (
{ - navigateTo({ - component: SubTypeMenuNav, - props: { - typePage: StepTypePageNav, - menuPage: StepPropertiesMenu, - passThrough: { dataType: CommandMapping }, - }, - values: props.values, - }); - }} - className="ml-auto tracking-wide hover:underline leading-6 text-sm text-circle-blue font-medium" - > - New - - } - > + titleExpanded={} + />
); }; diff --git a/src/components/containers/inspector/ExecutorInspector.tsx b/src/components/containers/inspector/ExecutorInspector.tsx index 8336bfa..6e68bc5 100644 --- a/src/components/containers/inspector/ExecutorInspector.tsx +++ b/src/components/containers/inspector/ExecutorInspector.tsx @@ -4,19 +4,32 @@ import InspectorProperty from '../../atoms/form/InspectorProperty'; import { executorSubtypes } from './subtypes/ExecutorSubtypes'; const ExecutorInspector = ( - props: FormikValues & { definitions: DefinitionModel }, + props: FormikValues & { + definitions: DefinitionModel; + subtype?: string; + }, ) => { + if (!props.subtype) { + return
+ Something went wrong! +
; + } + return (
- + - {executorSubtypes[props.values.type]?.resourceClasses?.map( + {executorSubtypes[props.subtype]?.resourceClasses?.map( (resourceClass) => ( - {executorSubtypes[props.values.type]?.fields} - - + {executorSubtypes[props.subtype]?.fields} + +
); }; diff --git a/src/components/containers/inspector/subtypes/ExecutorSubtypes.tsx b/src/components/containers/inspector/subtypes/ExecutorSubtypes.tsx index c7b201e..35e74ac 100644 --- a/src/components/containers/inspector/subtypes/ExecutorSubtypes.tsx +++ b/src/components/containers/inspector/subtypes/ExecutorSubtypes.tsx @@ -1,18 +1,17 @@ -import { executor } from '@circleci/circleci-config-sdk'; -import { DockerExecutor, MachineExecutor, MacOSExecutor, WindowsExecutor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Executor'; +import { executors } from '@circleci/circleci-config-sdk'; import { SubTypeMapping } from '../../../../mappings/ComponentMapping'; import InspectorProperty from '../../../atoms/form/InspectorProperty'; export interface ExecutorSubTypes { [type: string]: SubTypeMapping & { resourceClasses: string[]; - }; + }; } const executorSubtypes: ExecutorSubTypes = { docker: { text: 'Docker', - component: executor.DockerExecutor, + component: executors.DockerExecutor, resourceClasses: [ 'small', 'medium', @@ -23,34 +22,35 @@ const executorSubtypes: ExecutorSubTypes = { '2xlarge+', ], fields: ( - + ), docsLink: 'https://circleci.com/docs/2.0/executor-types/#using-docker', description: 'Steps run in container with provided image', }, machine: { text: 'Machine', - component: executor.MachineExecutor, + component: executors.MachineExecutor, resourceClasses: ['medium', 'large', 'xlarge', '2xlarge'], - fields: , + fields: , docsLink: 'https://circleci.com/docs/2.0/executor-types/#using-machine', description: 'Steps run on Linux Virtual Machine', }, macos: { text: 'MacOS', - component: executor.MacOSExecutor, + component: executors.MacOSExecutor, resourceClasses: ['medium', 'large'], - fields: , + fields: , docsLink: 'https://circleci.com/docs/2.0/executor-types/#using-macos', description: 'Steps run on macOS Virtual Machine with specific Xcode version', }, windows: { text: 'Windows', - component: executor.WindowsExecutor, - resourceClasses: ['medium', 'large', 'xlarge', '2xlarge'], - fields: , - docsLink: 'https://circleci.com/docs/2.0/executor-types/#using-the-windows-executor', + component: executors.WindowsExecutor, + resourceClasses: ['windows.medium', 'windows.large', 'windows.xlarge', 'windows.2xlarge'], + fields: , + docsLink: + 'https://circleci.com/docs/2.0/executor-types/#using-the-windows-executor', description: 'Steps run on Windows Virtual Machine', }, }; diff --git a/src/components/containers/inspector/subtypes/ParameterSubtypes.tsx b/src/components/containers/inspector/subtypes/ParameterSubtypes.tsx index c4f1f46..437e77f 100644 --- a/src/components/containers/inspector/subtypes/ParameterSubtypes.tsx +++ b/src/components/containers/inspector/subtypes/ParameterSubtypes.tsx @@ -1,4 +1,4 @@ -import { ReusableExecutor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Executor'; +import { ReusableExecutor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Reusable'; import { SubTypeMapping } from '../../../../mappings/ComponentMapping'; import InspectorProperty from '../../../atoms/form/InspectorProperty'; diff --git a/src/components/menus/SubTypeMenu.tsx b/src/components/menus/SubTypeMenu.tsx index fb8e1bf..f68ca51 100644 --- a/src/components/menus/SubTypeMenu.tsx +++ b/src/components/menus/SubTypeMenu.tsx @@ -14,7 +14,7 @@ export type SubTypeSelectPageProps = { }; export type SubTypeMenuPageProps = { subtype: SubTypeReference; - selectSubtype: () => void; + onSelectSubtype: () => void; }; export interface SelectedSubType { current?: SubTypeReference; @@ -42,7 +42,7 @@ const SubTypeMenu = (props: SubTypeMenuProps) => { {subtype?.current ? ( ) : ( diff --git a/src/components/menus/definitions/InspectorDefinitionMenu.tsx b/src/components/menus/definitions/InspectorDefinitionMenu.tsx index 30e3c43..d19cf5d 100644 --- a/src/components/menus/definitions/InspectorDefinitionMenu.tsx +++ b/src/components/menus/definitions/InspectorDefinitionMenu.tsx @@ -1,4 +1,5 @@ import { Form, Formik } from 'formik'; +import GenerableMapping from '../../../mappings/ComponentMapping'; import { useStoreActions, useStoreState } from '../../../state/Hooks'; import { DataModel, NavigationComponent } from '../../../state/Store'; import BreadCrumbs from '../../containers/BreadCrumbs'; @@ -7,9 +8,10 @@ import { SubTypeMenuPageProps } from '../SubTypeMenu'; import TabbedMenu from '../TabbedMenu'; type InspectorDefinitionProps = DataModel & { - values: any; + values: Record; editing?: boolean; passBackKey?: string; + activeTab?: number; } & SubTypeMenuPageProps; const InspectorDefinitionMenu = (props: InspectorDefinitionProps) => { @@ -35,9 +37,17 @@ const InspectorDefinitionMenu = (props: InspectorDefinitionProps) => { } }; + const getValues = () => { + if (props.values) { + return props.values; + } + + return props.subtype ? dataMapping?.defaults[props.subtype] : dataMapping?.defaults; + } + const tabs = ['PROPERTIES']; - const subtype = - props.subtype || dataMapping?.subtypes?.getSubtype(props.values); + const unpacked = getValues(); + const subtype = props.subtype || dataMapping?.subtypes?.getSubtype(unpacked); if (dataMapping?.parameters) { tabs.push('PARAMETERS'); @@ -58,25 +68,25 @@ const InspectorDefinitionMenu = (props: InspectorDefinitionProps) => { { - const newDefinition = dataMapping.transform(values, definitions); - - if (newDefinition) { - generateConfig({ [dataMapping.type]: [newDefinition] }); - } + // TODO: handle error handling + // const newDefinition = dataMapping.transform(values, definitions); + // if (newDefinition) { + // generateConfig({ [dataMapping.type]: [newDefinition] }); + // } }} enableReinitialize onSubmit={(values) => { const newDefinition = dataMapping.transform(values, definitions); + const submitData = props.editing + ? { old: unpacked, new: newDefinition } + : newDefinition; if (!props.passBackKey) { - submitToStore(newDefinition); + submitToStore(submitData); } if ( @@ -104,7 +114,7 @@ const InspectorDefinitionMenu = (props: InspectorDefinitionProps) => { > {(formikProps) => (
- +
{dataMapping.subtypes && (props.editing ? ( @@ -124,7 +134,7 @@ const InspectorDefinitionMenu = (props: InspectorDefinitionProps) => { className="p-4 mb-4 w-full border-circle-gray-300 border-2 rounded text-left" type="button" onClick={() => { - props.selectSubtype(); + props.onSelectSubtype(); }} >

@@ -141,7 +151,7 @@ const InspectorDefinitionMenu = (props: InspectorDefinitionProps) => { {dataMapping.components.inspector({ ...formikProps, definitions, - subtype: props.subtype, + subtype: subtype, })}

{dataMapping.parameters ? ( diff --git a/src/components/menus/definitions/StepDefinitionMenu.tsx b/src/components/menus/definitions/StepDefinitionMenu.tsx index dd9c863..e83158a 100644 --- a/src/components/menus/definitions/StepDefinitionMenu.tsx +++ b/src/components/menus/definitions/StepDefinitionMenu.tsx @@ -1,21 +1,22 @@ -import { commands } from '@circleci/circleci-config-sdk'; -import { CustomCommand } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Commands/exports/Reusable'; +import { reusable } from '@circleci/circleci-config-sdk'; import { Form, Formik } from 'formik'; import { useStoreActions } from '../../../state/Hooks'; import BreadCrumbs from '../../containers/BreadCrumbs'; import { commandSubtypes } from '../../containers/inspector/subtypes/CommandSubtypes'; -import TabbedMenu from '../TabbedMenu'; import { SubTypeMenuPageProps } from '../SubTypeMenu'; +import TabbedMenu from '../TabbedMenu'; const StepPropertiesMenu = ( - props: SubTypeMenuPageProps, + props: SubTypeMenuPageProps, ) => { const navigateBack = useStoreActions((actions) => actions.navigateBack); const builtIn = typeof props.subtype === 'string'; const builtInSubtype = builtIn ? commandSubtypes[props.subtype as string] : undefined; - const customCommand = !builtIn ? (props.subtype as CustomCommand) : undefined; + const customCommand = !builtIn + ? (props.subtype as reusable.CustomCommand) + : undefined; return (
@@ -24,7 +25,7 @@ const StepPropertiesMenu = (

New Step

{ navigateBack({ @@ -32,12 +33,9 @@ const StepPropertiesMenu = ( apply: (values: any) => { values.steps = [ ...values.steps, - builtInSubtype - ? builtInSubtype.generate(parameters) - : new commands.reusable.ReusableCommand( - props.subtype as CustomCommand, - values.parameters, - ), + { + [props.subtype as string]: parameters, + }, ]; return values; @@ -53,7 +51,7 @@ const StepPropertiesMenu = ( className="p-4 mb-4 w-full border-circle-gray-300 border-2 rounded text-left" type="button" onClick={() => { - props.selectSubtype(); + props.onSelectSubtype(); }} >

@@ -67,9 +65,7 @@ const StepPropertiesMenu = ( : customCommand?.description}

- {builtInSubtype - ? builtInSubtype?.fields - : 'custom fields'} + {builtInSubtype ? builtInSubtype?.fields : 'custom fields'}
diff --git a/src/mappings/CommandMapping.tsx b/src/mappings/CommandMapping.tsx index a19d8ab..69e943e 100644 --- a/src/mappings/CommandMapping.tsx +++ b/src/mappings/CommandMapping.tsx @@ -1,4 +1,4 @@ -import { commands } from '@circleci/circleci-config-sdk'; +import { parseCustomCommand } from '@circleci/circleci-config-sdk'; import { CustomCommand } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Commands/exports/Reusable'; import CommandSummary from '../components/atoms/summaries/CommandSummary'; import CommandInspector from '../components/containers/inspector/CommandInspector'; @@ -17,12 +17,9 @@ const CommandMapping: ComponentMapping = { steps: [], }, parameters: componentParametersSubtypes.command, - transform: (values: any) => - new commands.reusable.CustomCommand( - values.name, - values.steps, - values.parameters, - ), + transform: ({ name, ...values }, definitions) => { + return parseCustomCommand(name, values, definitions.commands); + }, store: { get: (state) => state.definitions.commands, add: (actions) => actions.defineCommand, diff --git a/src/mappings/ComponentMapping.tsx b/src/mappings/ComponentMapping.tsx index 8ac0db1..a893121 100644 --- a/src/mappings/ComponentMapping.tsx +++ b/src/mappings/ComponentMapping.tsx @@ -1,8 +1,8 @@ import { - commands, - executor, + executors, Job, parameters, + reusable, } from '@circleci/circleci-config-sdk'; import { ActionCreator, Actions, State } from 'easy-peasy'; import { FormikValues } from 'formik'; @@ -31,15 +31,15 @@ export interface DataMapping { * Registry of circleci-config-sdk component to data maps. */ -// thinking of adding a docs link to Executor and description as a key to each Mapping +// Maybe add docs link to Executor and description as a key to each Mapping const dataMappings: DataMapping[] = [ { type: 'executors', component: [ - executor.DockerExecutor, - executor.MacOSExecutor, - executor.MachineExecutor, - executor.WindowsExecutor, + executors.DockerExecutor, + executors.MacOSExecutor, + executors.MachineExecutor, + executors.WindowsExecutor, ], mapping: ExecutorMapping, }, @@ -50,7 +50,7 @@ const dataMappings: DataMapping[] = [ }, { type: 'commands', - component: [commands.reusable.CustomCommand], + component: [reusable.CustomCommand], mapping: CommandMapping, }, { @@ -87,8 +87,7 @@ const componentToType = (data: any): GenerableMapping | undefined => { export { componentToType, dataMappings }; -type storeType = typeof Store; - +type StoreType = typeof Store; type KeysOfUnion = T extends T ? keyof T : never; export interface SubTypeMapping { @@ -140,15 +139,15 @@ export default interface GenerableMapping< ) => ConfigDataType | undefined; store: { /** Returns easy-peasy state hook for component array */ - get: (state: State) => ConfigDataType[] | undefined; + get: (state: State) => ConfigDataType[] | undefined; /** Returns easy-peasy add action hook for component array */ - add: (state: Actions) => ActionCreator; + add: (state: Actions) => ActionCreator; /** Returns easy-peasy update action hook for data type */ update: ( - state: Actions, + state: Actions, ) => (data: UpdateType) => void; /** Returns easy-peasy removal action hook for data type */ - remove: (state: Actions) => (data: ConfigDataType) => void; + remove: (state: Actions) => (data: ConfigDataType) => void; }; /** * Name of target that a definition can be tragged to. @@ -185,7 +184,7 @@ export default interface GenerableMapping< inspector: ( props: FormikValues & { definitions: DefinitionModel; - subtype?: any; + subtype?: string; }, // data: ConfigDataType; ) => JSX.Element; diff --git a/src/mappings/ExecutorMapping.tsx b/src/mappings/ExecutorMapping.tsx index 36e913b..daaeac5 100644 --- a/src/mappings/ExecutorMapping.tsx +++ b/src/mappings/ExecutorMapping.tsx @@ -1,8 +1,10 @@ -import { executor, Job, WorkflowJob } from '@circleci/circleci-config-sdk'; import { - ReusableExecutor -} from '@circleci/circleci-config-sdk/dist/src/lib/Components/Executor'; -import { Executor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Executor/exports/Executor'; + executors, + Job, + parseExecutor, + reusable, + WorkflowJob, +} from '@circleci/circleci-config-sdk'; import ExecutorSummary from '../components/atoms/summaries/ExecutorSummary'; import ExecutorInspector from '../components/containers/inspector/ExecutorInspector'; import { executorSubtypes } from '../components/containers/inspector/subtypes/ExecutorSubtypes'; @@ -13,44 +15,16 @@ import ComponentMapping from './ComponentMapping'; import JobMapping from './JobMapping'; export type AnyExecutor = - | executor.DockerExecutor - | executor.MacOSExecutor - | executor.MachineExecutor - | executor.WindowsExecutor - | Executor; + | executors.DockerExecutor + | executors.MacOSExecutor + | executors.MachineExecutor + | executors.WindowsExecutor + | executors.Executor; -const transform = (values: any) => { - const subtypes: { [type: string]: () => AnyExecutor } = { - docker: () => - new executor.DockerExecutor( - values.executor.image.image || 'cimg/base:stable', - values.executor.resource_class, - values.executor.parameters, - ), - machine: () => - new executor.MachineExecutor( - values.executor.resource_class, - values.executor.image || 'cimg/base:latest', - values.executor.parameters, - ), - macos: () => - new executor.MacOSExecutor( - values.executor.xcode, - values.executor.resource_class, - values.executor.parameters, - ), - windows: () => - new executor.WindowsExecutor( - values.executor.image, - values.executor.resource_class, - values.executor.parameters, - ), - }; - - return new executor.ReusableExecutor(values.name, subtypes[values.type]()); -}; - -const ExecutorMapping: ComponentMapping = { +const ExecutorMapping: ComponentMapping< + reusable.ReusableExecutor, + WorkflowJob +> = { type: 'executors', name: { singular: 'Executor', @@ -58,42 +32,47 @@ const ExecutorMapping: ComponentMapping = { }, defaults: { docker: { - name: 'docker', - executor: { - image: { + name: 'new-docker-executor', + docker: [ + { image: 'cimg/base:stable', + parameters: {}, }, - parameters: {}, - }, + ], resource_class: 'medium', }, machine: { - name: 'machine', - executor: { + name: 'new-machine-executor', + machine: { image: 'ubuntu-2004:202111-01', parameters: {}, }, resource_class: 'medium', }, macos: { - name: 'macos', - executor: { + name: 'new-macos-executor', + macos: { xcode: '13.2.0', parameters: {}, }, resource_class: 'medium', }, windows: { - name: 'windows_server', - executor: { + name: 'new-windows-executor', + machine: { image: 'windows-server-2019-vs2019:stable', parameters: {}, }, - resource_class: 'medium', + resource_class: 'windows.medium', }, }, parameters: componentParametersSubtypes.executor, - transform: transform, + transform: ({ name, ...values }) => { + return new reusable.ReusableExecutor( + name, + parseExecutor(values) as executors.Executor, + ); + }, store: { get: (state) => state.definitions.executors, add: (actions) => actions.defineExecutor, @@ -111,10 +90,11 @@ const ExecutorMapping: ComponentMapping = { }, subtypes: { component: ExecutorTypePageNav, - getSubtype: (reusableExec: ReusableExecutor) => { - return Object.keys(executorSubtypes).find( - (subtype) => - reusableExec.executor instanceof executorSubtypes[subtype].component, + getSubtype: (reusableExec) => { + const reusableExecsKeys = Object.keys(reusableExec); + + return Object.keys(executorSubtypes).find((subtype) => + reusableExecsKeys.includes(subtype), ); }, definitions: executorSubtypes, diff --git a/src/mappings/JobMapping.tsx b/src/mappings/JobMapping.tsx index 64a3eaa..13f4c96 100644 --- a/src/mappings/JobMapping.tsx +++ b/src/mappings/JobMapping.tsx @@ -1,4 +1,4 @@ -import { Job, WorkflowJob } from '@circleci/circleci-config-sdk'; +import { Job, parseJob, WorkflowJob } from '@circleci/circleci-config-sdk'; import JobNode from '../components/atoms/nodes/JobNode'; import JobSummary from '../components/atoms/summaries/JobSummary'; import JobInspector from '../components/containers/inspector/JobInspector'; @@ -17,14 +17,12 @@ const JobMapping: ComponentMapping = { steps: [], }, parameters: componentParametersSubtypes.job, - transform: (values, definitions) => { - const executor = definitions.executors.find( - (executor) => executor.name === values.executor?.name, - ); - - if (executor) { - return new Job(values.name, executor, values.steps); - } + /** + TODO: Implement this to pass transform method to + dependsOn: (definitions) => [definitions.commands, definitions.executors], + */ + transform: ({ name, ...values }, definitions) => { + return parseJob(name, values, definitions.commands, definitions.executors); }, store: { get: (state) => { @@ -47,9 +45,9 @@ const JobMapping: ComponentMapping = { inspector: JobInspector, }, docsInfo: { - description: "Collection of steps to run your config", - link: "https://circleci.com/docs/2.0/concepts/#jobs", - } + description: 'Collection of steps to run your config', + link: 'https://circleci.com/docs/2.0/concepts/#jobs', + }, }; export default JobMapping; diff --git a/src/mappings/ParameterMapping.tsx b/src/mappings/ParameterMapping.tsx index bc165ab..ca88a79 100644 --- a/src/mappings/ParameterMapping.tsx +++ b/src/mappings/ParameterMapping.tsx @@ -1,4 +1,4 @@ -import { parameters } from '@circleci/circleci-config-sdk'; +import { parseParameter } from '@circleci/circleci-config-sdk'; import { CustomParameter } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters'; import { PipelineParameterLiteral } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters/types/CustomParameterLiterals.types'; import ParameterSummary from '../components/atoms/summaries/ParameterSummary'; @@ -30,13 +30,12 @@ const ParameterMapping: ComponentMapping< name: 'new_enum_parameter', }, }, - transform: (values: any) => - new parameters.CustomParameter( - values.name, - values.type, - values.defaultValue, - values.description, - ), + transform: ({ name, ...values }) => { + return parseParameter( + values, + name, + ) as CustomParameter; + }, store: { get: (state) => state.definitions.parameters, add: (actions) => actions.defineParameter, diff --git a/src/state/Store.tsx b/src/state/Store.tsx index 4b10363..c4f9588 100644 --- a/src/state/Store.tsx +++ b/src/state/Store.tsx @@ -2,10 +2,10 @@ import { Config, Job, parameters, + reusable, Workflow, } from '@circleci/circleci-config-sdk'; import { CustomCommand } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Commands/exports/Reusable'; -import { ReusableExecutor } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Executor'; import { CustomParameter } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters'; import { PipelineParameterLiteral } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters/types/CustomParameterLiterals.types'; import { Action, action } from 'easy-peasy'; @@ -32,7 +32,7 @@ export interface WorkflowModel { /** Reusable definitions of CircleCIConfigObject */ export interface DefinitionModel /*extends CircleCIConfigObject*/ { parameters: CustomParameter[]; - executors: ReusableExecutor[]; + executors: reusable.ReusableExecutor[]; jobs: Job[]; commands: CustomCommand[]; workflows: Workflow[]; @@ -56,7 +56,7 @@ export interface NavigationComponent { export interface NavigationStop { component: NavigationComponent; - props: any; + props: { [key: string]: any }; } export interface StoreModel { @@ -100,7 +100,7 @@ export interface UpdateType { } export interface StoreActions { - persistProps: Action; + persistProps: Action; setDragging: Action; setConnecting: Action< StoreModel, @@ -141,19 +141,17 @@ export interface StoreActions { defineJob: Action; updateJob: Action>; - /** @todo implement job removal */ + /** TODO: implement job removal */ undefineJob: Action; - /** @todo implement commands */ defineCommand: Action; updateCommand: Action>; undefineCommand: Action; - defineExecutor: Action; - updateExecutor: Action>; - undefineExecutor: Action; + defineExecutor: Action; + updateExecutor: Action>; + undefineExecutor: Action; - /** @todo implement parameters */ defineParameter: Action< StoreModel, CustomParameter @@ -286,9 +284,10 @@ const Actions: StoreActions = { removeWorkflowElement: action((state, payload) => { const workflow = state.workflows[state.selectedWorkflow]; - state.workflows[state.selectedWorkflow] = {...workflow, elements: workflow.elements.filter( - (element) => element.id !== payload, - )} + state.workflows[state.selectedWorkflow] = { + ...workflow, + elements: workflow.elements.filter((element) => element.id !== payload), + }; }), setWorkflowElements: action((state, payload) => { state.workflows[state.selectedWorkflow].elements = payload; @@ -321,12 +320,10 @@ const Actions: StoreActions = { defineExecutor: action((state, payload) => { state.definitions.executors = state.definitions.executors?.concat(payload); }), - /** @todo fix updating executors since reusable executors have been removed.*/ updateExecutor: action((state, payload) => { - if (state.definitions.executors) { - // const index = state.definitions.executors.findIndex((executor) => executor.name === payload.name) - // state.definitions.executors[index] = payload; - } + state.definitions.executors = state.definitions.executors?.map((executor) => + executor.name === payload.old.name ? payload.new : executor, + ); }), undefineExecutor: action((state, payload) => { state.definitions.executors?.filter( @@ -369,6 +366,7 @@ const Actions: StoreActions = { }); const defs = state.definitions; + // This is a merged config preview. TODO: Refactor merging process. const config = new Config( false, payload?.jobs ? [...defs.jobs, ...payload.jobs] : defs.jobs, diff --git a/yarn.lock b/yarn.lock index d0a4f5e..c984c63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,10 +1081,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@circleci/circleci-config-sdk@0.5.0-alpha.11": - version "0.5.0-alpha.11" - resolved "https://registry.yarnpkg.com/@circleci/circleci-config-sdk/-/circleci-config-sdk-0.5.0-alpha.11.tgz#9fdc9fa7311311b8cf6225180b303d3edd1f7dab" - integrity sha512-YlRmF7uqWpzd9Ar0Hx/nbvQ+3LnQMg4ON+YRoau8IvHz/T1f/KbVZB7wO/+PQKrAMXp3+Mut36maYqvwJ7dIYQ== +"@circleci/circleci-config-sdk@0.6.0-alpha.2": + version "0.6.0-alpha.2" + resolved "https://registry.yarnpkg.com/@circleci/circleci-config-sdk/-/circleci-config-sdk-0.6.0-alpha.2.tgz#deb98f2e2e7a0d97b703c4c380dc4a3343d16523" + integrity sha512-DqT9/cYpLnz+HoIoQw/xK+1siBGN4DZ+MHPj7f2VhKl20ogvPt8b9Prb7+qDdX5nz2gAA9ADGnyNBXQWen018Q== dependencies: ajv "^8.8.2" ajv-merge-patch "^5.0.1" @@ -12149,10 +12149,10 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@2.0.0-9: - version "2.0.0-9" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-9.tgz#0099f0645d1ffa686a2c5141b6da340f545d3634" - integrity sha512-Bf2KowHjyVkIIiGMt7+fbhmlvKOaE8DWuD07bnL4+FQ9sPmEl/5IzGpBpoxPqOaHuyasBjJhyXDcISpJWfhCGw== +yaml@*: + version "2.1.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.0.tgz#96ba62ff4dd990c0eb16bd96c6254a085d288b80" + integrity sha512-OuAINfTsoJrY5H7CBWnKZhX6nZciXBydrMtTHr1dC4nP40X5jyTIVlogZHxSlVZM8zSgXRfgZGsaHF4+pV+JRw== yaml@^1.10.0, yaml@^1.10.2: version "1.10.2"