-
Notifications
You must be signed in to change notification settings - Fork 8.1k
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
Improve logic for dashboard duplication title computation #185056
Improve logic for dashboard duplication title computation #185056
Conversation
Pinging @elastic/kibana-presentation (Team:Presentation) |
e7270a6
to
fe490a0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great so far! Especially nice to see the functional and jest test additions.
Left a few code quality nits and some other comments.
src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx
Outdated
Show resolved
Hide resolved
...public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.test.ts
Outdated
Show resolved
Hide resolved
...oard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.ts
Outdated
Show resolved
Hide resolved
|
||
if (isMatchedResultInvalid) { | ||
({ hits: searchMatchPageHits, pagination: searchMatchPaginationObject } = | ||
await contentManagement.client.search< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why exactly do we need to run the search again? Or use a cursor? It seems to me this could work quite well with one search of ~20 or so Dashboards with no cursor where we simply loop through the results to find the highest number and increment that.
If we miss and accidentally return a number that isn't unique, nothing really breaks, the user just has to increment it themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before switching the previous implementation, I'd used a cursor so we are able to fetch the last page of the search so we can pick the last created dashboard without having to loop multiple times. I opted for this approach because if we have a situation where the duplicated dashboard count exceeds search limit (right now it's 10) the returned page would not give us a way to determine the highest number without performing multiple loops.
I have switched the implementation though now so we are able to sort and simply pick the the last incremented dashboard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's okay. If the user has more than 10 Dashboards with the same basic title structure, it's completely okay if this doesn't recommend a unique title in ~5% or less of cases. Remember that the behaviour we're replacing simply told the user their title wasn't unique and let them figure out how to make it unique.
...oard/public/services/dashboard_content_management/lib/check_for_duplicate_dashboard_title.ts
Outdated
Show resolved
Hide resolved
75676e8
to
6b7bb62
Compare
export const dashboardSearchOptionsSchema = dashboardSearchOptionsSchemaV1.extends( | ||
{ | ||
sort: schema.maybe( | ||
schema.object({ | ||
sortField: searchOptionsSchemas.sortField, | ||
sortOrder: searchOptionsSchemas.sortOrder, | ||
}) | ||
), | ||
}, | ||
{ unknowns: 'ignore' } | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added this search option in V2, as it's a backward compatible change and also opt-in I'm not sure if this warrants a V3
const [baseTitle] = extractTitleAndCount(newTitle); | ||
|
||
const { hits: recentlyCreatedDashboards } = await findDashboards.search({ | ||
size: 1, | ||
search: baseTitle, | ||
options: { | ||
onlyTitle: true, | ||
sort: { | ||
sortField: 'created_at', | ||
sortOrder: 'desc', | ||
}, | ||
}, | ||
}); | ||
|
||
const [, mostRecentDuplicationId] = extractTitleAndCount( | ||
recentlyCreatedDashboards[0].attributes.title | ||
); | ||
|
||
let copyCount = mostRecentDuplicationId + 1; | ||
|
||
newTitle = `${baseTitle} (${copyCount})`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Request the last created dashboard, and use it's duplication Id to create the proposed duplicate title.
a7444cd
to
e5b8ce2
Compare
e5b8ce2
to
c5ba26c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this PR again, it seems like we're adding a lot of difficult to maintain code for very dubious user benefit.
All we're doing here is recommending a new title that might be unique. The consequences of accidentally returning a title that isn't unique are very low stakes. In that case the user just needs to write a unique title themselves. That tells me that this code does not need to work perfectly and a "good enough" approach is warranted.
I'd recommend removing almost all of the complexity here - especially the changes to the schema and content management, and building this functionality to cover 90% of the ways it will be used (duplicating a Dashboard once or twice).
@ThomThomson I'm taking over this PR while Eyo's out. I've removed all of the content management changes and simplified the logic for the duplicate title check. Only one dashboard search request is fired when the save modal renders to generate a best attempt at a unique title. It reliably finds a unique title until you have 20+ copies of the same dashboard with the same base title, and only struggles to find a unique title suggestion if you're duplicating one of the older duplicates with a lower count. It should still consistently suggest a unique title when duplicating one of the newer duplicate dashboards with a higher count. This should sufficiently cover most of the scenarios a user will run into. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes LGTM! Tested locally in chrome, and it's working great. Left a few nits
@@ -30,6 +35,7 @@ export async function checkForDuplicateDashboardTitle( | |||
lastSavedTitle, | |||
onTitleDuplicate, | |||
isTitleDuplicateConfirmed, | |||
searchLimit = 20, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this need to be an argument? It seems like it can just be hardcoded in the function more simply.
const { hits } = await contentManagement.client.search< | ||
DashboardCrudTypes['SearchIn'], | ||
DashboardCrudTypes['SearchOut'] | ||
>({ | ||
contentTypeId: DASHBOARD_CONTENT_ID, | ||
query: { | ||
text: title ? `${title}*` : undefined, | ||
limit: 10, | ||
text: title ? `${baseDashboardName}*` : undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of falling back to undefined here, can we just cancel the whole operation if the title
is undefined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't be undefined, but it's possible to receive an empty string. We can certainly return early and skip the whole check.
limit: 10, | ||
text: title ? `${baseDashboardName}*` : undefined, | ||
limit: searchLimit, | ||
cursor: String(duplicationId ? Math.ceil(duplicationId / searchLimit) : 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a cursor still?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily. Setting the cursor is only useful if there are more than the 20 duplicate dashboards, but even without this, we'll still come up with a good enough suggestion.
@@ -49,5 +49,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { | |||
await PageObjects.dashboard.gotoDashboardLandingPage(); | |||
await listingTable.searchAndExpectItemsCount('dashboard', `${dashboardName} (2)`, 1); | |||
}); | |||
|
|||
it('Clone should always increment from the last duplicated dashboard with a unique title', async function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding a functional test!
💛 Build succeeded, but was flaky
Failed CI StepsMetrics [docs]Async chunks
History
To update your PR or re-run it, just comment with: cc @eokoneyo |
…5056) ## Summary follow up to elastic#184777 (comment) Previously, if a user has cloned a dashboard N times, in the worst case that the next duplication attempt is initiated from the initial dashboard N network calls would be required to find a unique title for the next uniquely titled clone. This update short circuits that process by querying for the last created dashboard, getting it's duplicationId, increment it and proposing it as the next dashboard duplicate title, with this approach there's still a chance we might suggest a dashboard title that's been claimed if the user modifies the dashboard title to a higher duplication Id after creation, especially that we are querying for the last created and this is situation we can't correct for, the resolution for the appropriate title in that case would be on the user. ## How to test - Create couple of duplicated dashboards, the titles should follow the normal increment progression ie. n+1 - Create some that skip the expected pattern by any increment of choice, on attempting to duplicate this dashboard the suggested title should be previous + 1 - Choose to duplicate any previously created dashboard the suggested title should be the increment of the last duplicated dashboard ### Checklist <!-- Delete any items that are not applicable to this PR. --> <!-- - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials --> - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios <!-- - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --> --------- Co-authored-by: Catherine Liu <catherine.liu@elastic.co>
Summary
follow up to #184777 (comment)
Previously, if a user has cloned a dashboard N times, in the worst case that the next duplication attempt is initiated from the initial dashboard N network calls would be required to find a unique title for the next uniquely titled clone. This update short circuits that process by querying for the last created dashboard, getting it's duplicationId, increment it and proposing it as the next dashboard duplicate title, with this approach there's still a chance we might suggest a dashboard title that's been claimed if the user modifies the dashboard title to a higher duplication Id after creation, especially that we are querying for the last created and this is situation we can't correct for, the resolution for the appropriate title in that case would be on the user.
How to test
Checklist