From 2ac355998310c578344dc91452995a6aca64fced Mon Sep 17 00:00:00 2001 From: Romain Dreidemy <45939424+RomainDreidemy@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:27:08 +0200 Subject: [PATCH] feat(service image): handle context fields (#137) --- api/docs/docs.go | 33 ++++- api/docs/swagger.json | 30 ++++- api/docs/swagger.yaml | 20 +++- api/src/models/service_model.go | 113 ++++++++++-------- .../builders/service_builder.go | 8 +- api/src/services/factories/service_factory.go | 72 ++++++----- .../interfaces/Forms/Checkbox.interface.ts | 3 +- front/src/interfaces/Service.interface.ts | 1 + .../ServiceForm/LocalisationForm.organism.tsx | 65 ++-------- 9 files changed, 193 insertions(+), 152 deletions(-) diff --git a/api/docs/docs.go b/api/docs/docs.go index d2a1675d..53da4ff6 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -1,4 +1,5 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT +// Code generated by swaggo/swag. DO NOT EDIT. + package docs import "github.com/swaggo/swag" @@ -1650,9 +1651,15 @@ const docTemplate = `{ "containerName": { "type": "string" }, + "context": { + "type": "string" + }, "description": { "type": "string" }, + "dockerFile": { + "type": "string" + }, "dockerImage": { "type": "string" }, @@ -1665,6 +1672,9 @@ const docTemplate = `{ "envFile": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, @@ -1910,6 +1920,9 @@ const docTemplate = `{ "id": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, @@ -1927,9 +1940,15 @@ const docTemplate = `{ "containerName": { "type": "string" }, + "context": { + "type": "string" + }, "description": { "type": "string" }, + "dockerFile": { + "type": "string" + }, "dockerImage": { "type": "string" }, @@ -1951,6 +1970,9 @@ const docTemplate = `{ "id": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, @@ -1986,13 +2008,13 @@ const docTemplate = `{ "description": { "type": "string" }, - "dockerImage": { + "dockerFile": { "type": "string" }, - "dockerTag": { + "dockerImage": { "type": "string" }, - "dockerfile": { + "dockerTag": { "type": "string" }, "entrypoint": { @@ -2001,6 +2023,9 @@ const docTemplate = `{ "envFile": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, diff --git a/api/docs/swagger.json b/api/docs/swagger.json index 212ec5ea..c462f1a3 100644 --- a/api/docs/swagger.json +++ b/api/docs/swagger.json @@ -1641,9 +1641,15 @@ "containerName": { "type": "string" }, + "context": { + "type": "string" + }, "description": { "type": "string" }, + "dockerFile": { + "type": "string" + }, "dockerImage": { "type": "string" }, @@ -1656,6 +1662,9 @@ "envFile": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, @@ -1901,6 +1910,9 @@ "id": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, @@ -1918,9 +1930,15 @@ "containerName": { "type": "string" }, + "context": { + "type": "string" + }, "description": { "type": "string" }, + "dockerFile": { + "type": "string" + }, "dockerImage": { "type": "string" }, @@ -1942,6 +1960,9 @@ "id": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, @@ -1977,13 +1998,13 @@ "description": { "type": "string" }, - "dockerImage": { + "dockerFile": { "type": "string" }, - "dockerTag": { + "dockerImage": { "type": "string" }, - "dockerfile": { + "dockerTag": { "type": "string" }, "entrypoint": { @@ -1992,6 +2013,9 @@ "envFile": { "type": "string" }, + "imageSelectionType": { + "type": "string" + }, "name": { "type": "string" }, diff --git a/api/docs/swagger.yaml b/api/docs/swagger.yaml index c903b01d..d5d7f513 100644 --- a/api/docs/swagger.yaml +++ b/api/docs/swagger.yaml @@ -129,8 +129,12 @@ definitions: properties: containerName: type: string + context: + type: string description: type: string + dockerFile: + type: string dockerImage: type: string dockerTag: @@ -139,6 +143,8 @@ definitions: type: string envFile: type: string + imageSelectionType: + type: string name: type: string positionX: @@ -308,6 +314,8 @@ definitions: type: string id: type: string + imageSelectionType: + type: string name: type: string positionX: @@ -319,8 +327,12 @@ definitions: properties: containerName: type: string + context: + type: string description: type: string + dockerFile: + type: string dockerImage: type: string dockerTag: @@ -335,6 +347,8 @@ definitions: type: array id: type: string + imageSelectionType: + type: string name: type: string ports: @@ -358,16 +372,18 @@ definitions: type: string description: type: string + dockerFile: + type: string dockerImage: type: string dockerTag: type: string - dockerfile: - type: string entrypoint: type: string envFile: type: string + imageSelectionType: + type: string name: type: string positionX: diff --git a/api/src/models/service_model.go b/api/src/models/service_model.go index 185d802c..5ffc7f26 100644 --- a/api/src/models/service_model.go +++ b/api/src/models/service_model.go @@ -3,18 +3,19 @@ package models import "github.com/google/uuid" type Service struct { - ID *uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4();primary_key"` - Name string `gorm:"type:varchar(255)"` - ContainerName string `gorm:"type:varchar(255)"` - DockerImage string `gorm:"type:varchar(255)"` - DockerTag string `gorm:"type:varchar(255)"` - EnvFile string `gorm:"type:varchar(255)"` - Entrypoint string `gorm:"type:varchar(255)"` - Description string `gorm:"type:text"` - PositionX float32 `gorm:"type:decimal(20,8);not null"` - PositionY float32 `gorm:"type:decimal(20,8);not null"` - Context string `gorm:"type:varchar(255)"` - Dockerfile string `gorm:"type:varchar(255)"` + ID *uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4();primary_key"` + Name string `gorm:"type:varchar(255)"` + ContainerName string `gorm:"type:varchar(255)"` + ImageSelectionType string `gorm:"type:varchar(255);default:'remote'"` + DockerImage string `gorm:"type:varchar(255)"` + DockerTag string `gorm:"type:varchar(255)"` + EnvFile string `gorm:"type:varchar(255)"` + Entrypoint string `gorm:"type:varchar(255)"` + Description string `gorm:"type:text"` + PositionX float32 `gorm:"type:decimal(20,8);not null"` + PositionY float32 `gorm:"type:decimal(20,8);not null"` + Context string `gorm:"type:varchar(255)"` + Dockerfile string `gorm:"type:varchar(255)"` StackID string `gorm:"type:uuid;not null"` Stack Stack @@ -28,55 +29,63 @@ type Service struct { } type ServiceCreateInput struct { - Name string `json:"name"` - ContainerName string `json:"containerName"` - DockerImage string `json:"dockerImage"` - DockerTag string `json:"dockerTag"` - EnvFile string `json:"envFile"` - Entrypoint string `json:"entrypoint"` - Description string `json:"description"` - PositionX float32 `json:"positionX" validate:"required"` - PositionY float32 `json:"positionY" validate:"required"` + Name string `json:"name"` + ContainerName string `json:"containerName"` + ImageSelectionType string `json:"imageSelectionType"` + DockerImage string `json:"dockerImage"` + DockerTag string `json:"dockerTag"` + Dockerfile string `json:"dockerFile"` + Context string `json:"context"` + EnvFile string `json:"envFile"` + Entrypoint string `json:"entrypoint"` + Description string `json:"description"` + PositionX float32 `json:"positionX" validate:"required"` + PositionY float32 `json:"positionY" validate:"required"` } type ServiceUpdateInput struct { - Name string `json:"name"` - ContainerName string `json:"containerName"` - DockerImage string `json:"dockerImage"` - DockerTag string `json:"dockerTag"` - EnvFile string `json:"envFile"` - Context string `json:"context"` - Dockerfile string `json:"dockerfile"` - Entrypoint string `json:"entrypoint"` - Description string `json:"description"` - PositionX float32 `json:"positionX"` - PositionY float32 `json:"positionY"` + Name string `json:"name"` + ContainerName string `json:"containerName"` + ImageSelectionType string `json:"imageSelectionType"` + DockerImage string `json:"dockerImage"` + DockerTag string `json:"dockerTag"` + EnvFile string `json:"envFile"` + Context string `json:"context"` + Dockerfile string `json:"dockerFile"` + Entrypoint string `json:"entrypoint"` + Description string `json:"description"` + PositionX float32 `json:"positionX"` + PositionY float32 `json:"positionY"` } type ServiceResponse struct { - ID *uuid.UUID `json:"id"` - Name string `json:"name"` - ContainerName string `json:"containerName"` - DockerImage string `json:"dockerImage"` - DockerTag string `json:"dockerTag"` - EnvFile string `json:"envFile"` - Entrypoint string `json:"entrypoint"` - Description string `json:"description"` - PositionX float32 `json:"positionX"` - PositionY float32 `json:"positionY"` + ID *uuid.UUID `json:"id"` + Name string `json:"name"` + ContainerName string `json:"containerName"` + ImageSelectionType string `json:"imageSelectionType"` + DockerImage string `json:"dockerImage"` + DockerTag string `json:"dockerTag"` + EnvFile string `json:"envFile"` + Entrypoint string `json:"entrypoint"` + Description string `json:"description"` + PositionX float32 `json:"positionX"` + PositionY float32 `json:"positionY"` } type ServiceResponseItem struct { - ID *uuid.UUID `json:"id"` - Name string `json:"name"` - ContainerName string `json:"containerName"` - DockerImage string `json:"dockerImage"` - DockerTag string `json:"dockerTag"` - EnvFile string `json:"envFile"` - Entrypoint string `json:"entrypoint"` - Description string `json:"description"` - PositionX float32 `json:"positionX"` - PositionY float32 `json:"positionY"` + ID *uuid.UUID `json:"id"` + Name string `json:"name"` + ImageSelectionType string `json:"imageSelectionType"` + ContainerName string `json:"containerName"` + DockerImage string `json:"dockerImage"` + DockerTag string `json:"dockerTag"` + Dockerfile string `json:"dockerFile"` + Context string `json:"context"` + EnvFile string `json:"envFile"` + Entrypoint string `json:"entrypoint"` + Description string `json:"description"` + PositionX float32 `json:"positionX"` + PositionY float32 `json:"positionY"` Volumes []ServiceVolumeResponse `json:"volumes"` EnvVariables []ServiceEnvVariableResponse `json:"envVariables"` diff --git a/api/src/services/docker_compose/builders/service_builder.go b/api/src/services/docker_compose/builders/service_builder.go index fc05ae57..d93adf61 100644 --- a/api/src/services/docker_compose/builders/service_builder.go +++ b/api/src/services/docker_compose/builders/service_builder.go @@ -25,12 +25,10 @@ func DockerComposeServiceBuilder(service models.Service) models.DockerComposeSer Entrypoint: service.Entrypoint, } - if service.Context != "" && service.Dockerfile != "" { - dockerComposeService.Build = BuildDockerComposeServiceBuild(service) - } - - if service.DockerImage != "" { + if service.ImageSelectionType == "remote" { dockerComposeService.Image = BuildDockerComposeServiceImage(service) + } else { + dockerComposeService.Build = BuildDockerComposeServiceBuild(service) } if service.EnvFile != "" { diff --git a/api/src/services/factories/service_factory.go b/api/src/services/factories/service_factory.go index fd2b4a82..607e7ea1 100644 --- a/api/src/services/factories/service_factory.go +++ b/api/src/services/factories/service_factory.go @@ -6,19 +6,22 @@ import ( func BuildServiceResponse(service models.Service) models.ServiceResponseItem { return models.ServiceResponseItem{ - ID: service.ID, - Name: service.Name, - ContainerName: service.ContainerName, - DockerImage: service.DockerImage, - DockerTag: service.DockerTag, - EnvFile: service.EnvFile, - Entrypoint: service.Entrypoint, - Description: service.Description, - PositionX: service.PositionX, - PositionY: service.PositionY, - Volumes: BuildServiceVolumeResponses(service.ServiceVolumes), - EnvVariables: BuildServiceEnvVariableResponses(service.ServiceEnvVariables), - Ports: BuildServicePortResponses(service.ServicePorts), + ID: service.ID, + Name: service.Name, + ImageSelectionType: service.ImageSelectionType, + ContainerName: service.ContainerName, + DockerImage: service.DockerImage, + DockerTag: service.DockerTag, + Dockerfile: service.Dockerfile, + Context: service.Context, + EnvFile: service.EnvFile, + Entrypoint: service.Entrypoint, + Description: service.Description, + PositionX: service.PositionX, + PositionY: service.PositionY, + Volumes: BuildServiceVolumeResponses(service.ServiceVolumes), + EnvVariables: BuildServiceEnvVariableResponses(service.ServiceEnvVariables), + Ports: BuildServicePortResponses(service.ServicePorts), } } @@ -32,29 +35,34 @@ func BuildServiceResponses(services []models.Service) []models.ServiceResponseIt func BuildServiceFromServiceCreationInput(service models.ServiceCreateInput, stackId string) models.Service { return models.Service{ - Name: service.Name, - ContainerName: service.ContainerName, - DockerImage: service.DockerImage, - DockerTag: service.DockerTag, - EnvFile: service.EnvFile, - Entrypoint: service.Entrypoint, - Description: service.Description, - PositionX: service.PositionX, - PositionY: service.PositionY, - StackID: stackId, + Name: service.Name, + ContainerName: service.ContainerName, + ImageSelectionType: service.ImageSelectionType, + DockerImage: service.DockerImage, + DockerTag: service.DockerTag, + Dockerfile: service.Dockerfile, + EnvFile: service.EnvFile, + Entrypoint: service.Entrypoint, + Description: service.Description, + PositionX: service.PositionX, + PositionY: service.PositionY, + StackID: stackId, } } func BuildServiceFromServiceUpdateInput(service models.ServiceUpdateInput) models.Service { return models.Service{ - Name: service.Name, - ContainerName: service.ContainerName, - DockerImage: service.DockerImage, - DockerTag: service.DockerTag, - EnvFile: service.EnvFile, - Entrypoint: service.Entrypoint, - Description: service.Description, - PositionX: service.PositionX, - PositionY: service.PositionY, + Name: service.Name, + ContainerName: service.ContainerName, + ImageSelectionType: service.ImageSelectionType, + DockerImage: service.DockerImage, + DockerTag: service.DockerTag, + Dockerfile: service.Dockerfile, + Context: service.Context, + EnvFile: service.EnvFile, + Entrypoint: service.Entrypoint, + Description: service.Description, + PositionX: service.PositionX, + PositionY: service.PositionY, } } diff --git a/front/src/interfaces/Forms/Checkbox.interface.ts b/front/src/interfaces/Forms/Checkbox.interface.ts index 74ff6b2d..c6ecfbbb 100644 --- a/front/src/interfaces/Forms/Checkbox.interface.ts +++ b/front/src/interfaces/Forms/Checkbox.interface.ts @@ -1,10 +1,11 @@ import type React from 'react' +import { type TOnChange } from './file.interface' export interface CheckboxProps { label?: string name?: string value?: boolean className?: string - onChange?: (e: React.ChangeEvent) => void + onChange?: (e: TOnChange) => void onKeyDown?: (e: React.KeyboardEvent) => void } diff --git a/front/src/interfaces/Service.interface.ts b/front/src/interfaces/Service.interface.ts index 8cd17c22..9349df26 100644 --- a/front/src/interfaces/Service.interface.ts +++ b/front/src/interfaces/Service.interface.ts @@ -9,6 +9,7 @@ export interface IService { name: string description: string containerName: string + imageSelectionType: string dockerImage: string dockerTag: string entrypoint: string diff --git a/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx b/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx index 66dd15e9..7ee1752e 100644 --- a/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx +++ b/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx @@ -1,4 +1,4 @@ -import React, { type ChangeEvent } from 'react' +import React from 'react' import Input from '../../atoms/forms/Input.atom' import { type IService } from '../../../interfaces/Service.interface' import { type TEditor } from '../../../types/board/drawer/Common.bases' @@ -7,7 +7,7 @@ import { type EditorForm, TypeList } from '../../../forms/editor.structure' import { string } from 'yup' import Radio from '../../atoms/forms/RadioButton.atom' import useToggle from '../../../hooks/useToggle' -import { Errors } from '../../../enums/errors' +import { type TOnChange } from '../../../interfaces/Forms/Input.interface' const isDefined = (value: string): boolean => value !== undefined && value !== null && value !== '' @@ -32,7 +32,6 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: const isEmptyFields = !hasSelectedRemote && !hasSelectedLocal - const remoteFieldsDisabled = hasSelectedLocal || isEmptyFields const REMOTE_FIELDS: EditorForm[] = [ { label: 'Docker image', @@ -40,7 +39,7 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: type: TypeList.TEXT, component: Input, validator: string().nullable(), - disabled: remoteFieldsDisabled + disabled: serviceForm.imageSelectionType !== REMOTE_RADIO_NAME }, { label: 'Docker tag', @@ -48,11 +47,10 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: type: TypeList.TEXT, component: Input, validator: string().nullable(), - disabled: remoteFieldsDisabled + disabled: serviceForm.imageSelectionType !== REMOTE_RADIO_NAME } ] - const localFieldsDisabled = hasSelectedRemote || isEmptyFields const LOCAL_FIELDS: EditorForm[] = [ { label: 'Context', @@ -60,7 +58,7 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: type: TypeList.TEXT, component: Input, validator: string().nullable(), - disabled: localFieldsDisabled + disabled: serviceForm.imageSelectionType !== LOCAL_RADIO_NAME }, { label: 'Docker file', @@ -68,7 +66,7 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: type: TypeList.TEXT, component: Input, validator: string().nullable(), - disabled: localFieldsDisabled + disabled: serviceForm.imageSelectionType !== LOCAL_RADIO_NAME } ] @@ -77,49 +75,10 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: LOCAL_FIELDS ] - const onKeyFormReset = (key: keyof TEntity): void => { - // @ts-expect-error ts-migrate Object is possibly 'undefined'. - serviceForm[key] = '' - } - - const emptyFieldsRadioSelection = (name: string): void => { - if (name === LOCAL_RADIO_NAME) { - toggleSelectedLocal() - } else if (name === REMOTE_RADIO_NAME) { - toggleSelectedRemote() - } else { - throw new Error(Errors.NOT_IMPLEMENTED) - } - } - - const onEntityRadioChange = (name: string): void => { - if (name === LOCAL_RADIO_NAME) { - keys(REMOTE_FIELDS).forEach((key) => { - onKeyFormReset(key as keyof TEntity) - }) - } else if (name === REMOTE_RADIO_NAME) { - keys(LOCAL_FIELDS).forEach((key) => { - onKeyFormReset(key as keyof TEntity) - }) - } else { - throw new Error(Errors.NOT_IMPLEMENTED) - } - - toggleSelectedRemote() - toggleSelectedLocal() - onForm({ ...serviceForm }) - } - - const onRadioChange = (event: ChangeEvent): void => { - const { name, value } = event.target - - if (value !== 'on') return - - if (isEmptyFields) { - emptyFieldsRadioSelection(name) - } else { - onEntityRadioChange(name) - } + const onRadioChange = (event: TOnChange): void => { + event.target.value = event.target.name + event.target.name = 'imageSelectionType' + onChange(event) } const generateFields = (fields: EditorForm[]): JSX.Element[] => { @@ -147,10 +106,10 @@ const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }:
- +
- +
{LOCALISATION_FIELDS.map((editor, index) => {