diff --git a/.generated-info b/.generated-info index ca23f6db0fc2..21b6d9ccc2f6 100644 --- a/.generated-info +++ b/.generated-info @@ -1,4 +1,4 @@ { - "spec_repo_commit": "8001cbb", - "generated": "2025-08-13 20:27:09.207" + "spec_repo_commit": "095be0d", + "generated": "2025-08-13 22:40:11.170" } diff --git a/.generator/schemas/v1/openapi.yaml b/.generator/schemas/v1/openapi.yaml index f6e0e21ed71a..fda811d36e95 100644 --- a/.generator/schemas/v1/openapi.yaml +++ b/.generator/schemas/v1/openapi.yaml @@ -3445,14 +3445,33 @@ components: description: The view of the world that the map should render. example: focus: WORLD - properties: + oneOf: + - required: + - focus + - required: + - custom_extent + properties: + custom_extent: + description: A custom extent of the map defined by an array of four numbers + in the order `[minLongitude, minLatitude, maxLongitude, maxLatitude]`. + example: + - -30 + - -40 + - 40 + - 30 + items: + description: The longitudinal or latitudinal coordinates of the bounding + box. + format: double + type: number + maxItems: 4 + minItems: 4 + type: array focus: - description: The 2-letter ISO code of a country to focus the map on. Or - `WORLD`. + description: The ISO code of a country, subdivision, or region to focus + the map on. Or `WORLD`. Mutually exclusive with `custom_extent`. example: WORLD type: string - required: - - focus type: object GeomapWidgetRequest: description: An updated geomap widget. diff --git a/cassettes/v1/Dashboards_1335235736/Create-a-new-dashboard-with-geomap-widget-in-custom-focus_37738581/frozen.json b/cassettes/v1/Dashboards_1335235736/Create-a-new-dashboard-with-geomap-widget-in-custom-focus_37738581/frozen.json new file mode 100644 index 000000000000..ae48787e3f2e --- /dev/null +++ b/cassettes/v1/Dashboards_1335235736/Create-a-new-dashboard-with-geomap-widget-in-custom-focus_37738581/frozen.json @@ -0,0 +1 @@ +"2025-08-13T22:21:29.993Z" diff --git a/cassettes/v1/Dashboards_1335235736/Create-a-new-dashboard-with-geomap-widget-in-custom-focus_37738581/recording.har b/cassettes/v1/Dashboards_1335235736/Create-a-new-dashboard-with-geomap-widget-in-custom-focus_37738581/recording.har new file mode 100644 index 000000000000..2f75289f1e87 --- /dev/null +++ b/cassettes/v1/Dashboards_1335235736/Create-a-new-dashboard-with-geomap-widget-in-custom-focus_37738581/recording.har @@ -0,0 +1,110 @@ +{ + "log": { + "_recordingName": "Dashboards/Create a new dashboard with geomap widget in custom focus", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.5" + }, + "entries": [ + { + "_id": "6b17b4af82cf16ce8c4a879dbc8a1863", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 796, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + }, + { + "_fromType": "array", + "name": "content-type", + "value": "application/json" + } + ], + "headersSize": 559, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"description\":null,\"layout_type\":\"free\",\"notify_list\":[],\"template_variables\":[],\"title\":\"Test-Create_a_new_dashboard_with_geomap_widget_in_custom_focus-1755123689\",\"widgets\":[{\"definition\":{\"requests\":[{\"formulas\":[{\"formula\":\"query1\"}],\"queries\":[{\"compute\":{\"aggregation\":\"count\"},\"data_source\":\"rum\",\"group_by\":[{\"facet\":\"@geo.country_iso_code\",\"limit\":250,\"sort\":{\"aggregation\":\"count\",\"order\":\"desc\"}}],\"indexes\":[\"*\"],\"name\":\"query1\",\"search\":{\"query\":\"\"}}],\"response_format\":\"scalar\",\"sort\":{\"count\":250,\"order_by\":[{\"index\":0,\"order\":\"desc\",\"type\":\"formula\"}]}}],\"style\":{\"palette\":\"hostmap_blues\",\"palette_flip\":false},\"time\":{},\"title\":\"\",\"title_align\":\"left\",\"title_size\":\"16\",\"type\":\"geomap\",\"view\":{\"custom_extent\":[-30,-40,40,30]}},\"layout\":{\"height\":30,\"width\":47,\"x\":0,\"y\":0}}]}" + }, + "queryString": [], + "url": "https://api.datadoghq.com/api/v1/dashboard" + }, + "response": { + "bodySize": 1111, + "content": { + "mimeType": "application/json", + "size": 1111, + "text": "{\"id\":\"ift-hah-kbn\",\"title\":\"Test-Create_a_new_dashboard_with_geomap_widget_in_custom_focus-1755123689\",\"description\":null,\"author_handle\":\"frog@datadoghq.com\",\"author_name\":\"frog\",\"layout_type\":\"free\",\"url\":\"/dashboard/ift-hah-kbn/test-createanewdashboardwithgeomapwidgetincustomfocus-1755123689\",\"template_variables\":[],\"widgets\":[{\"definition\":{\"requests\":[{\"formulas\":[{\"formula\":\"query1\"}],\"queries\":[{\"compute\":{\"aggregation\":\"count\"},\"data_source\":\"rum\",\"group_by\":[{\"facet\":\"@geo.country_iso_code\",\"limit\":250,\"sort\":{\"aggregation\":\"count\",\"order\":\"desc\"}}],\"indexes\":[\"*\"],\"name\":\"query1\",\"search\":{\"query\":\"\"}}],\"response_format\":\"scalar\",\"sort\":{\"count\":250,\"order_by\":[{\"index\":0,\"order\":\"desc\",\"type\":\"formula\"}]}}],\"style\":{\"palette\":\"hostmap_blues\",\"palette_flip\":false},\"time\":{},\"title\":\"\",\"title_align\":\"left\",\"title_size\":\"16\",\"type\":\"geomap\",\"view\":{\"custom_extent\":[-30,-40,40,30]}},\"layout\":{\"height\":30,\"width\":47,\"x\":0,\"y\":0},\"id\":7638192239577908}],\"notify_list\":[],\"created_at\":\"2025-08-13T22:21:30.205019+00:00\",\"modified_at\":\"2025-08-13T22:21:30.205019+00:00\",\"restricted_roles\":[]}\n" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/json" + } + ], + "headersSize": 689, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-08-13T22:21:29.996Z", + "time": 287 + }, + { + "_id": "0a9f5ca2ea43d1dd545ba37fce11ce6a", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "application/json" + } + ], + "headersSize": 520, + "httpVersion": "HTTP/1.1", + "method": "DELETE", + "queryString": [], + "url": "https://api.datadoghq.com/api/v1/dashboard/ift-hah-kbn" + }, + "response": { + "bodySize": 39, + "content": { + "mimeType": "application/json", + "size": 39, + "text": "{\"deleted_dashboard_id\":\"ift-hah-kbn\"}\n" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/json" + } + ], + "headersSize": 687, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-08-13T22:21:30.290Z", + "time": 205 + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/features/v1/dashboards.feature b/features/v1/dashboards.feature index 47794c745f44..c8622bed91d6 100644 --- a/features/v1/dashboards.feature +++ b/features/v1/dashboards.feature @@ -422,6 +422,17 @@ Feature: Dashboards And the response "widgets[0].definition.requests[0].sort.order_by[0].type" is equal to "formula" And the response "widgets[0].definition.requests[0].sort.order_by[0].index" is equal to 0 + @team:DataDog/dashboards-backend + Scenario: Create a new dashboard with geomap widget in custom focus + Given new "CreateDashboard" request + And body from file "dashboards_json_payload/geomap_widget_in_custom_focus.json" + When the request is sent + Then the response status is 200 OK + And the response "widgets[0].definition.type" is equal to "geomap" + And the response "widgets[0].definition.requests[0].sort.order_by[0].order" is equal to "desc" + And the response "widgets[0].definition.requests[0].sort.order_by[0].type" is equal to "formula" + And the response "widgets[0].definition.requests[0].sort.order_by[0].index" is equal to 0 + @team:DataDog/dashboards-backend Scenario: Create a new dashboard with heatmap widget Given new "CreateDashboard" request diff --git a/features/v1/dashboards_json_payload/geomap_widget_in_custom_focus.json b/features/v1/dashboards_json_payload/geomap_widget_in_custom_focus.json new file mode 100644 index 000000000000..acd4723f644a --- /dev/null +++ b/features/v1/dashboards_json_payload/geomap_widget_in_custom_focus.json @@ -0,0 +1,82 @@ +{ + "title":"{{ unique }}", + "description":null, + "widgets":[ + { + "layout":{ + "x":0, + "y":0, + "width":47, + "height":30 + }, + "definition":{ + "title":"", + "title_size":"16", + "title_align":"left", + "time":{ + + }, + "type":"geomap", + "requests":[ + { + "formulas":[ + { + "formula":"query1" + } + ], + "queries":[ + { + "name":"query1", + "data_source":"rum", + "search":{ + "query":"" + }, + "indexes":[ + "*" + ], + "compute":{ + "aggregation":"count" + }, + "group_by":[ + { + "facet":"@geo.country_iso_code", + "limit":250, + "sort":{ + "order":"desc", + "aggregation":"count" + } + } + ] + } + ], + "sort": { + "count":250, + "order_by":[ + { + "type":"formula", + "index":0, + "order":"desc" + } + ] + }, + "response_format":"scalar" + } + ], + "style":{ + "palette":"hostmap_blues", + "palette_flip":false + }, + "view":{ + "custom_extent":[-30, -40, 40, 30] + } + } + } + ], + "template_variables":[ + + ], + "layout_type":"free", + "notify_list":[ + + ] +} \ No newline at end of file diff --git a/services/dashboards/src/v1/models/GeomapWidgetDefinitionView.ts b/services/dashboards/src/v1/models/GeomapWidgetDefinitionView.ts index cdf47f4d31fe..c3dd6784644b 100644 --- a/services/dashboards/src/v1/models/GeomapWidgetDefinitionView.ts +++ b/services/dashboards/src/v1/models/GeomapWidgetDefinitionView.ts @@ -5,9 +5,13 @@ import { AttributeTypeMap } from "@datadog/datadog-api-client"; */ export class GeomapWidgetDefinitionView { /** - * The 2-letter ISO code of a country to focus the map on. Or `WORLD`. + * A custom extent of the map defined by an array of four numbers in the order `[minLongitude, minLatitude, maxLongitude, maxLatitude]`. */ - "focus": string; + "customExtent"?: [number, number, number, number]; + /** + * The ISO code of a country, subdivision, or region to focus the map on. Or `WORLD`. Mutually exclusive with `custom_extent`. + */ + "focus"?: string; /** * A container for additional, undeclared properties. * This is a holder for any undeclared properties as specified with @@ -23,10 +27,14 @@ export class GeomapWidgetDefinitionView { * @ignore */ static readonly attributeTypeMap: AttributeTypeMap = { + customExtent: { + baseName: "custom_extent", + type: "[number, number, number, number]", + format: "double", + }, focus: { baseName: "focus", type: "string", - required: true, }, additionalProperties: { baseName: "additionalProperties",