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

[Enterprise Search] Added Logic for the Credentials View #77626

Merged
merged 35 commits into from
Sep 28, 2020

Conversation

JasonStoltz
Copy link
Member

@JasonStoltz JasonStoltz commented Sep 16, 2020

Summary

This PR adds the logic and API endpoints necessary for the Credentials View.

The logic file was ported largely as is from https://github.com/elastic/ent-search/blob/master/app/javascript/app_search/Credentials/CredentialsLogic.ts

I've added a number of comments inline explaining some of the thought behind the changes I made.

The main things that I added were:

  • credentials_logic.ts
  • new routes in /routes/app_search/credentials.ts

Everything else is supporting code.

Checklist

Delete any items that are not applicable to this PR.

For maintainers

});
});

it('should not change any other values', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

I added one of these tests to each action. My reasoning is this:

  1. As I was testing, it helped ensure for me that I had covered all of the necessary values with tests. It will also serve as a reminder to add test coverage as we make updates as well.
  2. Avoid unintentional values changes in actions

Copy link
Member

@cee-chen cee-chen Sep 18, 2020

Choose a reason for hiding this comment

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

Follow up to my comment here: #77626 (comment)

I strongly think we should not have extra tests specifically for should not change any other values. It's hard to read/understand, doesn't add anything significantly to line coverage, and bloats the file. That said, it's fully possible to check for this in your existing active tests. Simply do this:

// example assertion for "should remove an engine from the active api token's engine list" test

        expect(CredentialsLogic.values).toEqual({
          ...DEFAULT_VALUES,
          activeApiToken: {
            ...newToken,
            engines: ['anotherEngine'],
          },
        });

Instead of:

expect(CredentialsLogic.values.activeApiToken).toEqual({
...newToken,
engines: ['anotherEngine'],
});

... and bam, you've proved other values haven't changed at the same time you have asserted your expected values do change, in a fixed 3 lines instead of 9+ 🤷 (also cuts down on test times / time spent mounting extra tests, etc.)

FWIW, we also don't have to check the full expect(SomeLogic.values) for every action/value block, just the first one. Follow up edge case/conditional branch tests can simply check SomeLogic.values.someValue after that since the first test asserts unchanged values for us.

Copy link
Member Author

Choose a reason for hiding this comment

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

It gets a little weird when you have a bunch of values that change when you fire an action though, because then you can't just use DEFAULT_VALUES.

        expect(CredentialsLogic.values).toEqual({
          ...DEFAULT_VALUES,
          someOtherThing: 'foo',
          anotherValueThatChanged: 'bar',
          activeApiToken: {
            ...newToken,
            engines: ['anotherEngine'],
          },
        });

You'd have to put in assertions for anotherValueThatChanged and someOtherThing as well, as seen above. At which point it's like, well I have a separate test for anotherValueThatChanged and someOtherThing below, why do I need both if it's being asserted here?

One way to avoid that is to use expect.any to make it clear that that is not what's under test currently, and we don't care what is provided here.

        expect(CredentialsLogic.values).toEqual({
          ...DEFAULT_VALUES,
          someOtherThing: expect.any(string),
          anotherValueThatChanged: expect.any(string),
          activeApiToken: {
            ...newToken,
            engines: ['anotherEngine'],
          },
        });

Which is what I arrived at, and I ultimately thought it would be clearer to just move that into a separate test so it's explicit what is being tested. I may have taken it a step too far though.

I don't have a strong opinion here. I think it IS important to assert that the other values do not change somehow though. The entire updated state (the values) are returned from every action and hence part of the public interface of that action and should be asserted. If you prefer the inline assertion on the first test, I find that acceptable.

I wonder if something like this would work....

const values = {
   ...DEFAULT_VALUES,
   someOtherThing: expect.any(string),
   anotherValueThatChanged: expect.any(string),
   activeApiToken: expect.any(object)
}

....          
        expect(CredentialsLogic.values).toEqual({
          ...values,
          activeApiToken: {
            ...newToken,
            engines: ['anotherEngine'],
          },
        });

...
        expect(CredentialsLogic.values).toEqual({
          ...values,
          anotherValueThatChanged: 'foo'
        });

...

        expect(CredentialsLogic.values).toEqual({
          ...values,
          someOtherThing: 'foo'
        });

Thoughts?

Copy link
Member

Choose a reason for hiding this comment

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

You totally beat me to this lol, I spent a few more hrs last night reading through the test file and definitely get now why my proposed solution doesn't work well for all the test suites (esp. the ones dealing w/ a bunch of deeply nested obj children).

You'd have to put in assertions for anotherValueThatChanged and someOtherThing as well, as seen above. At which point it's like, well I have a separate test for anotherValueThatChanged and someOtherThing below, why do I need both if it's being asserted here?

Definitely a good point, and also potentially a indication that (for some, definitely not all) tests, it may be worth considering whether it would be easier to just group all the changes values in a combined single test/assertion, rather than splitting them out 🤔 That being said, I totally get why not though for a lot of the more complex tests - also this is not me requesting a change to the current PR, just potentially food for thought for future logic tests.

All that being said, I also like the proposed compromise a lot! Would love to give it a shot - feel free to timebox though, if it starts taking too long to refactor or creates unexpected issues I'd also be fine leaving the current should not change tests as-is.

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed the should not change tests and implemented the solution proposed above: 6347c61

});

