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

PKI role create view #17263

Merged
merged 20 commits into from
Sep 28, 2022
Merged

PKI role create view #17263

merged 20 commits into from
Sep 28, 2022

Conversation

Monkeychip
Copy link
Contributor

@Monkeychip Monkeychip commented Sep 21, 2022

This PR handles the PKI role creation for the default values only. The values hidden behind the Toggle options are more complicated and will have some design implications I wanted to keep in a separate PR (stay tuned).

Below is a video. The form correctly sends the default params on the network request, including the custom yield param (ttl and not_after).

roles_create.mov

Notes:

  • Moving the secretListHeader to the respective index routes will be the new pattern. The struggle with this, and why I originally had it on the parent "roles" route, is that the models for the roles vs the roles/index are different. The roles route is the secret-engine mount model and the roles/index route is the list of roles. To solve for this, I added a modelFor in the roles/index route. Please comment if you see any issues with this.
  • We can now have custom params on models that yield out whatever components you want. All you need to do is add editType: yield to the model and then yield out whatever you want between formFields.

ex:

// model
  @attr({
    label: 'Some label',
    editType: 'yield',
  })
  customComponent;

// some component
<FormField>
  <YieldedComponent or code />
</FormField>

TODO:

  • in the next PR handle the line-border styling at the bottom of the form.

image

@@ -20,9 +20,8 @@ export default class PkiIssuerEngineAdapter extends ApplicationAdapter {
return url;
}

async query(store, type, query) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

clean up (thank you Claire for pointing this out).

@@ -0,0 +1,52 @@
<div class="column is-narrow is-flex-center has-text-grey has-right-margin-s has-top-margin-negative-s">
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New component that I will use several times in PKI but unsure if it will be used in future designs.

newcomponent.mov

