[ResponseOps][Reporting] Add support for deletion of export schedules#238197
[ResponseOps][Reporting] Add support for deletion of export schedules#238197umbopepato merged 21 commits intoelastic:mainfrom
Conversation
| onCancel, | ||
| onConfirm, | ||
| }) => { | ||
| const ReportDestructiveActionConfirmationModalComponent: React.FC< |
There was a problem hiding this comment.
Made this generic so we can use it both for disable and delete
| ids.map((id) => ({ id, type: SCHEDULED_REPORT_SAVED_OBJECT_TYPE })) | ||
| ); | ||
|
|
||
| const scheduledReportSavedObjectsToDelete: Array<SavedObject<ScheduledReportType>> = []; |
There was a problem hiding this comment.
Existence and authorization check should be the same as when disabling, working on a possible abstraction to avoid duplicated code fragments, even though the two loops have small differences that make it difficult (or inefficient) to centralize everything...
There was a problem hiding this comment.
I tried to extract it to a checkScheduledReportBulkActionAuthorization function, but it is quite impractical since the two endpoints perform slightly different checks in the loop. The common structure is:
For each SO read
- if there was a read error, add it to bulkErrors to report
- otherwise check ownership
- if user is not manager and not author of SO, add error to bulkErrors
- otherwise add SO id to list of "authorized" ones to operate on
- audit log event
In the disable case though there is an early return condition on the enabled field, which would require a second iteration pass or some sort of callback passed from the outside.
Since @adcoelho started refactoring the whole factory into a class, I'd address this with the new structure: perhaps we can find a better way to extract this.
| }; | ||
| } | ||
|
|
||
| const resultFromRemoveTasks = await taskManager.bulkRemove(taskIdsToRemove); |
There was a problem hiding this comment.
Removing the tasks instead of disabling them since the schedule won't be recoverable
x-pack/platform/plugins/private/reporting/server/routes/common/audit_events.ts
Outdated
Show resolved
Hide resolved
|
Pinging @elastic/response-ops (Team:ResponseOps) |
| useGetScheduledList: jest.fn(), | ||
| })); | ||
| jest.mock('../hooks/use_bulk_disable'); | ||
| jest.mock('../apis/get_scheduled_reports_list'); |
There was a problem hiding this comment.
While I was at it, I switched from mocking the entire react-query hooks to just mocking the underlying fetch functions so we can mock with full type safety and have a more realistic behavior with RQ
...tform/plugins/private/reporting/public/management/components/report_schedules_table.test.tsx
Show resolved
Hide resolved
| @@ -400,5 +413,135 @@ export function scheduledQueryFactory(reportingCore: ReportingCore): ScheduledQu | |||
| }); | |||
| } | |||
| }, | |||
|
|
|||
| async bulkDelete(logger, req, res, ids, user) { | |||
There was a problem hiding this comment.
I have a couple of nits :D
- If the
idsare part of thereq, it feels kinda weird to pass both. - Passing
resalso doesn't make sense to me(I know other methods in this file do it too 😬 ). Why notthrow Boom.whatever?
I think fundamentally it would make sense to isolate the route logic from the client logic.
If there is time, we(and I'm saying we because I also have a task where I can help 😅), we could refactor this into a class.
- We would eliminate a lot of duplication. It would make sense to have a constructor where
this.savedObjectsClient = await reportingCore.getScopedSoClient(req);
this.esClient = await reportingCore.getEsClient();
this.auditLogger = await reportingCore.getAuditLogger(req);
this.taskManager = await reportingCore.getTaskManager();
this.canManageReporting = await reportingCore.canManageReportingForSpace(req);
- We could move part of the logic of these big functions into private methods that are more readable. The calls to
auditLogger.log(scheduledReportAuditEvent({are all pretty similar, e.g.
There was a problem hiding this comment.
I absolutely agree, it would be much simpler and more organized!
| ); | ||
|
|
||
| const scheduledReportSavedObjectsToDelete: Array<SavedObject<ScheduledReportType>> = []; | ||
| for (const so of bulkGetResult.saved_objects) { |
There was a problem hiding this comment.
Could we maybe add a filter to bulkGet to only return the ones created by this user when !canManageReporting?
We could then check the difference in IDs to build the error array and avoid this loop.
It would avoid this loop
There was a problem hiding this comment.
I repeated the same structure as the bulkDisable method, I think @ymao1's intention was to loop over all the requested SOs (regardless of authorship) to accumulate errors for the unauthorized ones? Filtering would be fine for me, but I wouldn't want the new endpoint to have a different behavior than the other ones, wdyt?
x-pack/platform/plugins/private/reporting/server/routes/internal/management/scheduled.ts
Show resolved
Hide resolved
Flaky Test Runner Stats🎉 All tests passed! - kibana-flaky-test-suite-runner#9524[✅] x-pack/platform/test/reporting_api_integration/reporting_and_security.config.ts: 50/50 tests passed. |
## Summary When I started to work on ["editing scheduled reports"](elastic/kibana-team#2083), I looked around in the reporting plugin for something like a `scheduledReportClient` so I could create an `update` method. This client does not exist. I was surprised that there isn't a single "client" to manage `SCHEDULED_REPORT_SAVED_OBJECT_TYPE` SOs. Instead, that is done in a couple of different places. - `ScheduleRequestHandler` has the creation logic and schedules the report generation tasks. - `ScheduledQueryFactory` handles `list` and `bulkDisable`. Moreover, these were found in `routes/common` but weren't shared by any routes. They are each imported in a single place. I think it makes sense to consolidate this client/service logic, and move it **outside** of the `routes` folder. As a first step, I am just moving some code around. The consolidation of `ScheduleRequestHandler` with `ScheduledQueryFactory` into a `ScheduledRequestClient` will be done later. For now, this PR does the following: - I will wait for @umbopepato 's PR to be merged first - #238197. - Just moving code around. Nothing changed(yet), and no tests should fail. - `ScheduledQueryFactory` now lives in `reporting/server/services/scheduled_reports/`. - the file where `ScheduledQueryFactory` had too much logic, it was split into: - `constants.ts` - `transforms.ts` - `transforms.test.ts` - `audit_events.ts` was moved into `reporting/server/services/audit_events/`.
x-pack/platform/plugins/private/reporting/server/routes/internal/management/scheduled.ts
Show resolved
Hide resolved
ymao1
left a comment
There was a problem hiding this comment.
LGTM. Verified works as described.
js-jankisalvi
left a comment
There was a problem hiding this comment.
Verified locally works as expected 🎉 Added some queries.
| onCancel, | ||
| onConfirm, | ||
| }) => { | ||
| const ReportDestructiveActionConfirmationModalComponent: React.FC< |
There was a problem hiding this comment.
nit: could you please add a test file for it?
| // scheduled reports for all users in this space | ||
| const canManageReporting = await reportingCore.canManageReportingForSpace(req); | ||
|
|
||
| const bulkGetResult = await savedObjectsClient.bulkGet<ScheduledReportType>( |
There was a problem hiding this comment.
nit: Should we do this bulk operation in batches to handle large number of report schedules in future like we do for rules?
There was a problem hiding this comment.
Thanks for the heads up! To keep things simple I repeated the same flow as in the disable endpoint that Ying implemented. It would be a nice way to future-proof these actions in case we actually want to start doing bulk operations, but I guess it would probably make sense to do it for the other ops as well (disable and update), so perhaps a dedicated PR is the best thing to do?
@cnasikas let us know what you think about this
There was a problem hiding this comment.
Even if the other methods/routes do not impose any limit or do not take into account a large number of reports, we should do it in this PR. I would suggest putting a limit of up to 50 IDs on the schema of the route. At the moment, it is set to 1K, which is too much, I think. Also, we can open a tech debt issue with Janki's suggestion. We should handle this.
...k/platform/plugins/private/reporting/public/management/apis/bulk_delete_scheduled_reports.ts
Show resolved
Hide resolved
x-pack/platform/plugins/private/reporting/server/routes/internal/management/scheduled.ts
Outdated
Show resolved
Hide resolved
| // scheduled reports for all users in this space | ||
| const canManageReporting = await reportingCore.canManageReportingForSpace(req); | ||
|
|
||
| const bulkGetResult = await savedObjectsClient.bulkGet<ScheduledReportType>( |
There was a problem hiding this comment.
Even if the other methods/routes do not impose any limit or do not take into account a large number of reports, we should do it in this PR. I would suggest putting a limit of up to 50 IDs on the schema of the route. At the moment, it is set to 1K, which is too much, I think. Also, we can open a tech debt issue with Janki's suggestion. We should handle this.
| } | ||
| }, | ||
|
|
||
| async bulkDelete(logger, req, res, ids, user) { |
There was a problem hiding this comment.
I think the function is too large, doing so many things. Could you please refactor a bit to make it easier to read? We can move a lot of the functionality to functions (inside the same folder) and reduce the code to perform a couple of steps. Wdyt?
## Summary When I started to work on ["editing scheduled reports"](elastic/kibana-team#2083), I looked around in the reporting plugin for something like a `scheduledReportClient` so I could create an `update` method. This client does not exist. I was surprised that there isn't a single "client" to manage `SCHEDULED_REPORT_SAVED_OBJECT_TYPE` SOs. Instead, that is done in a couple of different places. - `ScheduleRequestHandler` has the creation logic and schedules the report generation tasks. - `ScheduledQueryFactory` handles `list` and `bulkDisable`. Moreover, these were found in `routes/common` but weren't shared by any routes. They are each imported in a single place. I think it makes sense to consolidate this client/service logic, and move it **outside** of the `routes` folder. As a first step, I am just moving some code around. The consolidation of `ScheduleRequestHandler` with `ScheduledQueryFactory` into a `ScheduledRequestClient` will be done later. For now, this PR does the following: - I will wait for @umbopepato 's PR to be merged first - elastic#238197. - Just moving code around. Nothing changed(yet), and no tests should fail. - `ScheduledQueryFactory` now lives in `reporting/server/services/scheduled_reports/`. - the file where `ScheduledQueryFactory` had too much logic, it was split into: - `constants.ts` - `transforms.ts` - `transforms.test.ts` - `audit_events.ts` was moved into `reporting/server/services/audit_events/`.
## Summary When I started to work on ["editing scheduled reports"](elastic/kibana-team#2083), I looked around in the reporting plugin for something like a `scheduledReportClient` so I could create an `update` method. This client does not exist. I was surprised that there isn't a single "client" to manage `SCHEDULED_REPORT_SAVED_OBJECT_TYPE` SOs. Instead, that is done in a couple of different places. - `ScheduleRequestHandler` has the creation logic and schedules the report generation tasks. - `ScheduledQueryFactory` handles `list` and `bulkDisable`. Moreover, these were found in `routes/common` but weren't shared by any routes. They are each imported in a single place. I think it makes sense to consolidate this client/service logic, and move it **outside** of the `routes` folder. As a first step, I am just moving some code around. The consolidation of `ScheduleRequestHandler` with `ScheduledQueryFactory` into a `ScheduledRequestClient` will be done later. For now, this PR does the following: - I will wait for @umbopepato 's PR to be merged first - elastic#238197. - Just moving code around. Nothing changed(yet), and no tests should fail. - `ScheduledQueryFactory` now lives in `reporting/server/services/scheduled_reports/`. - the file where `ScheduledQueryFactory` had too much logic, it was split into: - `constants.ts` - `transforms.ts` - `transforms.test.ts` - `audit_events.ts` was moved into `reporting/server/services/audit_events/`.
cnasikas
left a comment
There was a problem hiding this comment.
Thanks for addressing my comments! LGTM!
...orm/plugins/private/reporting/server/services/scheduled_reports/scheduled_reports_service.ts
Outdated
Show resolved
Hide resolved
...orm/plugins/private/reporting/server/services/scheduled_reports/scheduled_reports_service.ts
Outdated
Show resolved
Hide resolved
...orm/plugins/private/reporting/server/services/scheduled_reports/scheduled_reports_service.ts
Outdated
Show resolved
Hide resolved
💚 Build Succeeded
Metrics [docs]Module Count
Public APIs missing comments
Async chunks
Page load bundle
History
|
## Summary When I started to work on ["editing scheduled reports"](elastic/kibana-team#2083), I looked around in the reporting plugin for something like a `scheduledReportClient` so I could create an `update` method. This client does not exist. I was surprised that there isn't a single "client" to manage `SCHEDULED_REPORT_SAVED_OBJECT_TYPE` SOs. Instead, that is done in a couple of different places. - `ScheduleRequestHandler` has the creation logic and schedules the report generation tasks. - `ScheduledQueryFactory` handles `list` and `bulkDisable`. Moreover, these were found in `routes/common` but weren't shared by any routes. They are each imported in a single place. I think it makes sense to consolidate this client/service logic, and move it **outside** of the `routes` folder. As a first step, I am just moving some code around. The consolidation of `ScheduleRequestHandler` with `ScheduledQueryFactory` into a `ScheduledRequestClient` will be done later. For now, this PR does the following: - I will wait for @umbopepato 's PR to be merged first - elastic#238197. - Just moving code around. Nothing changed(yet), and no tests should fail. - `ScheduledQueryFactory` now lives in `reporting/server/services/scheduled_reports/`. - the file where `ScheduledQueryFactory` had too much logic, it was split into: - `constants.ts` - `transforms.ts` - `transforms.test.ts` - `audit_events.ts` was moved into `reporting/server/services/audit_events/`.
…elastic#238197) ## 📄 Summary - Adds backend support for deleting export schedules - Adds client-side delete schedules fetcher and react-query hook - Adds delete UI action to `Stack Management` > `Reporting` > `Schedules` table rows <details> <summary> ## 🧪 Verification steps </summary> ### ✅ Happy Path 1. If you don't have any Dashboard or Discover session to export, install a sample data set from the Kibana home page 2. Open any dashboard or Discover session 3. Click on the ⬇️ (Export) button in the app bar 4. Click on Schedule export 5. Choose a close schedule time (minutes after) 6. Repeat from point 2 more times to get more schedules 7. Navigate to `Stack Management` > `Reporting` > `Schedules` 8. Click on the actions menu button (•••) 9. Click `Delete schedule` 10. Verify that the schedule item disappeared from the table 11. Wait for the execution time of the schedules and verify that after that time, no report was created in the `Reports` tab (refresh if necessary) </details> <details> <summary> ## 📷 Screenshots </summary> <img width="1015" height="392" alt="image" src="https://github.com/user-attachments/assets/36296a25-a6c9-4832-b190-54b35d14676d" /> <img width="1100" height="583" alt="image" src="https://github.com/user-attachments/assets/3e0ce676-6423-4a1e-b8aa-6c5680ec1523" /> <img width="684" height="447" alt="image" src="https://github.com/user-attachments/assets/66051f5a-ca38-4d26-996d-ba74a8d6181b" /> </details> ## ⏪ Backport rationale Not backporting since this is a new functionality ## 🔗 References Closes [elastic#2081](elastic/kibana-team#2081) ## Release Notes Added support for deleting export schedules ### Checklist - [x] 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/src/platform/packages/shared/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 - [ ] ~~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 was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations.~~ - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ([50 runs passed](elastic#238197 (comment))) - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels.
## Summary When I started to work on ["editing scheduled reports"](elastic/kibana-team#2083), I looked around in the reporting plugin for something like a `scheduledReportClient` so I could create an `update` method. This client does not exist. I was surprised that there isn't a single "client" to manage `SCHEDULED_REPORT_SAVED_OBJECT_TYPE` SOs. Instead, that is done in a couple of different places. - `ScheduleRequestHandler` has the creation logic and schedules the report generation tasks. - `ScheduledQueryFactory` handles `list` and `bulkDisable`. Moreover, these were found in `routes/common` but weren't shared by any routes. They are each imported in a single place. I think it makes sense to consolidate this client/service logic, and move it **outside** of the `routes` folder. As a first step, I am just moving some code around. The consolidation of `ScheduleRequestHandler` with `ScheduledQueryFactory` into a `ScheduledRequestClient` will be done later. For now, this PR does the following: - I will wait for @umbopepato 's PR to be merged first - elastic#238197. - Just moving code around. Nothing changed(yet), and no tests should fail. - `ScheduledQueryFactory` now lives in `reporting/server/services/scheduled_reports/`. - the file where `ScheduledQueryFactory` had too much logic, it was split into: - `constants.ts` - `transforms.ts` - `transforms.test.ts` - `audit_events.ts` was moved into `reporting/server/services/audit_events/`.
…elastic#238197) ## 📄 Summary - Adds backend support for deleting export schedules - Adds client-side delete schedules fetcher and react-query hook - Adds delete UI action to `Stack Management` > `Reporting` > `Schedules` table rows <details> <summary> ## 🧪 Verification steps </summary> ### ✅ Happy Path 1. If you don't have any Dashboard or Discover session to export, install a sample data set from the Kibana home page 2. Open any dashboard or Discover session 3. Click on the ⬇️ (Export) button in the app bar 4. Click on Schedule export 5. Choose a close schedule time (minutes after) 6. Repeat from point 2 more times to get more schedules 7. Navigate to `Stack Management` > `Reporting` > `Schedules` 8. Click on the actions menu button (•••) 9. Click `Delete schedule` 10. Verify that the schedule item disappeared from the table 11. Wait for the execution time of the schedules and verify that after that time, no report was created in the `Reports` tab (refresh if necessary) </details> <details> <summary> ## 📷 Screenshots </summary> <img width="1015" height="392" alt="image" src="https://github.com/user-attachments/assets/36296a25-a6c9-4832-b190-54b35d14676d" /> <img width="1100" height="583" alt="image" src="https://github.com/user-attachments/assets/3e0ce676-6423-4a1e-b8aa-6c5680ec1523" /> <img width="684" height="447" alt="image" src="https://github.com/user-attachments/assets/66051f5a-ca38-4d26-996d-ba74a8d6181b" /> </details> ## ⏪ Backport rationale Not backporting since this is a new functionality ## 🔗 References Closes [elastic#2081](elastic/kibana-team#2081) ## Release Notes Added support for deleting export schedules ### Checklist - [x] 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/src/platform/packages/shared/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 - [ ] ~~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 was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations.~~ - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ([50 runs passed](elastic#238197 (comment))) - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels.


📄 Summary
Stack Management>Reporting>Schedulestable rows🧪 Verification steps
✅ Happy Path
Stack Management>Reporting>SchedulesDelete scheduleReportstab (refresh if necessary)📷 Screenshots
⏪ Backport rationale
Not backporting since this is a new functionality
🔗 References
Closes #2081
Release Notes
Added support for deleting export schedules
Checklist
If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker listThis was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. Therelease_note:breakinglabel should be applied in these situations.release_note:*label is applied per the guidelinesbackport:*labels.