describe('activeApiToken', () => {
// TODO It is weird that methods like this update activeApiToken but not activeApiTokenIsExisting...
Copy link
Member Author

Choose a reason for hiding this comment

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

I found some oddities around this logic file that I just noted in TODOs for now. This is a good example. Everywhere else in the code, when we change the activeApiToken, we also update activeApiTokenIsExisting. They are manually kept in sync. Here, we do not update activeApiTokenIsExisting.

I think the ultimate solution is to change activeApiTokenIsExisting to be a selector, so we don't need to try to keep it in sync everywhere.

Copy link
Member

Choose a reason for hiding this comment

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

I think this is a super good example of unit tests catching a design flaw in code! I'd be +++ for fixing this behavior - this does indeed sound like the exact use-case for a selector.

Also while we're here, would we have any objections to renaming activeApiTokenIsExisting? It's fairly awkward-sounding - maybe doesActiveApiTokenExist or even just hasActiveApiToken instead?

Copy link
Member

@cee-chen cee-chen Sep 18, 2020

Choose a reason for hiding this comment

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

BTW, would activeTokenRawName be another good use-case to convert to a selector?

EDIT: After starting at this further it actually kinda feels to me like activeTokenRawName should be the reducer and activeTokenName should be the selector, since it undergoes a format. So something like activeTokenName is the raw name and activeTokenFormattedName is the final name 😛 Probably too annoying/large a change at this point tho :neckbeard: so feel free to disregard unless you really like that idea

Copy link
Member Author

Choose a reason for hiding this comment

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

Since we know this works as is, and this PR is already big, I'd like to address this is a follow-up PR.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds great! No rush at all!

});

// TODO Not sure if this is a good behavior or not
it('if for some reason the existing token is not found, it adds a new token...', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

This shouldn't ever happen theoretically, because this gets triggered after a user updates and saves a token. If this happens, something went wrong. I am just documenting the behavior as written, though.

Copy link
Member

Choose a reason for hiding this comment

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

Good point - I can't quite figure out if we should change this or not either 🤔 I'm fine kicking the can down on the road on this one I think / marking as tech debt for later

Copy link
Member Author

Choose a reason for hiding this comment

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

Since we know this works as is, and this PR is already big, I'd like to address this is a follow-up PR.

);
});

// TODO: This fails, is this an issue? Instead of reseting back to the default value, it sets it to the previously
Copy link
Member Author

Choose a reason for hiding this comment

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

Another oddity I found.

Copy link
Member

Choose a reason for hiding this comment

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

