fix(striker-ui): add gateway, DNS CSV, and input tests to NetworkInitForm

main
Tsu-ba-me 2 years ago
parent ca36fe21c0
commit 45b1e8a608
  1. 228
      striker-ui/components/NetworkInitForm.tsx

@ -32,6 +32,7 @@ import { v4 as uuidv4 } from 'uuid';
import API_BASE_URL from '../lib/consts/API_BASE_URL'; import API_BASE_URL from '../lib/consts/API_BASE_URL';
import { BLUE, GREY } from '../lib/consts/DEFAULT_THEME'; import { BLUE, GREY } from '../lib/consts/DEFAULT_THEME';
import { REP_IPV4, REP_IPV4_CSV } from '../lib/consts/REG_EXP_PATTERNS';
import BriefNetworkInterface from './BriefNetworkInterface'; import BriefNetworkInterface from './BriefNetworkInterface';
import Decorator from './Decorator'; import Decorator from './Decorator';
@ -39,12 +40,16 @@ import DropArea from './DropArea';
import FlexBox from './FlexBox'; import FlexBox from './FlexBox';
import IconButton from './IconButton'; import IconButton from './IconButton';
import InputWithRef, { InputForwardedRefContent } from './InputWithRef'; import InputWithRef, { InputForwardedRefContent } from './InputWithRef';
import { Message } from './MessageBox';
import MessageGroup, { MessageGroupForwardedRefContent } from './MessageGroup';
import OutlinedInputWithLabel from './OutlinedInputWithLabel'; import OutlinedInputWithLabel from './OutlinedInputWithLabel';
import { InnerPanel, InnerPanelHeader } from './Panels'; import { InnerPanel, InnerPanelHeader } from './Panels';
import periodicFetch from '../lib/fetchers/periodicFetch'; import periodicFetch from '../lib/fetchers/periodicFetch';
import SelectWithLabel from './SelectWithLabel'; import SelectWithLabel from './SelectWithLabel';
import Spinner from './Spinner'; import Spinner from './Spinner';
import sumstring from '../lib/sumstring'; import sumstring from '../lib/sumstring';
import { testInput, testNotBlank } from '../lib/test_input';
import { InputTestBatches } from '../types/TestInputFunction';
import { BodyText, DataGridCellText } from './Text'; import { BodyText, DataGridCellText } from './Text';
type NetworkInput = { type NetworkInput = {
@ -67,6 +72,8 @@ type NetworkInterfaceInputMap = Record<
type NetworkInitFormForwardRefContent = { type NetworkInitFormForwardRefContent = {
get?: () => { get?: () => {
domainNameServerCSV?: string;
gateway?: string;
networks: Omit< networks: Omit<
NetworkInput, NetworkInput,
'inputUUID' | 'ipAddressInputRef' | 'subnetMaskInputRef' 'inputUUID' | 'ipAddressInputRef' | 'subnetMaskInputRef'
@ -119,6 +126,7 @@ const REQUIRED_NETWORKS: NetworkInput[] = [
inputUUID: '30dd2ac5-8024-4a7e-83a1-6a3df7218972', inputUUID: '30dd2ac5-8024-4a7e-83a1-6a3df7218972',
interfaces: [], interfaces: [],
ipAddress: '10.200.1.1', ipAddress: '10.200.1.1',
name: `${NETWORK_TYPES.bcn} 1`,
subnetMask: '255.255.0.0', subnetMask: '255.255.0.0',
type: 'bcn', type: 'bcn',
}, },
@ -126,12 +134,16 @@ const REQUIRED_NETWORKS: NetworkInput[] = [
inputUUID: 'e7ef3af5-5602-440c-87f8-69c242e3d7f3', inputUUID: 'e7ef3af5-5602-440c-87f8-69c242e3d7f3',
interfaces: [], interfaces: [],
ipAddress: '10.201.1.1', ipAddress: '10.201.1.1',
name: `${NETWORK_TYPES.ifn} 1`,
subnetMask: '255.255.0.0', subnetMask: '255.255.0.0',
type: 'ifn', type: 'ifn',
}, },
]; ];
const BASE_INPUT_COUNT = 2;
const MAX_INTERFACES_PER_NETWORK = 2; const MAX_INTERFACES_PER_NETWORK = 2;
const PER_NETWORK_INPUT_COUNT = 3;
const NETWORK_INTERFACE_TEMPLATE = Array.from( const NETWORK_INTERFACE_TEMPLATE = Array.from(
{ length: MAX_INTERFACES_PER_NETWORK }, { length: MAX_INTERFACES_PER_NETWORK },
(unused, index) => index + 1, (unused, index) => index + 1,
@ -243,6 +255,7 @@ const NetworkForm: FC<{
interfaceIndex: number, interfaceIndex: number,
) => MUIBoxProps['onMouseUp']; ) => MUIBoxProps['onMouseUp'];
getNetworkTypeCount: (targetType: string, lastIndex?: number) => number; getNetworkTypeCount: (targetType: string, lastIndex?: number) => number;
inputTests: InputTestBatches;
networkIndex: number; networkIndex: number;
networkInput: NetworkInput; networkInput: NetworkInput;
networkInputs: NetworkInput[]; networkInputs: NetworkInput[];
@ -255,6 +268,7 @@ const NetworkForm: FC<{
}> = ({ }> = ({
createDropMouseUpHandler, createDropMouseUpHandler,
getNetworkTypeCount, getNetworkTypeCount,
inputTests,
networkIndex, networkIndex,
networkInput, networkInput,
networkInputs, networkInputs,
@ -272,6 +286,8 @@ const NetworkForm: FC<{
const { inputUUID, interfaces, ipAddress, subnetMask, type } = networkInput; const { inputUUID, interfaces, ipAddress, subnetMask, type } = networkInput;
const inputTestPrefix = `network${networkIndex}`;
const isNetworkOptional = networkIndex < optionalNetworkInputsLength; const isNetworkOptional = networkIndex < optionalNetworkInputsLength;
networkInput.ipAddressInputRef = ipAddressInputRef; networkInput.ipAddressInputRef = ipAddressInputRef;
@ -300,7 +316,12 @@ const NetworkForm: FC<{
)} )}
selectProps={{ selectProps={{
onChange: ({ target: { value } }) => { onChange: ({ target: { value } }) => {
networkInput.type = String(value); const networkType = String(value);
networkInput.type = networkType;
networkInput.name = `${
NETWORK_TYPES[networkType]
} ${getNetworkTypeCount(networkType, networkIndex)}`;
setNetworkInputs([...networkInputs]); setNetworkInputs([...networkInputs]);
}, },
@ -395,6 +416,12 @@ const NetworkForm: FC<{
id={`network-${inputUUID}-ip-address`} id={`network-${inputUUID}-ip-address`}
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="IP address" label="IP address"
onChange={({ target: { value } }) => {
testInput({
inputs: { [`${inputTestPrefix}IPAddress`]: { value } },
tests: inputTests,
});
}}
value={ipAddress} value={ipAddress}
/> />
} }
@ -406,6 +433,12 @@ const NetworkForm: FC<{
id={`network-${inputUUID}-subnet-mask`} id={`network-${inputUUID}-subnet-mask`}
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="Subnet mask" label="Subnet mask"
onChange={({ target: { value } }) => {
testInput({
inputs: { [`${inputTestPrefix}SubnetMask`]: { value } },
tests: inputTests,
});
}}
value={subnetMask} value={subnetMask}
/> />
} }
@ -432,6 +465,10 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
NetworkInterfaceOverviewMetadata | undefined NetworkInterfaceOverviewMetadata | undefined
>(); >();
const gatewayInputRef = useRef<InputForwardedRefContent<'string'>>({});
const dnsCSVInputRef = useRef<InputForwardedRefContent<'string'>>({});
const messageGroupRef = useRef<MessageGroupForwardedRefContent>({});
const { data: networkInterfaces = MOCK_NICS, isLoading } = periodicFetch< const { data: networkInterfaces = MOCK_NICS, isLoading } = periodicFetch<
NetworkInterfaceOverviewMetadata[] NetworkInterfaceOverviewMetadata[]
>(`${API_BASE_URL}/network-interface`, { >(`${API_BASE_URL}/network-interface`, {
@ -466,6 +503,136 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
[networkInputs, networkInterfaces, networkInterfaceInputMap], [networkInputs, networkInterfaces, networkInterfaceInputMap],
); );
const setGatewayInputMessage = useCallback(
(message?: Message) =>
messageGroupRef.current.setMessage?.call(null, 0, message),
[],
);
const setDomainNameServerCSVInputMessage = useCallback(
(message?: Message) =>
messageGroupRef.current.setMessage?.call(null, 1, message),
[],
);
const getNetworkInputMessageIndex = useCallback(
(networkIndex: number, inputIndex: number) =>
BASE_INPUT_COUNT +
(networkInputs.length - 1 - networkIndex) * PER_NETWORK_INPUT_COUNT +
inputIndex,
[networkInputs],
);
const setNetworkIPAddressInputMessage = useCallback(
(networkIndex: number, message?: Message) =>
messageGroupRef.current.setMessage?.call(
null,
getNetworkInputMessageIndex(networkIndex, 1),
message,
),
[getNetworkInputMessageIndex],
);
const setNetworkSubnetMaskInputMessage = useCallback(
(networkIndex: number, message?: Message) =>
messageGroupRef.current.setMessage?.call(
null,
getNetworkInputMessageIndex(networkIndex, 2),
message,
),
[getNetworkInputMessageIndex],
);
const inputTests: InputTestBatches = useMemo(() => {
const tests: InputTestBatches = {
domainNameServerCSV: {
defaults: {
onSuccess: () => {
setDomainNameServerCSVInputMessage(undefined);
},
},
tests: [
{
onFailure: () => {
setDomainNameServerCSVInputMessage({
children:
'Domain name servers should be a comma-separated list of IPv4 addresses.',
});
},
test: ({ value }) => REP_IPV4_CSV.test(value as string),
},
{ test: testNotBlank },
],
},
gateway: {
defaults: {
onSuccess: () => {
setGatewayInputMessage(undefined);
},
},
tests: [
{
onFailure: () => {
setGatewayInputMessage({
children: 'Gateway should be a valid IPv4 address.',
});
},
test: ({ value }) => REP_IPV4.test(value as string),
},
{ test: testNotBlank },
],
},
};
networkInputs.forEach(({ name }, networkIndex) => {
const inputTestPrefix = `network${networkIndex}`;
tests[`${inputTestPrefix}Name`] = {
tests: [{ test: testNotBlank }],
};
tests[`${inputTestPrefix}IPAddress`] = {
defaults: {
onSuccess: () => {
setNetworkIPAddressInputMessage(networkIndex, undefined);
},
},
tests: [
{
onFailure: () => {
setNetworkIPAddressInputMessage(networkIndex, {
children: `IP address in ${name} must be a valid IPv4 address.`,
});
},
test: ({ value }) => REP_IPV4.test(value as string),
},
{ test: testNotBlank },
],
};
tests[`${inputTestPrefix}SubnetMask`] = {
defaults: {
onSuccess: () => {
setNetworkSubnetMaskInputMessage(networkIndex, undefined);
},
},
tests: [
{
onFailure: () => {
setNetworkSubnetMaskInputMessage(networkIndex, {
children: `Subnet mask in ${name} must be a valid IPv4 address.`,
});
},
test: ({ value }) => REP_IPV4.test(value as string),
},
{ test: testNotBlank },
],
};
});
return tests;
}, [
networkInputs,
setDomainNameServerCSVInputMessage,
setGatewayInputMessage,
setNetworkIPAddressInputMessage,
setNetworkSubnetMaskInputMessage,
]);
const clearNetworkInterfaceHeld = useCallback(() => { const clearNetworkInterfaceHeld = useCallback(() => {
setNetworkInterfaceHeld(undefined); setNetworkInterfaceHeld(undefined);
}, []); }, []);
@ -474,7 +641,7 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
inputUUID: uuidv4(), inputUUID: uuidv4(),
interfaces: [], interfaces: [],
ipAddress: '', ipAddress: '',
name: '', name: 'Unknown Network',
subnetMask: '', subnetMask: '',
type: '', type: '',
}); });
@ -611,6 +778,8 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
ref, ref,
() => ({ () => ({
get: () => ({ get: () => ({
domainNameServerCSV: dnsCSVInputRef.current.getValue?.call(null),
gateway: gatewayInputRef.current.getValue?.call(null),
networks: networkInputs.map( networks: networkInputs.map(
( (
{ interfaces, ipAddressInputRef, subnetMaskInputRef, type }, { interfaces, ipAddressInputRef, subnetMaskInputRef, type },
@ -655,7 +824,7 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
'& > :not(:first-child, :last-child)': { '& > :not(:first-child, :nth-child(3))': {
marginTop: '1em', marginTop: '1em',
}, },
}} }}
@ -687,7 +856,7 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
sx={{ sx={{
'& > :first-child': { '& > :first-child': {
alignSelf: 'start', alignSelf: 'start',
marginTop: '1.1em', marginTop: '.7em',
}, },
'& > :last-child': { '& > :last-child': {
@ -709,9 +878,9 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
overflowX: 'auto', overflowX: 'auto',
paddingLeft: '.3em', paddingLeft: '.3em',
'& > *': { '& > div': {
marginBottom: '1em', marginBottom: '.8em',
marginTop: '1em', marginTop: '.4em',
minWidth: '13em', minWidth: '13em',
width: '25%', width: '25%',
}, },
@ -730,6 +899,7 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
{...{ {...{
createDropMouseUpHandler, createDropMouseUpHandler,
getNetworkTypeCount, getNetworkTypeCount,
inputTests,
networkIndex, networkIndex,
networkInput, networkInput,
networkInputs, networkInputs,
@ -743,6 +913,50 @@ const NetworkInitForm = forwardRef<NetworkInitFormForwardRefContent>(
})} })}
</MUIBox> </MUIBox>
</FlexBox> </FlexBox>
<FlexBox
sm="row"
sx={{ marginTop: '.2em', '& > :last-child': { flexGrow: 1 } }}
>
<InputWithRef
input={
<OutlinedInputWithLabel
id="network-init-gateway"
inputLabelProps={{ isNotifyRequired: true }}
onChange={({ target: { value } }) => {
testInput({
inputs: { gateway: { value } },
tests: inputTests,
});
}}
label="Gateway"
/>
}
ref={gatewayInputRef}
/>
<InputWithRef
input={
<OutlinedInputWithLabel
id="network-init-dns-csv"
inputLabelProps={{ isNotifyRequired: true }}
onChange={({ target: { value } }) => {
testInput({
inputs: { domainNameServerCSV: { value } },
tests: inputTests,
});
}}
label="Domain name server(s)"
/>
}
ref={dnsCSVInputRef}
/>
</FlexBox>
<MessageGroup
count={
BASE_INPUT_COUNT + networkInputs.length * PER_NETWORK_INPUT_COUNT
}
defaultMessageType="warning"
ref={messageGroupRef}
/>
</MUIBox> </MUIBox>
</MUIBox> </MUIBox>
); );

Loading…
Cancel
Save