Skip to content

Commit cfc2aa9

Browse files
authored
refactor: [M3-10038] - Address Linode Create Legacy Interface type-safety (linode#12612)
## Description 📝 Refactor types so that `LinodeCreateFormValues` satisfies the `CreateLinodeSchema` type without any omitting of types ## Changes 🔄 There should be no visual changes ### Verification steps (How to verify changes) - [ ] Ensure Linode creation with VPC IPv4 still works as expected
1 parent f089f93 commit cfc2aa9

File tree

6 files changed

+39
-97
lines changed

6 files changed

+39
-97
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/api-v4": Removed
3+
---
4+
5+
Delete `ConfigInterfaceIPv6` and use `IPv6Interface` instead ([#12612](https://github.com/linode/manager/pull/12612))

packages/api-v4/src/linodes/types.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,14 @@ export interface IPv6SLAAC {
195195
range: string;
196196
}
197197

198-
export interface ConfigInterfaceIPv6 {
199-
is_public: boolean;
200-
ranges: {
201-
range?: string;
202-
}[];
203-
slaac: IPv6SLAAC[];
204-
}
205-
206198
// The legacy interface type - for Configuration Profile Interfaces
207199
export interface Interface {
208200
active: boolean;
209201
id: number;
210202
ip_ranges?: string[];
211203
ipam_address: null | string;
212204
ipv4?: ConfigInterfaceIPv4;
213-
ipv6?: ConfigInterfaceIPv6;
205+
ipv6?: IPv6Interface;
214206
label: null | string;
215207
primary?: boolean;
216208
purpose: InterfacePurpose;
@@ -225,7 +217,7 @@ export interface InterfacePayload {
225217
*/
226218
ipam_address?: null | string;
227219
ipv4?: ConfigInterfaceIPv4;
228-
ipv6?: ConfigInterfaceIPv6;
220+
ipv6?: Partial<IPv6Interface>;
229221
/**
230222
* Required to specify a VLAN
231223
*/
@@ -301,14 +293,13 @@ export interface LinodeInterfaces {
301293
interfaces: LinodeInterface[];
302294
}
303295

304-
export interface LinodeInterfaceIPv6 {
296+
export interface IPv6Interface {
305297
is_public: boolean;
306298
ranges: {
307299
range: string;
308300
}[];
309301
slaac: IPv6SLAAC[];
310302
}
311-
312303
export interface VPCInterfaceData {
313304
ipv4?: {
314305
addresses: {
@@ -318,7 +309,7 @@ export interface VPCInterfaceData {
318309
}[];
319310
ranges: { range: string }[];
320311
};
321-
ipv6?: LinodeInterfaceIPv6;
312+
ipv6?: IPv6Interface;
322313
subnet_id: number;
323314
vpc_id: number;
324315
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Tech Stories
3+
---
4+
5+
Clean up types for `LinodeCreateFormValues` interface ([#12612](https://github.com/linode/manager/pull/12612))

packages/manager/src/features/Linodes/LinodeCreate/utilities.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,7 @@ const defaultInterfaces: InterfacePayload[] = [
190190
* For any extra values added to the form, we should make sure `getLinodeCreatePayload`
191191
* removes them from the payload before it is sent to the API.
192192
*/
193-
export interface LinodeCreateFormValues
194-
extends Omit<CreateLinodeRequest, 'maintenance_policy'> {
193+
export interface LinodeCreateFormValues extends CreateLinodeRequest {
195194
/**
196195
* Manually override firewall policy for sensitive users
197196
*/
@@ -202,10 +201,8 @@ export interface LinodeCreateFormValues
202201
hasSignedEUAgreement?: boolean;
203202
/**
204203
* Override the interfaces type of the Linode Create flow so it only has Legacy Interfaces.
205-
* `ipv4` and `ipv6` fields are only accepted for VPC interfaces and the omit type avoids
206-
* `CreateLinodeSchema` type errors (see https://github.com/linode/manager/pull/11942#discussion_r2043029481)
207204
*/
208-
interfaces: InterfacePayload[] | Omit<InterfacePayload, 'ipv4' | 'ipv6'>[];
205+
interfaces: InterfacePayload[];
209206
/**
210207
* The currently selected Linode (used for the Backups and Clone tabs)
211208
*/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/validation": Tech Stories
3+
---
4+
5+
Clean up linode ipv6 interfaces ([#12612](https://github.com/linode/manager/pull/12612))

packages/validation/src/linodes.schema.ts

Lines changed: 18 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -145,76 +145,31 @@ const slaacSchema = object().shape({
145145
message: 'Must be a /64 IPv6 network CIDR',
146146
test: (value) => validateIPv6PrefixLengthIs64(value),
147147
}),
148+
address: string().required(),
148149
});
149150

150-
const IPv6ConfigInterfaceRangesSchema = object({
151+
const VPCInterfaceIPv6RangeSchema = object({
151152
range: string()
152-
.optional()
153153
.test({
154154
name: 'IPv6 prefix length',
155155
message: 'Must be a /64 IPv6 network CIDR',
156156
test: (value) => validateIPv6PrefixLengthIs64(value),
157-
}),
157+
})
158+
.required(),
158159
});
159160

160-
const ipv6ConfigInterface = object().when('purpose', {
161-
is: 'vpc',
162-
then: (schema) =>
163-
schema
164-
.shape({
165-
slaac: array()
166-
.of(slaacSchema)
167-
.test({
168-
name: 'slaac field must have zero or one entry',
169-
message: 'ipv6.slaac field must have zero or one entry',
170-
test: (value) =>
171-
!value ? true : value?.length === 0 || value?.length === 1,
172-
}),
173-
ranges: array().of(IPv6ConfigInterfaceRangesSchema),
174-
is_public: boolean(),
175-
})
176-
.notRequired()
177-
.when('ipv4', {
178-
is: (value: unknown) => value === null || value === undefined,
179-
then: (schema) => schema.required(VPC_INTERFACE_IP_RULE),
180-
}),
181-
otherwise: (schema) =>
182-
schema
183-
.nullable()
184-
.test({
185-
name: testnameDisallowedBasedOnPurpose('VPC'),
186-
message: testmessageDisallowedBasedOnPurpose('vpc', 'ipv6.slaac'),
187-
test: (value: any) => {
188-
if (value?.slaac) {
189-
return typeof value.slaac === 'undefined';
190-
}
191-
192-
return true;
193-
},
194-
})
195-
.test({
196-
name: testnameDisallowedBasedOnPurpose('VPC'),
197-
message: testmessageDisallowedBasedOnPurpose('vpc', 'ipv6.ranges'),
198-
test: (value: any) => {
199-
if (value?.ranges) {
200-
return typeof value.ranges === 'undefined';
201-
}
202-
203-
return true;
204-
},
205-
})
206-
.test({
207-
name: testnameDisallowedBasedOnPurpose('VPC'),
208-
message: testmessageDisallowedBasedOnPurpose('vpc', 'ipv6.is_public'),
209-
test: (value: any) => {
210-
if (value?.is_public) {
211-
return typeof value.is_public === 'undefined';
212-
}
213-
214-
return true;
215-
},
216-
}),
217-
});
161+
const ipv6Interface = object({
162+
slaac: array()
163+
.of(slaacSchema)
164+
.test({
165+
name: 'slaac field must have zero or one entry',
166+
message: 'ipv6.slaac field must have zero or one entry',
167+
test: (value) =>
168+
!value ? true : value?.length === 0 || value?.length === 1,
169+
}),
170+
ranges: array().of(VPCInterfaceIPv6RangeSchema),
171+
is_public: boolean(),
172+
}).notRequired();
218173

219174
// This is the validation schema for legacy interfaces attached to configuration profiles
220175
// For new interfaces, denoted as Linode Interfaces, see CreateLinodeInterfaceSchema or ModifyLinodeInterfaceSchema
@@ -314,7 +269,7 @@ export const ConfigProfileInterfaceSchema = object().shape(
314269
}),
315270
}),
316271
ipv4: ipv4ConfigInterface,
317-
ipv6: ipv6ConfigInterface,
272+
ipv6: ipv6Interface.nonNullable(),
318273
ip_ranges: array()
319274
.of(string().defined())
320275
.notRequired()
@@ -385,20 +340,12 @@ export const UpdateConfigInterfaceSchema = object({
385340
ip_ranges: array().of(string().test(validateIP)).max(1).notRequired(),
386341
});
387342

388-
// const rootPasswordValidation = string().test(
389-
// 'is-strong-password',
390-
// 'Password does not meet strength requirements.',
391-
// (value: string) =>
392-
// Boolean(value) && zxcvbn(value).score >= MINIMUM_PASSWORD_STRENGTH
393-
// );
394-
395343
export const ResizeLinodeDiskSchema = object({
396344
size: number().required('Size is required.').min(1),
397345
});
398346

399347
export const UpdateLinodePasswordSchema = object({
400348
password: string().required('Password is required.'),
401-
// .concat(rootPasswordValidation)
402349
});
403350

404351
const MetadataSchema = object({
@@ -629,10 +576,6 @@ const VPCInterfaceIPv4RangeSchema = object({
629576
range: string().required('Range is required.'),
630577
});
631578

632-
const VPCInterfaceIPv6RangeSchema = object({
633-
range: string().test((value) => validateIPv6PrefixLengthIs64(value)),
634-
});
635-
636579
const PublicInterfaceRangeSchema = object({
637580
range: string().required('IPv6 range is required.').nullable(),
638581
});
@@ -663,11 +606,7 @@ export const CreateVPCInterfaceSchema = object({
663606
addresses: array().of(CreateVPCInterfaceIpv4AddressSchema),
664607
ranges: array().of(VPCInterfaceIPv4RangeSchema),
665608
}).notRequired(),
666-
ipv6: object({
667-
slaac: array().of(slaacSchema),
668-
ranges: array().of(VPCInterfaceIPv6RangeSchema),
669-
is_public: boolean(),
670-
}).notRequired(),
609+
ipv6: ipv6Interface,
671610
});
672611

673612
export const CreateLinodeInterfaceSchema = object({

0 commit comments

Comments
 (0)