@@ -213,6 +213,9 @@
.has-top-margin-xxl {
margin-top: $spacing-xxl;
}
.has-top-margin-negative-s {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the new component, needed them to be closer to the label so they seemed "grouped" together.

@@ -6,8 +16,8 @@
</ToolbarActions>
</Toolbar>

{{#if (gt this.model.length 0)}}
{{#each this.model as |pkiRole|}}
{{#if (gt this.model.model.length 0)}}
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 suspect there's a better way to do this, but I couldn't figure out how shape the model without giving it a label. I tried spread attrs, and that didn't work.

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 change the key specified in RSVP.hash to something other than model

@@ -19,5 +23,9 @@ export default class RolesIndexRoute extends Route {
throw err;
}
});
return RSVP.hash({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Jordan, you might have some comments on this. I tried not using hash, but had issues with it returning a promise and not a class. I tried a then, and that didn't work.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since modelFor does not return a promise you could return an object in the then of query:

return this.store
  .query('pki/pki-role-engine', { backend: this.secretMountPath.currentPath })
  .then((model) => {
    return { model, parentModel: this.modelFor('roles') };
  })
  .catch((err) => {
    if (err.httpStatus === 404) {
      return [];
    } else {
      throw err;
    }
  });

I don't mind using RSVP.hash though too. It just might not be immediately clear to someone who is unfamiliar with the method.

I would avoid using let to assign those variables since they are not being reassigned if you decide to leave as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Awesome, thank you. I see now how to structure the then. I prefer this method only because it doesn't rely on an import, it's pure js.

@Monkeychip Monkeychip marked this pull request as ready for review September 26, 2022 17:10
@@ -13,7 +13,7 @@
<FormFieldLabel
for={{@attr.name}}
@label={{this.labelString}}
@helpText={{@attr.options.helpText}}
@helpText={{(if this.showHelpText @attr.options.helpText)}}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some issues on the formField component with the attribute showHelpText not being added to all form field types.

@@ -58,7 +58,10 @@ export default class FormFieldComponent extends Component {
return this.args.disabled || false;
}
get showHelpText() {
return this.args.showHelpText || true;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was my bad 🤦🏻‍♀️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And yeah, ideally we wouldn't return a true by default on a param, but this is an old pattern and if we choose to modify should be done in a cleanup ticket.

Copy link
Contributor

@hellobontempo hellobontempo left a comment

Choose a reason for hiding this comment

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

Made some comments! Nice work on this. PKI is 🤯

Great job on the new component! It'd be nice to have some tests written for it, maybe as a follow on after this PR? It'd be nice to have early on in PKI development to confirm that the values sent are as expected.

ui/app/models/pki/pki-role-engine.js Show resolved Hide resolved
ui/app/models/pki/pki-role-engine.js Outdated Show resolved Hide resolved
ui/app/models/pki/pki-role-engine.js Show resolved Hide resolved
ui/app/models/pki/pki-role-engine.js Outdated Show resolved Hide resolved
ui/app/models/pki/pki-role-engine.js Outdated Show resolved Hide resolved
ui/lib/core/addon/components/radio-select-ttl-or-string.js Outdated Show resolved Hide resolved
ui/lib/core/addon/components/radio-select-ttl-or-string.js Outdated Show resolved Hide resolved
<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
{{! ARG TODO write a test for namespace reminder }}
<NamespaceReminder @mode={{if @model.isNew "create" "update"}} @noun="PKI role" />
{{! This is the code from FormFieldGroupsLoop. However, because we need to pass the attr into the yielded component, I'm unable to use that component.}}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this comment is necessary as we've used this pattern of iterating over FormFields outside of the loop component

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 guess I just wanted to make a comment in case a future developer came along and noticed the code was nearly identical to the FormFieldGroupsLoop component and wondered why I didn't just use that component instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I recall discussing this and maybe I didn't have the full context at the time but slightly modifying FormFieldGroupsLoop will prevent having to copy the code out.

Since you added the yield to FormField you can yield that up from the FormFieldGroupsLoop as well. Update both instances to something like:

<FormField data-test-field={{true}} @attr={{attr}} @model={{@model}}>
  {{yield attr}}
</FormField

Then you can use the RadioSelectTtlOrString component which will render in the yield block of FormField:

<FormFieldGroupsLoop @model={{this.model}} @mode={{this.mode}} as |attr|>
  <RadioSelectTtlOrString @model={{@model}} @attr={{attr}} />
</FormFieldGroupsLoop>

It looks like you also need modelValidations and showHelpText passed in so you could add those as args to the groups loop as well.

In a situation where you might have multiple different attr types that yield you can just do a check in the block and display the proper component.

<FormFieldGroupsLoop @model={{this.model}} @mode={{this.mode}} as |attr|>
  {{#if attr.type "someType"}}
    <RadioSelectTtlOrString @model={{@model}} @attr={{attr}} />
  {{else if attr.type "someOtherType"}}
    <SomeOtherComponent />
  {{/if}}
</FormFieldGroupsLoop>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you! (reviewed over zoom), but passing through the attr in the yield the way you showed was awesome. A lot cleaner now.

ui/lib/pki/addon/components/pki-role-form.js Outdated Show resolved Hide resolved
ui/app/adapters/pki/pki-role-engine.js Show resolved Hide resolved
Comment on lines 13 to 24
createRecord(store, type, snapshot) {
let name = snapshot.attr('name');
let url = this._urlForRole(snapshot.record.backend, name);
return this.ajax(url, 'POST', { data: this.serialize(snapshot) }).then(() => {
return {
id: name,
name,
backend: snapshot.record.backend,
};
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Comparing this to the base method and wondering if you can use it and just update the response? Something like:

createRecord(store, type, snapshot){
  return super.createRecord(...arguments).then(() => {
    return { id: name, name, backend: snapshot.record.backend };
  });
}

Comment on lines +348 to +349
{{else if (eq @attr.options.editType "yield")}}
{{yield}}
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

Comment on lines 61 to 64
if (this.args.showHelpText === false) {
return false;
}
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ternary might be cleaner here:

return this.args.showHelpText === false ? false : true;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! Thank you. I knew there was a cleaner way.

Comment on lines 23 to 24
@tracked ttlTime = '';
@tracked notAfter = '';
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if these props need to be initialized to empty strings? Is there somewhere that expects the value to always be a string, as in maybe does a charAt or calls some other method on the prototype without first checking for existence?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. They are supposed to be strings, and ttl specifically says empty, though not_after doesn't seem to care. But the api seems to handle this. The only thing I think calling them out as strings does here, it let the next developer know these are not numbers but strings. But it's a toss up. I'll ahead and remove the default.
image

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't feel strongly about it but was curious. Since you are defining them as tracked properties it doesn't hurt to give them an initial value.

<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
{{! ARG TODO write a test for namespace reminder }}
<NamespaceReminder @mode={{if @model.isNew "create" "update"}} @noun="PKI role" />
{{! This is the code from FormFieldGroupsLoop. However, because we need to pass the attr into the yielded component, I'm unable to use that component.}}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I recall discussing this and maybe I didn't have the full context at the time but slightly modifying FormFieldGroupsLoop will prevent having to copy the code out.

Since you added the yield to FormField you can yield that up from the FormFieldGroupsLoop as well. Update both instances to something like:

<FormField data-test-field={{true}} @attr={{attr}} @model={{@model}}>
  {{yield attr}}
</FormField

Then you can use the RadioSelectTtlOrString component which will render in the yield block of FormField:

<FormFieldGroupsLoop @model={{this.model}} @mode={{this.mode}} as |attr|>
  <RadioSelectTtlOrString @model={{@model}} @attr={{attr}} />
</FormFieldGroupsLoop>

It looks like you also need modelValidations and showHelpText passed in so you could add those as args to the groups loop as well.

In a situation where you might have multiple different attr types that yield you can just do a check in the block and display the proper component.

<FormFieldGroupsLoop @model={{this.model}} @mode={{this.mode}} as |attr|>
  {{#if attr.type "someType"}}
    <RadioSelectTtlOrString @model={{@model}} @attr={{attr}} />
  {{else if attr.type "someOtherType"}}
    <SomeOtherComponent />
  {{/if}}
</FormFieldGroupsLoop>

@@ -19,5 +23,9 @@ export default class RolesIndexRoute extends Route {
throw err;
}
});
return RSVP.hash({
Copy link
Contributor

Choose a reason for hiding this comment

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

Since modelFor does not return a promise you could return an object in the then of query:

return this.store
  .query('pki/pki-role-engine', { backend: this.secretMountPath.currentPath })
  .then((model) => {
    return { model, parentModel: this.modelFor('roles') };
  })
  .catch((err) => {
    if (err.httpStatus === 404) {
      return [];
    } else {
      throw err;
    }
  });

I don't mind using RSVP.hash though too. It just might not be immediately clear to someone who is unfamiliar with the method.

I would avoid using let to assign those variables since they are not being reassigned if you decide to leave as is.

@@ -6,8 +16,8 @@
</ToolbarActions>
</Toolbar>

{{#if (gt this.model.length 0)}}
{{#each this.model as |pkiRole|}}
{{#if (gt this.model.model.length 0)}}
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 change the key specified in RSVP.hash to something other than model

@Monkeychip Monkeychip enabled auto-merge (squash) September 28, 2022 21:58
@Monkeychip Monkeychip merged commit 6771e56 into main Sep 28, 2022
@Monkeychip Monkeychip deleted the ui/VAULT-6521/pki-role-create branch September 28, 2022 22:59
jayant07-yb pushed a commit to jayant07-yb/hashicorp-vault-integrations that referenced this pull request Mar 15, 2023
* dynamically render the secretlistheader in the parent route.

* start getting form setup even without openAPi working

* add in create and cancel

* making openAPI work

* add default openAPI params

* wip for new component with two radio options a ttl and input

* handle createRecord on pki-roles-form

* remove tooltips and cleanup

* move formfieldgroupsloop back to non addon

* cleanup

* move secretListHeader

* broadcast from radioSelectTtlOrString to parent

* cleanup

* cleanup from pr comments

* more cleanup

* addressing Jordans comments

* use formFieldGroupsLoop move into addon.

* cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants