Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upcoming: [M3-8028]: Linode Create Refactor - User Defined Fields #10395

Conversation

bnussman-akamai
Copy link
Contributor

@bnussman-akamai bnussman-akamai commented Apr 22, 2024

Description πŸ“

Adds initial User Defined Field support to the Linode Create flow when deploying from a StackScript

Changes πŸ”„

  • Extracts out some until functions from the existing create flow and unit tests them (no changes in logic, only naming changes) πŸ§ͺ
  • Adds initial User Defined Field support to the new Linode Create flow

Preview πŸ“·

Screenshot 2024-04-22 at 3 02 13β€―PM

How to test πŸ§ͺ

Prerequisites

  • Check out this PR or use an internal preview link
  • Enable the Linode Create v2 feature flag

Verification steps

  • Select various community StackScripts and check the functionality of the user defined fields section
  • Compare behavior to production

As an Author I have considered πŸ€”

  • πŸ‘€ Doing a self review
  • ❔ Our contribution guidelines
  • 🀏 Splitting feature into small PRs
  • βž• Adding a changeset
  • πŸ§ͺ Providing/Improving test coverage
  • πŸ” Removing all sensitive information from the code and PR description
  • 🚩 Using a feature flag to protect the release
  • πŸ‘£ Providing comprehensive reproduction steps
  • πŸ“‘ Providing or updating our documentation
  • πŸ•› Scheduling a pair reviewing session
  • πŸ“± Providing mobile support
  • β™Ώ Providing accessibility support

@bnussman-akamai bnussman-akamai marked this pull request as ready for review April 22, 2024 20:52
@bnussman-akamai bnussman-akamai requested a review from a team as a code owner April 22, 2024 20:52
@bnussman-akamai bnussman-akamai requested review from dwiley-akamai, carrillo-erik, hkhalil-akamai and abailly-akamai and removed request for a team April 22, 2024 20:52
Copy link

github-actions bot commented Apr 22, 2024

Coverage Report: βœ…
Base Coverage: 81.86%
Current Coverage: 81.86%

@bnussman-akamai bnussman-akamai changed the title upcoming: [M3-7872]: Linode Create Refactor - User Defined Fields upcoming: [M3-8028]: Linode Create Refactor - User Defined Fields Apr 23, 2024
Copy link
Contributor

@abailly-akamai abailly-akamai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-04-23 at 13 12 18

Screenshot 2024-04-23 at 13 12 58

I got a 500 trying to submit this one ☝️
Tried in prod with exact same specs and it went through

{
    "authorized_users": [],
    "backups_enabled": false,
    "booted": true,
    "image": "linode/almalinux8",
    "label": "setup-ipsec-vpn-us-east",
    "private_ip": false,
    "region": "us-east",
    "root_pass": "[pass]",
    "stackscript_data": {
        "VPN_PASSWORD": "dfasdf4534",
        "VPN_USER": "dddesdfgd",
        "VPN_IPSEC_PSK": "dfgsdfg564"
    },
    "stackscript_id": 37239,
    "tags": [],
    "type": "g6-nanode-1"
}

one thing i noticed is the stackscript_id seems to be missing in the v2 flow but unsure if that's the root cause


