From 69f6687af9db6a0f0af82c9ed50cf2bdde7250b6 Mon Sep 17 00:00:00 2001
From: Shahzad <shahzad31comp@gmail.com>
Date: Mon, 12 Aug 2024 21:13:33 +0200
Subject: [PATCH] [Synthetics] Delete monitor API via id param !! (#190210)

## Summary

Allow deletion of monitor via id param !!

User can now delete monitor via passing id as url param

`DELETE <kibana host>:<port>/api/synthetics/monitors/<config_id>`

Previous bulk delete via list of ids via API body still works as well !!

Docs are updated !!
---
 .../monitors/delete-monitor-api.asciidoc      | 24 +++++++++++--
 .../management/monitor_list_table/columns.tsx |  2 +-
 .../routes/monitor_cruds/delete_monitor.ts    | 36 ++++++++++++++-----
 .../apis/synthetics/delete_monitor.ts         | 20 +++++++++++
 .../synthetics_monitor_test_service.ts        | 13 +++++++
 5 files changed, 83 insertions(+), 12 deletions(-)

diff --git a/docs/api/synthetics/monitors/delete-monitor-api.asciidoc b/docs/api/synthetics/monitors/delete-monitor-api.asciidoc
index 8730cc272a44..70861fcd60a3 100644
--- a/docs/api/synthetics/monitors/delete-monitor-api.asciidoc
+++ b/docs/api/synthetics/monitors/delete-monitor-api.asciidoc
@@ -8,9 +8,9 @@ Deletes one or more monitors from the Synthetics app.
 
 === {api-request-title}
 
-`DELETE <kibana host>:<port>/api/synthetics/monitors`
+`DELETE <kibana host>:<port>/api/synthetics/monitors/<config_id>`
 
-`DELETE <kibana host>:<port>/s/<space_id>/api/synthetics/monitors`
+`DELETE <kibana host>:<port>/s/<space_id>/api/synthetics/monitors/<config_id>`
 
 === {api-prereq-title}
 
@@ -20,6 +20,26 @@ You must have `all` privileges for the *Synthetics* feature in the *{observabili
 You must have `all` privileges for the *Synthetics* feature in the *{observability}* section of the
 <<kibana-feature-privileges,{kib} feature privileges>>.
 
+
+[[delete-monitor-api-path-params]]
+=== {api-path-parms-title}
+
+`config_id`::
+(Required, string) The ID of the monitor that you want to delete.
+
+
+Here is an example of a DELETE request to delete a monitor by ID:
+
+[source,sh]
+--------------------------------------------------
+DELETE /api/synthetics/monitors/monitor1-id
+--------------------------------------------------
+
+==== Bulk Delete Monitors
+
+You can delete multiple monitors by sending a list of config ids to a DELETE request to the `/api/synthetics/monitors` endpoint.
+
+
 [[monitors-delete-request-body]]
 ==== Request Body
 
diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx
index a2970af2e98c..1369fb086ada 100644
--- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx
+++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_list_table/columns.tsx
@@ -200,7 +200,7 @@ export function useMonitorListColumns({
         },
         {
           'data-test-subj': 'syntheticsMonitorCopyAction',
-          isPrimary: true,
+          isPrimary: false,
           name: (fields) => (
             <NoPermissionsTooltip
               canEditSynthetics={canEditSynthetics}
diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/delete_monitor.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/delete_monitor.ts
index df437cc43189..b7a1d0b2d48d 100644
--- a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/delete_monitor.ts
+++ b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/delete_monitor.ts
@@ -29,30 +29,48 @@ import { formatSecrets, normalizeSecrets } from '../../synthetics_service/utils/
 
 export const deleteSyntheticsMonitorRoute: SyntheticsRestApiRouteFactory<
   DeleteParamsResponse[],
-  Record<string, any>,
-  Record<string, any>,
+  Record<string, string>,
+  Record<string, string>,
   { ids: string[] }
 > = () => ({
   method: 'DELETE',
-  path: SYNTHETICS_API_URLS.SYNTHETICS_MONITORS,
+  path: SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/{id?}',
   validate: {},
   validation: {
     request: {
-      body: schema.object({
-        ids: schema.arrayOf(schema.string(), {
-          minSize: 1,
-        }),
+      body: schema.nullable(
+        schema.object({
+          ids: schema.arrayOf(schema.string(), {
+            minSize: 1,
+          }),
+        })
+      ),
+      params: schema.object({
+        id: schema.maybe(schema.string()),
       }),
     },
   },
   handler: async (routeContext): Promise<any> => {
     const { request, response } = routeContext;
 
-    const { ids } = request.body;
+    const { ids } = request.body || {};
+    const { id: queryId } = request.params;
+
+    if (ids && queryId) {
+      return response.badRequest({
+        body: { message: 'id must be provided either via param or body.' },
+      });
+    }
 
     const result: Array<{ id: string; deleted: boolean; error?: string }> = [];
+    const idsToDelete = [...(ids ?? []), ...(queryId ? [queryId] : [])];
+    if (idsToDelete.length === 0) {
+      return response.badRequest({
+        body: { message: 'id must be provided via param or body.' },
+      });
+    }
 
-    await pMap(ids, async (id) => {
+    await pMap(idsToDelete, async (id) => {
       try {
         const { errors, res } = await deleteMonitor({
           routeContext,
diff --git a/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts b/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts
index ed5e67c5fe26..c96175d2982b 100644
--- a/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts
+++ b/x-pack/test/api_integration/apis/synthetics/delete_monitor.ts
@@ -79,6 +79,26 @@ export default function ({ getService }: FtrProviderContext) {
       // Hit get endpoint and expect 404 as well
       await supertest.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId).expect(404);
     });
+
+    it('deletes monitor by param id', async () => {
+      const { id: monitorId } = await saveMonitor(httpMonitorJson as MonitorFields);
+
+      const deleteResponse = await monitorTestService.deleteMonitorByIdParam(monitorId, 200);
+
+      expect(deleteResponse.body).eql([{ id: monitorId, deleted: true }]);
+
+      // Hit get endpoint and expect 404 as well
+      await supertest.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId).expect(404);
+    });
+
+    it('throws error if both body and param are missing', async () => {
+      const deleteResponse = await supertest
+        .delete(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS)
+        .send()
+        .set('kbn-xsrf', 'true');
+      expect(deleteResponse.status).to.eql(400);
+    });
+
     it('deletes multiple monitors by id', async () => {
       const { id: monitorId } = await saveMonitor(httpMonitorJson as MonitorFields);
       const { id: monitorId2 } = await saveMonitor({
diff --git a/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts b/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts
index 4fc4c172eb6c..b30ea45a8c10 100644
--- a/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts
+++ b/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts
@@ -173,4 +173,17 @@ export class SyntheticsMonitorTestService {
     expect(deleteResponse.status).to.eql(statusCode);
     return deleteResponse;
   }
+
+  async deleteMonitorByIdParam(monitorId?: string, statusCode = 200, spaceId?: string) {
+    const deleteResponse = await this.supertest
+      .delete(
+        spaceId
+          ? `/s/${spaceId}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}/${monitorId}`
+          : SYNTHETICS_API_URLS.SYNTHETICS_MONITORS + '/' + monitorId
+      )
+      .send()
+      .set('kbn-xsrf', 'true');
+    expect(deleteResponse.status).to.eql(statusCode);
+    return deleteResponse;
+  }
 }