Skip to content

Commit 38b4b19

Browse files
committed
Merge branch 'develop' of https://github.com/devtron-labs/dashboard into feat/app-env-details
2 parents 3d55c71 + 9d99d4d commit 38b4b19

29 files changed

+938
-690
lines changed

.eslintignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ src/components/ApplicationGroup/SearchBar.tsx
3636
src/components/CIPipelineN/AdvancedConfigOptions.tsx
3737
src/components/CIPipelineN/Build.tsx
3838
src/components/CIPipelineN/CIPipeline.tsx
39-
src/components/CIPipelineN/ConditionContainer.tsx
4039
src/components/CIPipelineN/CustomImageTags.tsx
4140
src/components/CIPipelineN/CustomInputOutputVariables.tsx
4241
src/components/CIPipelineN/CustomInputVariableSelect.tsx

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.12.0-pre-0-beta-1",
7+
"@devtron-labs/devtron-fe-common-lib": "1.12.0-pre-2",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",

src/Pages/Shared/ConfigMapSecret/ConfigMapSecretData.tsx

Lines changed: 16 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,11 @@ import {
2828
convertKeyValuePairToYAML,
2929
convertYAMLToKeyValuePair,
3030
isCodeMirrorEnabled,
31-
KeyValueConfig,
3231
KeyValueTable,
32+
KeyValueTableData,
3333
MODES,
3434
noop,
3535
OverrideMergeStrategyType,
36-
PATTERNS,
3736
SelectPickerOptionType,
3837
StyledRadioGroup,
3938
ToastManager,
@@ -57,6 +56,8 @@ import { externalTypeSecretCodeEditorDataHeaders, renderYamlInfoText } from './h
5756
import { ConfigMapSecretDataProps } from './types'
5857
import {
5958
getCMCSExpressEditComparisonDataDiffConfig,
59+
getConfigMapSecretKeyValueTableRows,
60+
getConfigMapSecretKeyValueTableValidationSchema,
6061
getExpressEditComparisonViewLHS,
6162
getLockedYamlString,
6263
} from './utils'
@@ -99,81 +100,16 @@ export const ConfigMapSecretData = ({
99100
const isSelectedTypeVolume =
100101
data.externalType === '' && data.selectedType === configMapSecretMountDataMap.volume.value
101102

102-
// METHODS & CONFIGURATIONS
103-
const config: KeyValueConfig<'k' | 'v'> = {
104-
headers: [
105-
{
106-
label: isSelectedTypeVolume ? 'File Name' : 'Key',
107-
key: 'k',
108-
},
109-
{
110-
label: isSelectedTypeVolume ? 'File Content' : 'Value',
111-
key: 'v',
112-
},
113-
],
114-
rows: data.currentData.map(({ k, v, id }) => ({
115-
data: {
116-
k: {
117-
value: k,
118-
},
119-
v: {
120-
value: typeof v === 'object' ? YAMLStringify(v) : v.toString(),
121-
},
122-
},
123-
id,
124-
})),
125-
}
126-
103+
// METHODS
127104
const onMergeStrategySelect = (newValue: SelectPickerOptionType) => {
128105
handleMergeStrategyChange(newValue.value as OverrideMergeStrategyType)
129106
}
130107

131-
const keyValueTableHandleChange =
132-
(onChange: (value: unknown) => void) => (rowId: string | number, headerKey: string, value: string) => {
133-
// - When data is changed from the YAML editor to the GUI, IDs are mapped to indices (numbers).
134-
// - When data is added via the GUI, IDs are created internally by the GUI editor as strings.
135-
const _currentData = data.currentData.reduce(
136-
(acc, currentData) => {
137-
if (currentData.id === rowId) {
138-
// If the item is found, update it with the new value and reset errors.
139-
acc.found = true
140-
acc.updatedData.push({
141-
...currentData,
142-
[headerKey]: value,
143-
})
144-
} else {
145-
// If the item is not the one we're looking for, just add it as is.
146-
acc.updatedData.push(currentData)
147-
}
148-
return acc
149-
},
150-
{ updatedData: [], found: false },
151-
)
152-
153-
// If the item is not found, it means it's a new entry added via the GUI editor.
154-
// Create a new data object and add it to the current data state.
155-
if (!_currentData.found) {
156-
_currentData.updatedData.push({
157-
k: '',
158-
v: '',
159-
[headerKey]: value,
160-
id: rowId,
161-
})
162-
}
163-
164-
// call useForm register 'onChange' method to update the form data value
165-
onChange(_currentData.updatedData)
166-
// Convert the current key-value data to YAML format and set it in the 'yaml' field.
167-
setValue('yaml', convertKeyValuePairToYAML(_currentData.updatedData), { shouldDirty: true })
168-
}
169-
170-
const keyValueHandleDelete = (onChange: (e: unknown) => void) => (rowId: string | number) => {
171-
// Create a new array by filtering out the item with the matching rowId.
172-
const _currentData = data.currentData.filter(({ id }) => id !== rowId)
108+
const keyValueTableHandleChange = (onChange: (value: unknown) => void) => (keyValueData: KeyValueTableData[]) => {
173109
// call useForm register 'onChange' method to update the form data value
174-
onChange(_currentData)
110+
onChange(keyValueData)
175111
// Convert the current key-value data to YAML format and set it in the 'yaml' field.
176-
setValue('yaml', convertKeyValuePairToYAML(_currentData), { shouldDirty: true })
112+
setValue('yaml', convertKeyValuePairToYAML(keyValueData), { shouldDirty: true })
177113
}
178114

179115
const keyValueHandleError = (err: boolean) => {
@@ -467,28 +403,21 @@ export const ConfigMapSecretData = ({
467403
isAdditionNotAllowed={secretMode || data.yamlMode || data.external}
468404
readOnly={readOnly || secretMode}
469405
isSortable
470-
config={config}
406+
headerLabel={{
407+
key: isSelectedTypeVolume ? 'File Name' : 'Key',
408+
value: isSelectedTypeVolume ? 'File Content' : 'Value',
409+
}}
410+
rows={getConfigMapSecretKeyValueTableRows(data.currentData)}
471411
placeholder={{
472-
k: 'Enter Key',
473-
v: 'Enter Value',
412+
key: 'Enter Key',
413+
value: 'Enter Value',
474414
}}
475415
onChange={keyValueTableHandleChange(onChange)}
476416
maskValue={{
477-
v: isLocked,
417+
value: isLocked,
478418
}}
479-
onDelete={keyValueHandleDelete(onChange)}
480419
showError
481-
validationSchema={(value, key) => {
482-
if (key === 'k' && value) {
483-
const isValid = new RegExp(PATTERNS.CONFIG_MAP_AND_SECRET_KEY).test(value)
484-
return isValid
485-
}
486-
return true
487-
}}
488-
errorMessages={[
489-
'Can only contain alphanumeric chars and ( - ), ( _ ), ( . )',
490-
'Spaces not allowed',
491-
]}
420+
validationSchema={getConfigMapSecretKeyValueTableValidationSchema}
492421
onError={keyValueHandleError}
493422
headerComponent={renderSecretShowHide(false)}
494423
validateEmptyKeys

src/Pages/Shared/ConfigMapSecret/utils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ import {
3939
getConfigMapSecretPayload,
4040
getSecretDataTypeOptions,
4141
getSelectPickerOptionByValue,
42+
KeyValueTableData,
43+
KeyValueTableProps,
4244
OverrideMergeStrategyType,
45+
PATTERNS,
4346
SelectPickerOptionType,
4447
YAMLStringify,
4548
} from '@devtron-labs/devtron-fe-common-lib'
@@ -398,6 +401,30 @@ export const getConfigMapSecretDataType = (
398401
: (getSelectPickerOptionByValue(getSecretDataTypeOptions(false, true), externalType).label as string)
399402
}
400403

404+
export const getConfigMapSecretKeyValueTableRows = (data: KeyValueTableData[]): KeyValueTableProps['rows'] =>
405+
data.map(({ key, value, id }) => ({
406+
data: {
407+
key: {
408+
value: key,
409+
},
410+
value: {
411+
value: typeof value === 'object' ? YAMLStringify(value) : value.toString(),
412+
},
413+
},
414+
id,
415+
}))
416+
417+
export const getConfigMapSecretKeyValueTableValidationSchema: KeyValueTableProps['validationSchema'] = (value, key) => {
418+
if (key === 'key' && value) {
419+
const isValid = new RegExp(PATTERNS.CONFIG_MAP_AND_SECRET_KEY).test(value)
420+
return {
421+
isValid,
422+
errorMessages: ['Can only contain alphanumeric chars and ( - ), ( _ ), ( . )', 'Spaces not allowed'],
423+
}
424+
}
425+
return { isValid: true }
426+
}
427+
401428
export const shouldHidePatchOption = (configMapSecretData: CMSecretConfigData, isJob: boolean) =>
402429
isJob || configMapSecretData?.external || false
403430

src/components/CIPipelineN/AdvancedConfigOptions.tsx

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
import React, { useContext, useEffect, useState } from 'react'
18-
import { CIBuildConfigType, CIBuildType, noop } from '@devtron-labs/devtron-fe-common-lib'
17+
import { useContext, useEffect, useState } from 'react'
18+
import { CIBuildConfigType, CIBuildType, KeyValueTableProps, noop } from '@devtron-labs/devtron-fe-common-lib'
1919
import CIConfig from '../ciConfig/CIConfig'
2020
import DockerArgs from './DockerArgs'
2121
import CustomImageTags from './CustomImageTags'
@@ -24,7 +24,6 @@ import { ComponentStates } from '../../Pages/Shared/EnvironmentOverride/Environm
2424
import { AdvancedConfigOptionsProps, CIConfigParentState } from '../ciConfig/types'
2525
import { DockerConfigOverrideKeys } from '../ciPipeline/types'
2626
import { OptionType } from '../app/types'
27-
import { DockerArgsAction, HandleDockerArgsUpdateType } from './types'
2827
import { getTargetPlatformMap } from '../ciConfig/CIConfig.utils'
2928
import { pipelineContext } from '../workflowEditor/workflowEditor'
3029
import '../ciConfig/CIConfig.scss'
@@ -83,32 +82,22 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
8382
}
8483

8584
// All updates to docker args will be handled here, which will will be further merged into formData on introduction of reducer
86-
const handleDockerArgsUpdate = ({ action, argData }: HandleDockerArgsUpdateType) => {
85+
const handleDockerArgsUpdate: KeyValueTableProps['onChange'] = (args) => {
8786
setFormData((prevFormData) => {
8887
const _form = structuredClone(prevFormData)
89-
90-
switch (action) {
91-
case DockerArgsAction.ADD:
92-
_form.args.unshift({ key: '', value: '' })
93-
break
94-
95-
case DockerArgsAction.UPDATE_KEY:
96-
_form.args[argData.index]['key'] = argData.value
97-
break
98-
99-
case DockerArgsAction.UPDATE_VALUE:
100-
_form.args[argData.index]['value'] = argData.value
101-
break
102-
103-
case DockerArgsAction.DELETE:
104-
_form.args = _form.args.filter((_, index) => index !== argData.index)
105-
break
106-
}
88+
_form.args = args
10789

10890
return _form
10991
})
11092
}
11193

94+
const handleDockerArgsError: KeyValueTableProps['onError'] = (errorState) => {
95+
setFormDataErrorObj({
96+
...formDataErrorObj,
97+
dockerArgsError: { isValid: !errorState, message: 'Invalid docker build arguments' },
98+
})
99+
}
100+
112101
const updateDockerConfigOverride = (
113102
key: string,
114103
value: CIBuildConfigType | OptionType[] | boolean | string,
@@ -238,7 +227,11 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
238227
)}
239228

240229
{showNonBuildpackOptions && (
241-
<DockerArgs args={formData.args} handleDockerArgsUpdate={handleDockerArgsUpdate} />
230+
<DockerArgs
231+
args={formData.args}
232+
handleDockerArgsUpdate={handleDockerArgsUpdate}
233+
handleDockerArgsError={handleDockerArgsError}
234+
/>
242235
)}
243236
</div>
244237
</div>

src/components/CIPipelineN/CIPipeline.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,7 @@ import {
5858
sortObjectArrayAlphabetically,
5959
} from '../common'
6060
import { BuildStageVariable, BuildTabText, JobPipelineTabText, URLS, ViewType } from '../../config'
61-
import {
62-
getInitData,
63-
getInitDataWithCIPipeline,
64-
saveCIPipeline,
65-
} from '../ciPipeline/ciPipeline.service'
61+
import { getInitData, getInitDataWithCIPipeline, saveCIPipeline } from '../ciPipeline/ciPipeline.service'
6662
import { ValidationRules } from '../ciPipeline/validationRules'
6763
import { CIBuildType, CIPipelineBuildType, CIPipelineDataType, CIPipelineType } from '../ciPipeline/types'
6864
import { ReactComponent as Close } from '../../assets/icons/ic-cross.svg'
@@ -175,6 +171,10 @@ export default function CIPipeline({
175171
message: '',
176172
isValid: true,
177173
},
174+
dockerArgsError: {
175+
message: '',
176+
isValid: true,
177+
},
178178
})
179179

180180
const [ciPipeline, setCIPipeline] = useState<CIPipelineDataType>({
@@ -407,8 +407,10 @@ export default function CIPipeline({
407407
): void => {
408408
const _formDataErrorObj = {
409409
...(formDataErrorObject ?? formDataErrorObj),
410+
// validating name always as it's a mandatory field
410411
name: validationRules.name(_formData.name),
411-
} // validating name always as it's a mandatory field
412+
}
413+
412414
if (stageName === BuildStageVariable.Build) {
413415
_formDataErrorObj[BuildStageVariable.Build].isValid = _formDataErrorObj.name.isValid
414416

@@ -426,7 +428,9 @@ export default function CIPipeline({
426428
}
427429
}
428430

429-
_formDataErrorObj[BuildStageVariable.Build].isValid = _formDataErrorObj.name.isValid && valid
431+
_formDataErrorObj[BuildStageVariable.Build].isValid =
432+
_formDataErrorObj.name.isValid && valid && _formDataErrorObj.dockerArgsError.isValid
433+
430434
if (!_formDataErrorObj[BuildStageVariable.Build].isValid) {
431435
setShowFormError(true)
432436
}
@@ -622,7 +626,7 @@ export default function CIPipeline({
622626
) {
623627
setApiInProgress(false)
624628
const branchNameNotPresent = formData.materials.some((_mat) => !_mat.value)
625-
if (formData.name === '' || branchNameNotPresent) {
629+
if (formData.name === '' || branchNameNotPresent || !formDataErrorObj.dockerArgsError.isValid) {
626630
ToastManager.showToast({
627631
variant: ToastVariantType.error,
628632
description: 'Please ensure all fields are valid',

0 commit comments

Comments
 (0)