From cb3c1b1fc3f057cf64d5bfb9b721668da347ff44 Mon Sep 17 00:00:00 2001 From: Artem Golub Date: Tue, 12 Jul 2022 10:51:00 +0300 Subject: [PATCH] chore: add tabs with forms, service interface --- .gitignore | 3 + Makefile | 8 +- README.md | 1 + services/backend/src/api/views/utils.py | 53 ++-- services/backend/src/main/settings.py | 4 +- .../frontend/src/components/Canvas/index.tsx | 14 +- .../src/components/Modal/ConfirmDelete.tsx | 6 +- .../src/components/Modal/Node/Container.tsx | 71 ----- .../src/components/Modal/Node/Create.tsx | 152 ----------- .../src/components/Modal/Node/Edit.tsx | 175 ------------ .../src/components/Modal/Node/General.tsx | 76 ------ .../src/components/Modal/Node/Resource.tsx | 50 ---- .../src/components/Modal/Service/Create.tsx | 120 +++++---- .../src/components/Modal/Service/Edit.tsx | 216 ++++++++------- .../components/Modal/Service/Environment.tsx | 6 + .../src/components/Modal/Service/General.tsx | 56 ++++ .../src/components/Modal/Service/Labels.tsx | 6 + .../src/components/Modal/Service/Volumes.tsx | 6 + .../frontend/src/components/Project/index.tsx | 2 +- services/frontend/src/types/index.ts | 255 +++++++++++++++--- services/frontend/src/utils/data/libraries.ts | 22 +- .../frontend/src/utils/data/serviceNode.ts | 2 +- services/frontend/src/utils/generators.ts | 14 +- services/frontend/src/utils/index.ts | 64 +---- services/frontend/src/utils/position.ts | 12 +- 25 files changed, 581 insertions(+), 813 deletions(-) delete mode 100644 services/frontend/src/components/Modal/Node/Container.tsx delete mode 100644 services/frontend/src/components/Modal/Node/Create.tsx delete mode 100644 services/frontend/src/components/Modal/Node/Edit.tsx delete mode 100644 services/frontend/src/components/Modal/Node/General.tsx delete mode 100644 services/frontend/src/components/Modal/Node/Resource.tsx create mode 100644 services/frontend/src/components/Modal/Service/Environment.tsx create mode 100644 services/frontend/src/components/Modal/Service/General.tsx create mode 100644 services/frontend/src/components/Modal/Service/Labels.tsx create mode 100644 services/frontend/src/components/Modal/Service/Volumes.tsx diff --git a/.gitignore b/.gitignore index aa4a983..8f1602d 100644 --- a/.gitignore +++ b/.gitignore @@ -313,3 +313,6 @@ Icon Network Trash Folder Temporary Items .apdisk + +# Django +static diff --git a/Makefile b/Makefile index 4774423..cf07e73 100644 --- a/Makefile +++ b/Makefile @@ -41,9 +41,7 @@ shell_server: frontend_build: @ cd ./services/frontend/src && npm install && npm run build -local_setup: frontend_build up - @ echo "Waiting for PostgreSQL..." \ - && sleep 5 \ - && docker exec -it ${CONTAINER} python /home/server/manage.py makemigrations \ +local_server_init: + docker exec -it ${CONTAINER} python /home/server/manage.py makemigrations \ && docker exec -it ${CONTAINER} python /home/server/manage.py migrate \ - && docker exec -it ${BACKEND_CONTAINER_NAME} python /home/server/manage.py collectstatic --noinput + && docker exec -it ${CONTAINER} python /home/server/manage.py collectstatic --noinput diff --git a/README.md b/README.md index e86c749..7858194 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,4 @@ $ cd services/frontend && npm run start ## Docs - https://docs.jsplumbtoolkit.com/community/ +- https://github.com/compose-spec/compose-spec/blob/master/spec.md \ No newline at end of file diff --git a/services/backend/src/api/views/utils.py b/services/backend/src/api/views/utils.py index 4799f31..df3be0f 100644 --- a/services/backend/src/api/views/utils.py +++ b/services/backend/src/api/views/utils.py @@ -10,7 +10,9 @@ import docker from better_profanity import profanity from ruamel.yaml import YAML -from ruamel.yaml.scalarstring import SingleQuotedScalarString, DoubleQuotedScalarString +from ruamel.yaml.scalarstring import ( + SingleQuotedScalarString, + DoubleQuotedScalarString) from api.models import Project @@ -18,7 +20,7 @@ from api.models import Project try: import textwrap textwrap.indent -except AttributeError: # undefined function (wasn't added until Python 3.3) +except AttributeError: def indent(text, amount, ch=' '): padding = amount * ch return ''.join(padding+line for line in text.splitlines(True)) @@ -267,32 +269,31 @@ def format_build(specified_version, build): return build_str for _key, _val in build.items(): - if _key in ['args', 'cache_from', 'labels']: - if _val: + if _val: + if _key in ['args', 'cache_from', 'labels']: ret[_key] = format_key_val_pairs(_val) - else: - if _val: + else: ret[_key] = _val return ret -def _remove_missing_and_underscored_keys(d): - if not d: return d - for key in list(d.keys()): - if isinstance(d[key], list): - d[key] = list(filter(None, d[key])) - if not d.get(key): - del d[key] - elif isinstance(d[key], dict): - d[key] = _remove_missing_and_underscored_keys(d[key]) - if d[key] is None or d[key] == {}: - del d[key] +def _remove_missing_and_underscored_keys(str): + if not str: return str + for key in list(str.keys()): + if isinstance(str[key], list): + str[key] = list(filter(None, str[key])) + if not str.get(key): + del str[key] + elif isinstance(str[key], dict): + str[key] = _remove_missing_and_underscored_keys(str[key]) + if str[key] is None or str[key] == {}: + del str[key] - return d + return str -def format_deploy(specified_version, deploy): +def format_deploy(deploy): ret = deploy with contextlib.suppress(Exception): @@ -418,7 +419,7 @@ def format_services_version_three(specified_version, services, connections, volu if connected_services := get_connected_services(service_key, connections, services): service_formatted['depends_on'] = [] for connected_service in connected_services: - service_formatted['depends_on'].append(f"{connected_service['name']}") + service_formatted['depends_on'].append(f"{connected_service['service_name']}") with contextlib.suppress(KeyError): if service['container_name']: service_formatted['container_name'] = service['container_name'] @@ -458,9 +459,9 @@ def format_services_version_three(specified_version, services, connections, volu service_formatted['build'] = build if int(float(specified_version)) >= 3: with contextlib.suppress(KeyError): - if deploy := format_deploy(specified_version, service['deploy']): + if deploy := format_deploy(service['deploy']): service_formatted['deploy'] = deploy - services_formatted[service['name']] = service_formatted + services_formatted[service['service_name']] = service_formatted return services_formatted @@ -534,9 +535,7 @@ def generate(cname): sys.exit(1) cattrs = c.containers.get(cid).attrs - cfile = {} - networks = {} - cfile[cattrs['Name'][1:]] = {} + cfile = {cattrs['Name'][1:]: {}} ct = cfile[cattrs['Name'][1:]] values = { @@ -580,9 +579,7 @@ def generate(cname): } networklist = c.networks.list() - for network in networklist: - if network.attrs['Name'] in values['networks'].keys(): - networks[network.attrs['Name']] = {'external': (not network.attrs['Internal'])} + networks = {network.attrs['Name']: {'external': (not network.attrs['Internal'])} for network in networklist if network.attrs['Name'] in values['networks'].keys()} # Check for command and add it if present. if cattrs['Config']['Cmd'] != None: diff --git a/services/backend/src/main/settings.py b/services/backend/src/main/settings.py index 58fbb8d..522bbf5 100644 --- a/services/backend/src/main/settings.py +++ b/services/backend/src/main/settings.py @@ -153,7 +153,9 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ -STATIC_URL = "static/" +PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static') # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field diff --git a/services/frontend/src/components/Canvas/index.tsx b/services/frontend/src/components/Canvas/index.tsx index c107804..9f65135 100644 --- a/services/frontend/src/components/Canvas/index.tsx +++ b/services/frontend/src/components/Canvas/index.tsx @@ -1,15 +1,15 @@ import { FC, useState, useEffect } from "react"; -import { values } from "lodash"; +import { Dictionary, values } from "lodash"; import { v4 as uuidv4 } from "uuid"; import eventBus from "../../events/eventBus"; import { Popover } from "./Popover"; -import { IGraphData, CallbackFunction } from "../../types"; +import { IGraphData, CallbackFunction, IClientNodeItem } from "../../types"; import { useJsPlumb } from "../useJsPlumb"; const CANVAS_ID: string = "canvas-container-" + uuidv4(); interface ICanvasProps { - nodes: any; + nodes: Dictionary; connections: any; canvasPosition: any; onNodeUpdate: CallbackFunction; @@ -200,11 +200,11 @@ export const Canvas: FC = (props) => { > )}
-
- {x.configuration.prettyName} +
+ {x.canvasConfig.service_name}
-
- {x.configuration.prettyName} +
+ {x.serviceConfig?.container_name}
diff --git a/services/frontend/src/components/Modal/ConfirmDelete.tsx b/services/frontend/src/components/Modal/ConfirmDelete.tsx index 2773d0e..ac5da9c 100644 --- a/services/frontend/src/components/Modal/ConfirmDelete.tsx +++ b/services/frontend/src/components/Modal/ConfirmDelete.tsx @@ -47,10 +47,10 @@ const ModalConfirmDelete = (props: IModalConfirmDeleteProps) => {
-
+
-
- -
-
- - -
- - - -
-
- {openTab === "General" && } - - {openTab === "Container" && } - - {openTab === "Resource" && } - -
-
- -
- -
-
- - - - - ); -}; - -export default ModalCreate; diff --git a/services/frontend/src/components/Modal/Node/Edit.tsx b/services/frontend/src/components/Modal/Node/Edit.tsx deleted file mode 100644 index 3797451..0000000 --- a/services/frontend/src/components/Modal/Node/Edit.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import React from "react"; -import { useFormik } from "formik"; -import { XIcon } from "@heroicons/react/outline"; -import General from "./General"; -import Container from "./Container"; -import Resource from "./Resource"; -import { initialValues, formatName } from "./../../../utils"; -import { IClientNodeItem, CallbackFunction } from "../../../types"; - -interface IModalProps { - node: IClientNodeItem | null; - onHide: CallbackFunction; - onUpdateEndpoint: CallbackFunction; -} - -const ModalEdit = (props: IModalProps) => { - const { node, onHide, onUpdateEndpoint } = props; - const [selectedNode, setSelectedNode] = - React.useState(null); - const [openTab, setOpenTab] = React.useState("General"); - const formik = useFormik({ - initialValues: { - configuration: { - ...initialValues() - } - }, - onSubmit: () => undefined - }); - const tabs = [ - { - name: "General", - href: "#", - current: true, - hidden: false - }, - { - name: "Container", - href: "#", - current: false, - hidden: formik.values.configuration.type === "container" ? false : true - }, - { - name: "Resource", - href: "#", - current: false, - hidden: formik.values.configuration.type === "resource" ? false : true - } - ]; - - const classNames = (...classes: string[]) => { - return classes.filter(Boolean).join(" "); - }; - - React.useEffect(() => { - if (node) { - setSelectedNode(node); - } - }, [node]); - - React.useEffect(() => { - formik.resetForm(); - - if (selectedNode) { - formik.initialValues.configuration = { ...selectedNode.configuration }; - } - }, [selectedNode]); - - React.useEffect(() => { - return () => { - formik.resetForm(); - }; - }, []); - - return ( - <> -
-
-
-
-
-
-

Update template

- -
- -
-
- - -
- - - -
-
- {openTab === "General" && } - - {openTab === "Container" && } - - {openTab === "Resource" && } - -
-
- -
- -
-
-
-
-
- - ); -}; - -export default ModalEdit; diff --git a/services/frontend/src/components/Modal/Node/General.tsx b/services/frontend/src/components/Modal/Node/General.tsx deleted file mode 100644 index 1336e4a..0000000 --- a/services/frontend/src/components/Modal/Node/General.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React from "react"; - -const General = (props: any) => { - const { formik } = props; - - return ( - <> -
-
- -
- -
-
-
- -
- -
- -
-
- -
-
- -
- -
-
-
- - ); -}; -export default General; diff --git a/services/frontend/src/components/Modal/Node/Resource.tsx b/services/frontend/src/components/Modal/Node/Resource.tsx deleted file mode 100644 index abbd230..0000000 --- a/services/frontend/src/components/Modal/Node/Resource.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; - -const Resource = (props: any) => { - const { formik } = props; - - return ( - <> -
-
- -
- -
-
-
- -
-
- - -
-
- - ); -}; -export default Resource; diff --git a/services/frontend/src/components/Modal/Service/Create.tsx b/services/frontend/src/components/Modal/Service/Create.tsx index ad69090..602526c 100644 --- a/services/frontend/src/components/Modal/Service/Create.tsx +++ b/services/frontend/src/components/Modal/Service/Create.tsx @@ -1,6 +1,11 @@ +import { useState } from "react"; import { useFormik } from "formik"; import { XIcon } from "@heroicons/react/outline"; -import { serviceInitialValues, formatName } from "../../../utils"; +import General from "./General"; +import Environment from "./Environment"; +import Volumes from "./Volumes"; +import Labels from "./Labels"; +import { canvasConfigInitialValues } from "../../../utils"; import { CallbackFunction } from "../../../types"; interface IModalServiceProps { @@ -10,10 +15,15 @@ interface IModalServiceProps { const ModalServiceCreate = (props: IModalServiceProps) => { const { onHide, onAddEndpoint } = props; + const [openTab, setOpenTab] = useState("General"); + const formik = useFormik({ initialValues: { - configuration: { - ...serviceInitialValues() + canvasConfig: { + ...canvasConfigInitialValues() + }, + serviceConfig: { + container_name: "" }, key: "service", type: "SERVICE", @@ -23,6 +33,35 @@ const ModalServiceCreate = (props: IModalServiceProps) => { }, onSubmit: () => undefined }); + const tabs = [ + { + name: "General", + href: "#", + current: true, + hidden: false + }, + { + name: "Environment", + href: "#", + current: false, + hidden: false + }, + { + name: "Volumes", + href: "#", + current: false, + hidden: false + }, + { + name: "Labels", + href: "#", + current: false, + hidden: false + } + ]; + const classNames = (...classes: string[]) => { + return classes.filter(Boolean).join(" "); + }; return (
@@ -45,49 +84,41 @@ const ModalServiceCreate = (props: IModalServiceProps) => {
-
-
-
- -
- -
+
+ -
-
- -
- -
-
+
+
+ {openTab === "General" && } + {openTab === "Environment" && } + {openTab === "Volumes" && } + {openTab === "Labels" && } +
@@ -96,9 +127,6 @@ const ModalServiceCreate = (props: IModalServiceProps) => { className="btn-util" type="button" onClick={() => { - formik.values.configuration.name = formatName( - formik.values.configuration.prettyName - ); onAddEndpoint(formik.values); formik.resetForm(); }} diff --git a/services/frontend/src/components/Modal/Service/Edit.tsx b/services/frontend/src/components/Modal/Service/Edit.tsx index 7476f15..b9d5c66 100644 --- a/services/frontend/src/components/Modal/Service/Edit.tsx +++ b/services/frontend/src/components/Modal/Service/Edit.tsx @@ -1,135 +1,171 @@ -import React from "react"; +import { useState, useEffect } from "react"; import { useFormik } from "formik"; import { XIcon } from "@heroicons/react/outline"; -import { serviceInitialValues, formatName } from "../../../utils"; +import General from "./General"; +import Environment from "./Environment"; +import Volumes from "./Volumes"; +import Labels from "./Labels"; +import { canvasConfigInitialValues } from "../../../utils"; +import { + CallbackFunction, + ICanvasConfig, + IClientNodeItem, + IService +} from "../../../types"; interface IModalServiceProps { node: any; - onHide: any; - onUpdateEndpoint: any; + onHide: CallbackFunction; + onUpdateEndpoint: CallbackFunction; } const ModalServiceEdit = (props: IModalServiceProps) => { const { node, onHide, onUpdateEndpoint } = props; - const [selectedNode, setSelectedNode] = React.useState(null); + const [openTab, setOpenTab] = useState("General"); + const [selectedNode, setSelectedNode] = useState(); const formik = useFormik({ initialValues: { - configuration: { - ...serviceInitialValues() + canvasConfig: { + ...canvasConfigInitialValues() + }, + serviceConfig: { + container_name: "" } }, onSubmit: () => undefined }); + const tabs = [ + { + name: "General", + href: "#", + current: true, + hidden: false + }, + { + name: "Environment", + href: "#", + current: false, + hidden: false + }, + { + name: "Volumes", + href: "#", + current: false, + hidden: false + }, + { + name: "Labels", + href: "#", + current: false, + hidden: false + } + ]; + const classNames = (...classes: string[]) => { + return classes.filter(Boolean).join(" "); + }; - React.useEffect(() => { + useEffect(() => { if (node) { setSelectedNode(node); } }, [node]); - React.useEffect(() => { + useEffect(() => { formik.resetForm(); if (selectedNode) { - formik.initialValues.configuration = { ...selectedNode.configuration }; + formik.initialValues.canvasConfig = { + ...selectedNode.canvasConfig + } as ICanvasConfig; + formik.initialValues.serviceConfig = { + ...selectedNode.serviceConfig + } as IService; } }, [selectedNode]); - React.useEffect(() => { + useEffect(() => { return () => { formik.resetForm(); }; }, []); return ( - <> -
-
-
-
-
-
-

Update service

- -
- -
-
-
- -
- -
-
-
+
+
+
+
+
+
+

Update service

+ +
-
-
- -
- -
-
+
+ -
- +
+
+ {openTab === "General" && } + {openTab === "Environment" && } + {openTab === "Volumes" && } + {openTab === "Labels" && } +
+ +
+ +
- +
); }; diff --git a/services/frontend/src/components/Modal/Service/Environment.tsx b/services/frontend/src/components/Modal/Service/Environment.tsx new file mode 100644 index 0000000..f68aaa3 --- /dev/null +++ b/services/frontend/src/components/Modal/Service/Environment.tsx @@ -0,0 +1,6 @@ +const Environment = (props: any) => { + const { formik } = props; + + return <>; +}; +export default Environment; diff --git a/services/frontend/src/components/Modal/Service/General.tsx b/services/frontend/src/components/Modal/Service/General.tsx new file mode 100644 index 0000000..3ae0c59 --- /dev/null +++ b/services/frontend/src/components/Modal/Service/General.tsx @@ -0,0 +1,56 @@ +const General = (props: any) => { + const { formik } = props; + + return ( + <> +
+
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ + ); +}; +export default General; diff --git a/services/frontend/src/components/Modal/Service/Labels.tsx b/services/frontend/src/components/Modal/Service/Labels.tsx new file mode 100644 index 0000000..9838a34 --- /dev/null +++ b/services/frontend/src/components/Modal/Service/Labels.tsx @@ -0,0 +1,6 @@ +const Labels = (props: any) => { + const { formik } = props; + + return <>; +}; +export default Labels; diff --git a/services/frontend/src/components/Modal/Service/Volumes.tsx b/services/frontend/src/components/Modal/Service/Volumes.tsx new file mode 100644 index 0000000..8ed7f94 --- /dev/null +++ b/services/frontend/src/components/Modal/Service/Volumes.tsx @@ -0,0 +1,6 @@ +const Volumes = (props: any) => { + const { formik } = props; + + return <>; +}; +export default Volumes; diff --git a/services/frontend/src/components/Project/index.tsx b/services/frontend/src/components/Project/index.tsx index 03e2b56..08b2c94 100644 --- a/services/frontend/src/components/Project/index.tsx +++ b/services/frontend/src/components/Project/index.tsx @@ -208,7 +208,7 @@ export default function Project() { const sections = flattenLibraries(nodeLibraries); const clientNodeItem = getClientNodeItem( values, - ensure(sections.find((l) => l.Type === values.type)) + ensure(sections.find((l) => l.type === values.type)) ); clientNodeItem.position = { left: 60, top: 30 }; setNodes({ ...nodes, [clientNodeItem.key]: clientNodeItem }); diff --git a/services/frontend/src/types/index.ts b/services/frontend/src/types/index.ts index 189a5d3..b1ed0ff 100644 --- a/services/frontend/src/types/index.ts +++ b/services/frontend/src/types/index.ts @@ -2,6 +2,10 @@ import { AnchorId } from "@jsplumb/common"; import { Dictionary } from "lodash"; import { NodeGroupType } from "./enums"; +type KeyValPair = { + [x: string]: string | number; +}; + export type CallbackFunction = (...args: any[]) => any; export interface IServiceNodePosition { @@ -30,25 +34,20 @@ export interface IContainer { } export interface INodeLibraryItem { - Id: number; - Name: string; - Type: string; - Description: string; - NoInputs: number; - NoOutputs: number; - IsActive: boolean; + id: number; + name: string; + type: string; + description: string; + noInputs: number; + noOutputs: number; + isActive: boolean; } export interface INodeGroup { - Id: number; - Name: NodeGroupType; - Description: string; - NodeTypes: INodeLibraryItem[]; -} - -export interface IDockerCompose { - version: string; - services: any[]; + id: number; + name: NodeGroupType; + description: string; + nodeTypes: INodeLibraryItem[]; } interface INodeItem { @@ -63,11 +62,8 @@ export interface IFlatConnection { target: string; } -export interface IBaseConfiguration { - prettyName: string; - name: string; - description: string; - type: string; +export interface ICanvasConfig { + service_name: string; } export interface IGraphData { @@ -75,23 +71,214 @@ export interface IGraphData { connections: Dictionary; } -export interface IClientNodeItem extends INodeItem { - outputs: string[]; - configuration: IBaseConfiguration; -} - -export interface IServiceNodeItem extends INodeItem { - configuration: IBaseConfiguration; -} - export interface IAnchor { id: string; position: AnchorId; } +export interface IVolume { + type: string; + source: string; + target: string; + read_only: boolean; + bind: { + propagation: string; + create_host_path: boolean; + selinux: string; + }; + volume: { + nocopy: boolean; + }; + tmpfs: { + size: string | number; + }; + consistency: string; +} + export interface IService { - name: string; - labels: any; + build: { + context: string; + dockerfile: string; + args: KeyValPair[]; + ssh: string[]; + cache_from: string[]; + cache_to: string[]; + extra_hosts: string[]; + isolation: string; + labels: KeyValPair[]; + shm_size: string | number; + target: string; + }; + cpu_count: string | number; + cpu_percent: string | number; + cpu_shares: string | number; + cpu_period: string | number; + cpu_quota: string | number; + cpu_rt_runtime: string | number; + cpu_rt_period: string | number; + cpuset: number | number[]; + cap_add: string[]; + cap_drop: string[]; + cgroup_parent: string; + command: string | string[]; + configs: string[] | KeyValPair[]; + container_name: string; + credential_spec: KeyValPair; + depends_on: string[] | { [key: string]: string | number | KeyValPair }; + deploy: { + endpoint_mode: string; + labels: string[] | { [key: string]: string }; + mode: string; + placement: { + constraints: KeyValPair[] | KeyValPair; + preferences: KeyValPair[] | KeyValPair; + }; + replicas: number; + resources: { + limits: { + cpus: string; + memory: string; + pids: number; + }; + reservations: { + cpus: string; + memory: string; + devices: { [key: string]: string | number | string[] }[]; + }; + }; + restart_policy: { + condition: string; + delay: string; + max_attempts: number; + window: string; + }; + rollback_config: { + parallelism: number; + delay: string; + failure_action: string; + monitor: string; + max_failure_ratio: string; + order: string; + }; + update_config: { + parallelism: number; + delay: string; + failure_action: string; + monitor: string; + max_failure_ratio: string; + order: string; + }; + }; + device_cgroup_rules: string[]; + devices: string[]; + dns: string | string[]; + dns_opt: string[]; + dns_search: string | string[]; + domainname: string; + entrypoint: string | string[]; + env_file: string | string[]; + environment: string[] | KeyValPair; + expose: string[]; + extends: KeyValPair; + external_links: string[]; + extra_hosts: string[]; + group_add: string[]; + healthcheck: { + test: string[]; + interval: string; + timeout: string; + retries: number; + start_period: string; + }; + hostname: string; + image: string; + init: boolean; + ipc: string; + isolation: string; + labels: string[] | KeyValPair; + links: string[]; + logging: { + driver: string; + options: KeyValPair; + }; + network_mode: string; + networks: + | string[] + | { + [x: string]: { + aliases: string[]; + ipv4_address: string; + ipv6_address: string; + link_local_ips: string[]; + priority: number; + }; + }; + mac_address: string; + mem_swappiness: number; + memswap_limit: string | number; + oom_kill_disable: boolean; + oom_score_adj: number; + pid: string | number; + platform: string; + ports: + | string[] + | { + target: number; + host_ip: string; + published: string | number; + protocol: string; + mode: string; + }; + privileged: boolean; + profiles: string; + pull_policy: string; + read_only: boolean; + restart: string; + runtime: string; + secrets: + | string[] + | { + source: string; + target: string; + uid: string; + gid: string; + mode: number; + }; + security_opt: string[]; + shm_size: string; + stdin_open: boolean; + stop_grace_period: string; + stop_signal: string; + storage_opt: { + size: string; + }; + sysctls: string[] | KeyValPair; + tmpfs: string | string[]; + tty: boolean; + ulimits: { + nproc: number; + nofile: { + soft: number; + hard: number; + }; + }; + user: string; + userns_mode: string; + volumes: string[] | IVolume; + volumes_from: string[]; + working_dir: string; + tag: string; +} + +export interface IDockerCompose { + version: string; + services: IService[]; +} + +export interface IClientNodeItem extends INodeItem { + outputs: string[]; + canvasConfig: ICanvasConfig; + serviceConfig: Partial; } export interface IProjectPayload { @@ -115,12 +302,14 @@ export interface IProjectPayload { }; } +export interface ISaturatedService extends Partial, ICanvasConfig {} + export interface IGeneratePayload { data: { configs: []; networks: []; secrets: []; - services: IService[]; + services: ISaturatedService[]; connections: [[string, string]]; version: number; volumes: []; diff --git a/services/frontend/src/utils/data/libraries.ts b/services/frontend/src/utils/data/libraries.ts index 3d27898..e4e9719 100644 --- a/services/frontend/src/utils/data/libraries.ts +++ b/services/frontend/src/utils/data/libraries.ts @@ -3,18 +3,18 @@ import { INodeGroup } from "../../types"; export const nodeLibraries: INodeGroup[] = [ { - Id: 1, - Name: NodeGroupType.Services, - Description: "Services", - NodeTypes: [ + id: 1, + name: NodeGroupType.Services, + description: "Services", + nodeTypes: [ { - Id: 1, - Name: "service", - Type: "SERVICE", - Description: "Service node", - NoInputs: 1, - NoOutputs: 1, - IsActive: true + id: 1, + name: "service", + type: "SERVICE", + description: "Service node", + noInputs: 1, + noOutputs: 1, + isActive: true } ] } diff --git a/services/frontend/src/utils/data/serviceNode.ts b/services/frontend/src/utils/data/serviceNode.ts index 37f4177..7141a07 100644 --- a/services/frontend/src/utils/data/serviceNode.ts +++ b/services/frontend/src/utils/data/serviceNode.ts @@ -1,2 +1,2 @@ export const ServiceNodeConfiguration = - '{"prettyName":"","name":"","key":"service","type":"SERVICE","inputs":["op_source"],"outputs":[]}'; + '{"canvasConfig":{"name":""},"key":"service","type":"SERVICE","inputs":["op_source"],"outputs":[]}'; diff --git a/services/frontend/src/utils/generators.ts b/services/frontend/src/utils/generators.ts index 7dce271..1c645c2 100644 --- a/services/frontend/src/utils/generators.ts +++ b/services/frontend/src/utils/generators.ts @@ -1,14 +1,14 @@ -import { IClientNodeItem, IService, IGeneratePayload } from "../types"; +import { IClientNodeItem, IGeneratePayload, ISaturatedService } from "../types"; import { Dictionary } from "lodash"; -const getServices = (graphNodes: Dictionary): IService[] => { - const ret: IService[] = []; +const getServices = ( + graphNodes: Dictionary +): ISaturatedService[] => { + const ret: ISaturatedService[] = []; for (const [, value] of Object.entries(graphNodes)) { ret.push({ - name: value.configuration.name, - labels: { - key: value.key - } + ...value.canvasConfig, + ...value.serviceConfig }); } diff --git a/services/frontend/src/utils/index.ts b/services/frontend/src/utils/index.ts index 0bf1d6e..1d45421 100644 --- a/services/frontend/src/utils/index.ts +++ b/services/frontend/src/utils/index.ts @@ -13,26 +13,11 @@ import { import { LOCAL_STORAGE } from "../constants"; import { IClientNodeItem, - IServiceNodeItem, INodeLibraryItem, INodeGroup, - IContainer + ICanvasConfig } from "../types"; -interface IConf { - prettyName: string; - name: string; - description: string; - type: string; - container?: IContainer; -} - -interface IServiceConf { - prettyName: string; - name: string; - template: string; -} - export function ensure( argument: T | undefined | null, message = "This value was promised to be there." @@ -44,8 +29,8 @@ export function ensure( return argument; } -export const parseSingleNode = (configurationStr: string): IServiceNodeItem => { - let node: IServiceNodeItem = {} as IServiceNodeItem; +export const parseSingleNode = (configurationStr: string): IClientNodeItem => { + let node: IClientNodeItem = {} as IClientNodeItem; const configurationObj = JSON.parse(configurationStr); if (isPlainObject(configurationObj)) { @@ -62,8 +47,8 @@ export const formatName = (name: string): string => { export const parseConfiguration = ( configurationStr: string -): IServiceNodeItem[] => { - let nodes: IServiceNodeItem[] = []; +): IClientNodeItem[] => { + let nodes: IClientNodeItem[] = []; const configurationObj = JSON.parse(configurationStr); if (isPlainObject(configurationObj)) { @@ -86,7 +71,7 @@ export const parseConfiguration = ( export const flattenLibraries = ( sections: INodeGroup[] ): INodeLibraryItem[] => { - return flattenDeep(sections.map((x) => x.NodeTypes)); + return flattenDeep(sections.map((x) => x.nodeTypes)); }; const getEndPointUuids = ( @@ -120,21 +105,16 @@ export const attachUUID = (key: string): string => { }; export const getClientNodeItem = ( - nodeItem: IServiceNodeItem, + nodeItem: IClientNodeItem, library: INodeLibraryItem ): IClientNodeItem => { const uniqueKey = attachUUID(nodeItem.key); return { + ...nodeItem, key: uniqueKey, - type: nodeItem.type, - position: nodeItem.position, - inputs: getEndPointUuids(uniqueKey, "ip", library.NoInputs), - configuration: { - ...nodeItem.configuration, - name: formatName(nodeItem.configuration.prettyName) - }, - outputs: getEndPointUuids(uniqueKey, "op", library.NoOutputs) + inputs: getEndPointUuids(uniqueKey, "ip", library.noInputs), + outputs: getEndPointUuids(uniqueKey, "op", library.noOutputs) }; }; @@ -169,7 +149,7 @@ export const getConnections = ( }; export const getClientNodesAndConnections = ( - nodeItems: IServiceNodeItem[], + nodeItems: IClientNodeItem[], sections: INodeGroup[] ): Dictionary => { if (!Array.isArray(nodeItems) || !Array.isArray(sections)) { @@ -180,7 +160,7 @@ export const getClientNodesAndConnections = ( const clientItems = nodeItems.map((x) => { return getClientNodeItem( x, - ensure(libraries.find((l) => l.Type === x.type)) + ensure(libraries.find((l) => l.type === x.type)) ); }); @@ -192,25 +172,9 @@ export const getNodeKeyFromConnectionId = (uuid: string) => { return key; }; -export const initialValues = (): IConf => { - return { - prettyName: "Unnamed", - name: "unnamed", - description: "", - type: "", - container: { - name: "", - image: "", - imagePullPolicy: "" - } - }; -}; - -export const serviceInitialValues = (): IServiceConf => { +export const canvasConfigInitialValues = (): ICanvasConfig => { return { - prettyName: "Unnamed", - name: "unnamed", - template: "" + service_name: "unnamed" }; }; diff --git a/services/frontend/src/utils/position.ts b/services/frontend/src/utils/position.ts index 18c4e98..2ed8721 100644 --- a/services/frontend/src/utils/position.ts +++ b/services/frontend/src/utils/position.ts @@ -1,8 +1,8 @@ -import { IServiceNodeItem } from "../types"; import * as d3 from "d3"; +import { IClientNodeItem } from "../types"; import { getNodeKeyFromConnectionId } from "./index"; -interface INodeItemWithParent extends IServiceNodeItem { +interface INodeItemWithParent extends IClientNodeItem { parent: string; } @@ -10,7 +10,7 @@ const nodeWidth = 150; const nodeHeight = 60; export const getHierarchyTree = ( - nodes: IServiceNodeItem[] + nodes: IClientNodeItem[] ): d3.HierarchyPointNode => { const data = nodes.map((node): INodeItemWithParent => { return { @@ -48,9 +48,9 @@ export const getHierarchyTree = ( }; export const getNodesPositions = ( - nodes: IServiceNodeItem[] -): [IServiceNodeItem[], number, number] => { - const nodeWithPosition: IServiceNodeItem[] = []; + nodes: IClientNodeItem[] +): [IClientNodeItem[], number, number] => { + const nodeWithPosition: IClientNodeItem[] = []; const tree = getHierarchyTree(nodes); let x0 = Infinity; let x1 = -x0;