Would be ++ for fixing. After some thought (maybe it's just very late) not totally sure how activeApiTokenRawName works as a selector tho - shouldn't activeApiTokenName be the selector instead? 🤷

Copy link
Member Author

Choose a reason for hiding this comment

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

Since we know this works as is, and this PR is already big, I'd like to address this is a follow-up PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh yeah, now that I think about it...

When a user enters a token name and then saves it, the token name is formatted before it's persisted to activeApiToken.name.

activeApiTokenRawName persists the unformatted name that the user had entered. I'm guessing this is used to back the form field, so is transient and only required until the user presses save.

@JasonStoltz JasonStoltz added Feature:Plugins release_note:skip Skip the PR/issue when compiling release notes v7.10.0 labels Sep 16, 2020
@JasonStoltz JasonStoltz marked this pull request as ready for review September 16, 2020 18:31
@JasonStoltz JasonStoltz requested review from a team as code owners September 16, 2020 18:31
Copy link
Member

@cee-chen cee-chen left a comment

Choose a reason for hiding this comment

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

Ran tests on the credentials file and noticed we had a just few more uncovered lines to go. I know it's possibly unhealthy to chase those sweet sweet green 100%s with too much fervor, but in this case I think it's not too much extra effort to cover the lines we need (and in one case, reveals unnecessary code).

Let me know what you think!

@JasonStoltz
Copy link
Member Author

Ran tests on the credentials file and noticed we had a just few more uncovered lines to go.

Ah yes, I meant to run the coverage report but I ended up forgetting. I need to get in the habit of checking. I definitely feel the need to scratch that itch as well.

@JasonStoltz
Copy link
Member Author

@constancecchen Ready for a second look

@cee-chen
Copy link
Member

Changes look great!! Just two super minor comments left from me 🤞

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

Copy link
Member

@cee-chen cee-chen left a comment

Choose a reason for hiding this comment

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

🎉 Wahoo!!! This looks amazing. Thanks so much as always for your patience with my review process Jason!

Comment on lines +18 to +21
interface ITokenReadWrite {
name: 'read' | 'write';
checked: boolean;
}
Copy link
Member

Choose a reason for hiding this comment

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

Totally forgot to mention this too, but this one's super optional so feel free to ignore or do it another PR if you like it :) This might be a good candidate for moving to credentials/types, but it's also used right here in this file, so whatever works for you!

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call. I will put this one up in my next PR.

@JasonStoltz
Copy link
Member Author

@constancecchen Thanks for the review!

@JasonStoltz JasonStoltz merged commit 378b8c8 into elastic:master Sep 28, 2020
gmmorris added a commit to gmmorris/kibana that referenced this pull request Sep 28, 2020
* master: (226 commits)
  [Enterprise Search] Added Logic for the Credentials View (elastic#77626)
  [CSM] Js errors (elastic#77919)
  Add the @kbn/apm-config-loader package (elastic#77855)
  [Security Solution] Refactor useSelector (elastic#75297)
  Implement tagcloud renderer (elastic#77910)
  [APM] Alerting: Add global option to create all alert types (elastic#78151)
  [Ingest pipelines] Upload indexed document to test a pipeline (elastic#77939)
  TypeScript cleanup in visualizations plugin (elastic#78428)
  Lazy load metric & mardown visualizations (elastic#78391)
  [Detections][EQL] EQL rule execution in detection engine (elastic#77419)
  Update tutorial-full-experience.asciidoc (elastic#75836)
  Update tutorial-define-index.asciidoc (elastic#75754)
  Add support for runtime field types to mappings editor. (elastic#77420)
  [Monitoring] Usage collection (elastic#75878)
  [Docs][Actions] Add docs for Jira and IBM Resilient (elastic#78316)
  [Security Solution][Resolver] Update @timestamp formatting (elastic#78166)
  [Security Solution] Fix app layout (elastic#76668)
  [Security Solution][Resolver] 2 new functions to DAL (elastic#78477)
  Adds new elasticsearch client to telemetry plugin (elastic#78046)
  skip flaky suite (elastic#78512) (elastic#78511) (elastic#78510) (elastic#78509) (elastic#78508) (elastic#78507) (elastic#78506) (elastic#78505) (elastic#78504) (elastic#78503) (elastic#78502) (elastic#78501) (elastic#78500)
  ...
@kibanamachine kibanamachine added the backport missing Added to PRs automatically when the are determined to be missing a backport. label Sep 30, 2020
@kibanamachine
Copy link
Contributor

Friendly reminder: Looks like this PR hasn’t been backported yet.
To create backports run node scripts/backport --pr 77626 or prevent reminders by adding the backport:skip label.

JasonStoltz added a commit to JasonStoltz/kibana that referenced this pull request Sep 30, 2020
@kibanamachine kibanamachine removed the backport missing Added to PRs automatically when the are determined to be missing a backport. label Sep 30, 2020
@JasonStoltz JasonStoltz deleted the credentials-logic branch April 6, 2021 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Plugins release_note:skip Skip the PR/issue when compiling release notes v7.10.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants