11import { MinusCircleOutlined , PlusOutlined } from '@ant-design/icons' ;
2- import { Button , Form , FormItemProps , Input , InputRef } from 'antd' ;
2+ import {
3+ AutoComplete ,
4+ Button ,
5+ Form ,
6+ FormItemProps ,
7+ Input ,
8+ InputRef ,
9+ } from 'antd' ;
310import { FormListProps } from 'antd/lib/form' ;
411import { BAIFlex } from 'backend.ai-ui' ;
512import _ from 'lodash' ;
613import React , { useRef } from 'react' ;
714import { useTranslation } from 'react-i18next' ;
815
16+ export interface EnvVarConfig {
17+ variable : string ;
18+ placeholder ?: string ;
19+ required ?: boolean ;
20+ description ?: string ;
21+ }
22+
923interface EnvVarFormListProps extends Omit < FormListProps , 'children' > {
1024 formItemProps ?: FormItemProps ;
25+ requiredEnvVars ?: EnvVarConfig [ ] ;
26+ optionalEnvVars ?: EnvVarConfig [ ] ;
1127}
1228
1329export interface EnvVarFormListValue {
@@ -17,11 +33,67 @@ export interface EnvVarFormListValue {
1733// TODO: validation rule for duplicate variable name
1834const EnvVarFormList : React . FC < EnvVarFormListProps > = ( {
1935 formItemProps,
36+ requiredEnvVars,
37+ optionalEnvVars,
2038 ...props
2139} ) => {
40+ 'use memo' ;
2241 const inputRef = useRef < InputRef > ( null ) ;
2342 const { t } = useTranslation ( ) ;
2443 const form = Form . useFormInstance ( ) ;
44+
45+ const allEnvVars = [
46+ ..._ . filter (
47+ requiredEnvVars || [ ] ,
48+ ( env ) : env is EnvVarConfig => env != null && ! ! env . variable ,
49+ ) ,
50+ ..._ . filter (
51+ optionalEnvVars || [ ] ,
52+ ( env ) : env is EnvVarConfig => env != null && ! ! env . variable ,
53+ ) ,
54+ ] ;
55+
56+ const getPlaceholderForVariable = ( variable : string ) => {
57+ if ( ! variable || ! allEnvVars . length )
58+ return t ( 'session.launcher.EnvironmentVariableValue' ) ;
59+ const envVarConfig = _ . find (
60+ allEnvVars ,
61+ ( env ) => env && env . variable === variable ,
62+ ) ;
63+ return (
64+ envVarConfig ?. placeholder ||
65+ t ( 'session.launcher.EnvironmentVariableValue' )
66+ ) ;
67+ } ;
68+
69+ const getAutoCompleteOptions = ( ) => {
70+ const currentValues = form . getFieldValue ( props . name ) || [ ] ;
71+ const usedVariables = _ . map (
72+ _ . filter (
73+ currentValues ,
74+ ( item : EnvVarFormListValue ) =>
75+ item != null &&
76+ typeof item . variable === 'string' &&
77+ item . variable . trim ( ) !== '' ,
78+ ) ,
79+ 'variable' ,
80+ ) ;
81+
82+ return _ . map (
83+ _ . filter (
84+ optionalEnvVars || [ ] ,
85+ ( env ) : env is EnvVarConfig =>
86+ env != null &&
87+ ! ! env . variable &&
88+ ! _ . includes ( usedVariables , env . variable ) ,
89+ ) ,
90+ ( env ) => ( {
91+ value : env . variable ,
92+ label : env . variable ,
93+ } ) ,
94+ ) ;
95+ } ;
96+
2597 return (
2698 < Form . List { ...props } >
2799 { ( fields , { add, remove } ) => {
@@ -34,6 +106,7 @@ const EnvVarFormList: React.FC<EnvVarFormListProps> = ({
34106 style = { { marginBottom : 0 , flex : 1 } }
35107 name = { [ name , 'variable' ] }
36108 rules = { [
109+ ...( formItemProps ?. rules || [ ] ) ,
37110 {
38111 required : true ,
39112 message : t ( 'session.launcher.EnterEnvironmentVariable' ) ,
@@ -71,18 +144,38 @@ const EnvVarFormList: React.FC<EnvVarFormListProps> = ({
71144 ] }
72145 { ...formItemProps }
73146 >
74- < Input
75- ref = { index === fields . length - 1 ? inputRef : null }
76- placeholder = "Variable"
77- onChange = { ( ) => {
78- const fieldNames = fields . map ( ( _field , index ) => [
79- props . name ,
80- index ,
81- 'variable' ,
82- ] ) ;
83- form . validateFields ( fieldNames ) ;
84- } }
85- />
147+ { optionalEnvVars && getAutoCompleteOptions ( ) . length > 0 ? (
148+ < AutoComplete
149+ placeholder = { t ( 'session.launcher.EnvironmentVariable' ) }
150+ options = { getAutoCompleteOptions ( ) }
151+ onChange = { ( ) => {
152+ const fieldNames = fields . map ( ( _field , index ) => [
153+ props . name ,
154+ index ,
155+ 'variable' ,
156+ ] ) ;
157+ form . validateFields ( fieldNames ) ;
158+ } }
159+ filterOption = { ( inputValue , option ) =>
160+ option ?. value
161+ ?. toLowerCase ( )
162+ . indexOf ( inputValue . toLowerCase ( ) ) !== - 1
163+ }
164+ />
165+ ) : (
166+ < Input
167+ ref = { index === fields . length - 1 ? inputRef : null }
168+ placeholder = { t ( 'session.launcher.EnvironmentVariable' ) }
169+ onChange = { ( ) => {
170+ const fieldNames = fields . map ( ( _field , index ) => [
171+ props . name ,
172+ index ,
173+ 'variable' ,
174+ ] ) ;
175+ form . validateFields ( fieldNames ) ;
176+ } }
177+ />
178+ ) }
86179 </ Form . Item >
87180 < Form . Item
88181 { ...restField }
@@ -97,17 +190,12 @@ const EnvVarFormList: React.FC<EnvVarFormListProps> = ({
97190 } ,
98191 ] }
99192 validateTrigger = { [ 'onChange' , 'onBlur' ] }
193+ dependencies = { [ [ props . name , name , 'variable' ] ] }
100194 >
101195 < Input
102- placeholder = "Value"
103- // onChange={() => {
104- // const valueFields = fields.map((field, index) => [
105- // props.name,
106- // index,
107- // 'value',
108- // ]);
109- // form.validateFields(valueFields);
110- // }}
196+ placeholder = { getPlaceholderForVariable (
197+ form . getFieldValue ( [ props . name , name , 'variable' ] ) ,
198+ ) }
111199 />
112200 </ Form . Item >
113201 < MinusCircleOutlined onClick = { ( ) => remove ( name ) } />
@@ -161,12 +249,20 @@ const sensitivePatterns = [
161249] ;
162250
163251export function isSensitiveEnv ( key : string ) {
164- return sensitivePatterns . some ( ( pattern ) => pattern . test ( key ) ) ;
252+ return _ . some ( sensitivePatterns , ( pattern ) => pattern . test ( key ) ) ;
165253}
166254
255+ // Environment variables that should not be sanitized even if they match sensitive patterns
256+ const SANITIZE_EXCEPTIONS = [ 'NGC_API_KEY' ] ;
257+
167258export function sanitizeSensitiveEnv ( envs : EnvVarFormListValue [ ] ) {
168259 return _ . map ( envs , ( env ) => {
169- if ( env && isSensitiveEnv ( env . variable ) ) {
260+ if (
261+ env &&
262+ env . variable &&
263+ ! _ . includes ( SANITIZE_EXCEPTIONS , env . variable ) &&
264+ isSensitiveEnv ( env . variable )
265+ ) {
170266 return { ...env , value : '' } ;
171267 }
172268 return env ;
0 commit comments