if (getIsUDFMultiSelect(userDefinedField)) {
const options = userDefinedField
.manyof!.split(',')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can have have better type safety around manyof & oneof (or safer conditionals)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What approach would you recommend here? Changing the condition to if (udf.manyof)? I can't think of a way to make getIsUDFMultiSelect more typesafe without writing lots more typescript types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the condition to if (udf.manyof)

yeah i don't have a better solution. maybe not crucial cause we know, but always in favor of being extra verbose to avoid non-null assertions

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably accomplish what you're looking for using a nifty feature of TypeScript: type predicates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about that, but wouldn't that require a new type?

interface UserDefinedFieldMulti extends UserDefinedField {
  manyof: string;
}

/**
 * @returns true if a user defined field should be treated as a multi-select
 */
export const getIsUDFMultiSelect = (udf: UserDefinedField): udf is UserDefinedFieldMulti => {
  return !!udf.manyof;
}

Is there a way to do it cleaner? I'm not sure having a bunch of extra types is worth it

@bnussman-akamai
Copy link
Contributor Author

@abailly-akamai I wasn't able to replicate the 500 error, but I'm assuming the empty placement_group object was confusing the API. I pushed d25d2ca based on that assumption

Copy link
Contributor

@abailly-akamai abailly-akamai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested well on my end with various payloads and stackscripts. βœ…

Thanks for the extra care of unit testing thoroughly ❀️

Copy link
Contributor

@dwiley-akamai dwiley-akamai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expect full parity at this stage when it comes to the images that are available for each StackScript? I noticed the below example:

Screenshots
This branch Prod
Screenshot 2024-04-23 at 4 07 27β€―PM Screenshot 2024-04-23 at 4 07 57β€―PM

Comment on lines +37 to +44
{formState.errors.stackscript_id && (
<Notice
spacingBottom={0}
spacingTop={8}
text={formState.errors.stackscript_id.message}
variant="error"
/>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity, how can we hit this error? You have to select a StackScript in order to select an image, and submitting the form with neither just gets you the Image is required. error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to trigger formState.errors.stackscript_id by sending a UDF that was super long

.oneof!.split(',')
.map((option) => ({ label: option }));

if (options.length > 4) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the condition be >= here? Storybook says we use radio buttons if there are 3 or fewer options

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to retain > 4 because that's what the existing create flow uses (see packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedSelect.tsx)

@bnussman-akamai
Copy link
Contributor Author

@dwiley-akamai There should be full parity with StackScript UDF fields. Your screenshot shows two different StackScripts selected, so I'm not sure if you were referring to UDFs. The icon missing in the new ImageSelect is expected. That hasn't been implemented yet

@bnussman-akamai bnussman-akamai added the Add'tl Approval Needed Waiting on another approval! label Apr 23, 2024
@dwiley-akamai
Copy link
Contributor

@dwiley-akamai There should be full parity with StackScript UDF fields. Your screenshot shows two different StackScripts selected, so I'm not sure if you were referring to UDFs. The icon missing in the new ImageSelect is expected. That hasn't been implemented yet

Whoops, you can disregard that comment. I definitely honed in on "Shadowsocks" in the StackScript name and overlooked that the two StackScripts weren't the same πŸ€¦πŸΎβ€β™‚οΈ

Copy link
Contributor

@hkhalil-akamai hkhalil-akamai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good and I tested deploying some linodes from stackscripts with UDFs. Left some suggestions.

@@ -15,7 +15,7 @@ interface Props {
disabled?: boolean;
isSelected: boolean;
onOpenDetails: () => void;
onSelect: () => void;
onSelect?: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious as to when this prop would not be passed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the prop optional because in the case of a pre-selected StackScript, the row is disabled and doesn't really need an onClick. I could revert this, but I felt wrong to have a "dummy" onSelect for a row that should never be selected. Happy do go either way on this change


if (getIsUDFMultiSelect(userDefinedField)) {
const options = userDefinedField
.manyof!.split(',')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably accomplish what you're looking for using a nifty feature of TypeScript: type predicates.

Comment on lines 9 to 18
return udfs.reduce(
(accum, udf) => {
if (getIsUDFOptional(udf)) {
return [[...accum[0]], [...accum[1], udf]];
} else {
return [[...accum[0], udf], [...accum[1]]];
}
},
[[], []]
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also might be more tersely expressed as two filters:

Suggested change
return udfs.reduce(
(accum, udf) => {
if (getIsUDFOptional(udf)) {
return [[...accum[0]], [...accum[1], udf]];
} else {
return [[...accum[0], udf], [...accum[1]]];
}
},
[[], []]
);
return [udfs.filter(getIsUDFRequired), udfs.filter(getIsUDFOptional)];

Copy link
Contributor

@hkhalil-akamai hkhalil-akamai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been brought up previously but worth mentioning: if we want to match prod behavior with autofilling the Linode label, we would also want to prefill the stackscript name.

@bnussman-akamai bnussman-akamai added Approved Multiple approvals and ready to merge! and removed Add'tl Approval Needed Waiting on another approval! labels Apr 24, 2024
Copy link
Contributor

@dwiley-akamai dwiley-akamai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review βœ…
Functionality of UDFs for various StackScripts βœ…

@bnussman-akamai bnussman-akamai merged commit 7bf7766 into linode:develop Apr 24, 2024
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved Multiple approvals and ready to merge! Linode Create Refactor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants