Merge pull request #117 from ctk-hq/feat/depends-on-spec

Update `depends_on` field to adhere the specification
master
Artem Golub 3 years ago committed by GitHub
commit 4cf2ecdd37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,7 +8,8 @@ import {
IServiceNodeItem, IServiceNodeItem,
IVolumeNodeItem, IVolumeNodeItem,
IServiceNodePosition, IServiceNodePosition,
IProject IProject,
IEditServiceFormDependsOn
} from "../../types"; } from "../../types";
import eventBus from "../../events/eventBus"; import eventBus from "../../events/eventBus";
import { useMutation } from "react-query"; import { useMutation } from "react-query";
@ -40,6 +41,7 @@ import EditVolumeModal from "../modals/docker-compose/volume/EditVolumeModal";
import CodeEditor from "../CodeEditor"; import CodeEditor from "../CodeEditor";
import { useTitle } from "../../hooks"; import { useTitle } from "../../hooks";
import VisibilitySwitch from "../global/VisibilitySwitch"; import VisibilitySwitch from "../global/VisibilitySwitch";
import _ from "lodash";
interface IProjectProps { interface IProjectProps {
isAuthenticated: boolean; isAuthenticated: boolean;
@ -248,6 +250,22 @@ export default function Project(props: IProjectProps) {
setNetworks({ ..._networks }); setNetworks({ ..._networks });
}; };
const getDependsOnKeys = (dependsOn: any): string[] => {
let dependsOnKeys: string[] = [];
if (dependsOn) {
if (dependsOn.constructor === Object) {
dependsOnKeys = Object.keys(dependsOn);
}
if (Array.isArray(dependsOn)) {
dependsOnKeys = dependsOn as [];
}
}
return dependsOnKeys;
};
const onUpdateEndpoint = (nodeItem: IServiceNodeItem) => { const onUpdateEndpoint = (nodeItem: IServiceNodeItem) => {
const key = nodeItem.key; const key = nodeItem.key;
@ -266,11 +284,10 @@ export default function Project(props: IProjectProps) {
}); });
} }
if ( const dependsOnData = nodeItem.serviceConfig.depends_on;
nodeItem.serviceConfig?.depends_on && const dependsOnKeys = getDependsOnKeys(dependsOnData);
Array.isArray(nodeItem.serviceConfig.depends_on)
) { dependsOnKeys.forEach((dep: string) => {
nodeItem.serviceConfig.depends_on.forEach((dep: string) => {
const depObject = Object.keys(nodes).find((key: string) => { const depObject = Object.keys(nodes).find((key: string) => {
const node = nodes[key]; const node = nodes[key];
if (node.canvasConfig.node_name === dep) { if (node.canvasConfig.node_name === dep) {
@ -282,7 +299,6 @@ export default function Project(props: IProjectProps) {
onConnectionAttached([key, depObject]); onConnectionAttached([key, depObject]);
} }
}); });
}
setNodes({ ...nodes, [nodeItem.key]: nodeItem }); setNodes({ ...nodes, [nodeItem.key]: nodeItem });
}; };
@ -301,19 +317,25 @@ export default function Project(props: IProjectProps) {
} as IServiceNodeItem; } as IServiceNodeItem;
const targetNode = stateNodesRef.current[data[1]]; const targetNode = stateNodesRef.current[data[1]];
const targetServiceName = targetNode.canvasConfig.node_name; const targetServiceName = targetNode.canvasConfig.node_name;
const sourceDependsOn = sourceNode.serviceConfig.depends_on as string[]; const sourceDependsOn = sourceNode.serviceConfig.depends_on as any;
if (sourceDependsOn && sourceDependsOn.length) {
if (targetServiceName) { if (targetServiceName) {
const filtered = sourceDependsOn.filter( const dependsOnKeys = getDependsOnKeys(sourceDependsOn);
(nodeName: string) => nodeName !== targetServiceName
);
if (filtered.length) { dependsOnKeys.forEach((key: string) => {
sourceNode.serviceConfig.depends_on = filtered; if (key === targetServiceName) {
} else { if (Array.isArray(sourceDependsOn)) {
delete sourceNode.serviceConfig.depends_on; _.remove(sourceDependsOn, (key) => key === targetServiceName);
}
if (sourceDependsOn && sourceDependsOn.constructor === Object) {
delete sourceDependsOn[key];
}
} }
});
if (!getDependsOnKeys(sourceDependsOn).length) {
delete sourceNode.serviceConfig.depends_on;
} }
} }
} }
@ -337,13 +359,22 @@ export default function Project(props: IProjectProps) {
} as IServiceNodeItem; } as IServiceNodeItem;
const targetNode = stateNodesRef.current[data[1]]; const targetNode = stateNodesRef.current[data[1]];
const targetServiceName = targetNode.canvasConfig.node_name; const targetServiceName = targetNode.canvasConfig.node_name;
let sourceDependsOn = sourceNode.serviceConfig.depends_on as string[]; let sourceDependsOn = sourceNode.serviceConfig.depends_on as any;
const dependsOnKeys = getDependsOnKeys(sourceDependsOn);
if (sourceDependsOn && sourceDependsOn.length) { if (sourceDependsOn) {
if (targetServiceName) { if (targetServiceName) {
if (!sourceDependsOn.includes(targetServiceName)) { if (!dependsOnKeys.includes(targetServiceName)) {
if (Array.isArray(sourceDependsOn)) {
sourceDependsOn.push(targetServiceName); sourceDependsOn.push(targetServiceName);
} }
if (sourceDependsOn.constructor === Object) {
sourceDependsOn[targetServiceName] = {
condition: "service_healthy"
};
}
}
} }
} else { } else {
if (targetServiceName) { if (targetServiceName) {

@ -222,14 +222,38 @@ const General = () => {
title: "Depends on", title: "Depends on",
fields: (index: number): TFinalFormField[] => [ fields: (index: number): TFinalFormField[] => [
{ {
id: `dependsOn[${index}]`, id: `dependsOn[${index}].serviceName`,
type: "text", type: "text",
name: `dependsOn[${index}]`, name: `dependsOn[${index}].serviceName`,
placeholder: "Service name", placeholder: "Service name",
required: false required: true
},
{
id: `dependsOn[${index}].condition`,
type: "toggle",
name: `dependsOn[${index}].condition`,
label: "Condition",
required: true,
options: [
{
value: "service_started",
text: "Service started"
},
{
value: "service_healthy",
text: "Service healthy"
},
{
value: "service_completed_successfully",
text: "Service completed successfully"
}
]
} }
], ],
newValue: "" newValue: {
serviceName: "",
condition: "service_started"
}
}, },
{ {
id: "networks", id: "networks",

@ -1,4 +1,8 @@
import type { IEditServiceForm, IServiceNodeItem } from "../../../../types"; import type {
IEditServiceForm,
IEditServiceFormDependsOn,
IServiceNodeItem
} from "../../../../types";
import * as yup from "yup"; import * as yup from "yup";
import { import {
checkArray, checkArray,
@ -278,9 +282,43 @@ export const validationSchema = yup.object({
key: yup.string().required("Key is required"), key: yup.string().required("Key is required"),
value: yup.string() value: yup.string()
}) })
),
dependsOn: yup.array(
yup.object({
serviceName: yup.string().required("Service name is required"),
condition: yup
.string()
.oneOf(
[
"service_started",
"service_healthy",
"service_completed_successfully"
],
"Condition should be one of: service_started, service_healthy, or service_completed_successfully."
) )
})
),
workingDir: yup.string()
}); });
const extractDependsOn = (object: string[] | any) => {
if (!object) {
return object;
}
if (Array.isArray(object)) {
return object.map((item) => ({
serviceName: item,
condition: "service_started"
}));
}
return Object.keys(object).map((key) => ({
serviceName: key,
condition: object[key].condition
}));
};
export const getInitialValues = (node?: IServiceNodeItem): IEditServiceForm => { export const getInitialValues = (node?: IServiceNodeItem): IEditServiceForm => {
if (!node) { if (!node) {
return { return {
@ -456,8 +494,7 @@ export const getInitialValues = (node?: IServiceNodeItem): IEditServiceForm => {
initialValues.deploy.labels initialValues.deploy.labels
} }
: initialValues.deploy, : initialValues.deploy,
dependsOn: dependsOn: extractDependsOn(depends_on) ?? initialValues.dependsOn,
(depends_on as string[]) ?? (initialValues.dependsOn as string[]),
entrypoint: (entrypoint as string) ?? (initialValues.entrypoint as string), entrypoint: (entrypoint as string) ?? (initialValues.entrypoint as string),
envFile: (env_file as string) ?? (initialValues.envFile as string), envFile: (env_file as string) ?? (initialValues.envFile as string),
imageName, imageName,
@ -500,6 +537,31 @@ export const getInitialValues = (node?: IServiceNodeItem): IEditServiceForm => {
}; };
}; };
const getFinalDependsOn = (dependsOn: IEditServiceFormDependsOn[]) => {
let shortForm = true;
for (const item of dependsOn) {
if (item.condition !== "service_started") {
shortForm = false;
break;
}
}
if (shortForm) {
return pruneArray(dependsOn.map((item) => item.serviceName));
}
return pruneObject(
Object.fromEntries(
dependsOn.map((item) => [
item.serviceName,
{
condition: item.condition
}
])
)
);
};
export const getFinalValues = ( export const getFinalValues = (
values: IEditServiceForm, values: IEditServiceForm,
previous?: IServiceNodeItem previous?: IServiceNodeItem
@ -606,7 +668,7 @@ export const getFinalValues = (
}), }),
labels: packArrayAsObject(deploy.labels, "key", "value") labels: packArrayAsObject(deploy.labels, "key", "value")
}), }),
depends_on: pruneArray(dependsOn), depends_on: getFinalDependsOn(dependsOn),
entrypoint: pruneString(entrypoint), entrypoint: pruneString(entrypoint),
env_file: pruneString(envFile), env_file: pruneString(envFile),
image: pruneString( image: pruneString(

@ -149,11 +149,15 @@ export interface IService {
credential_spec: KeyValPair; credential_spec: KeyValPair;
depends_on: depends_on:
| string[] | string[]
| { | Record<
[key: string]: { string,
condition: string; {
}; condition:
}; | "service_started"
| "service_healthy"
| "service_completed_successfully";
}
>;
deploy?: { deploy?: {
endpoint_mode?: "vip" | "dnsrr"; endpoint_mode?: "vip" | "dnsrr";
labels?: string[] | Record<string, string>; labels?: string[] | Record<string, string>;
@ -480,10 +484,18 @@ export interface IEditServiceForm {
key: string; key: string;
value: string; value: string;
}[]; }[];
dependsOn: string[]; dependsOn: IEditServiceFormDependsOn[];
workingDir: string; workingDir: string;
} }
export interface IEditServiceFormDependsOn {
serviceName: string;
condition:
| "service_started"
| "service_healthy"
| "service_completed_successfully";
}
export interface IEditVolumeForm { export interface IEditVolumeForm {
entryName: string; entryName: string;
volumeName: string; volumeName: string;

Loading…
Cancel
Save