Skip to content

Commit a625d2c

Browse files
committed
feat: Add Chronicle DashboardChart and NativeDashboard resources
1 parent f0681df commit a625d2c

11 files changed

Lines changed: 1857 additions & 0 deletions

mmv1/products/chronicle/DashboardChart.yaml

Lines changed: 703 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# Copyright 2026 Google Inc.
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
---
15+
name: NativeDashboard
16+
description: A configuration for a native dashboard within a Google SecOps (Chronicle) instance.
17+
references:
18+
guides:
19+
'Google SecOps Guides': 'https://cloud.google.com/chronicle/docs/secops/secops-overview'
20+
api: 'https://cloud.google.com/chronicle/docs/reference/rest/v1beta/projects.locations.instances.nativeDashboards'
21+
base_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards
22+
update_mask: true
23+
update_verb: PATCH
24+
self_link: 'projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards/{{dashboard_id}}'
25+
create_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards
26+
id_format: 'projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards/{{dashboard_id}}'
27+
28+
import_format:
29+
- projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards/{{dashboard_id}}
30+
31+
examples:
32+
- name: chronicle_nativedashboard_basic
33+
primary_resource_id: my-basic-dashboard
34+
ignore_read_extra:
35+
- "last_viewed_time"
36+
vars:
37+
resource_name: test-resource
38+
dashboard_name: 'dashboard-1'
39+
dashboard: 'testdashboard'
40+
dashboard_description: 'dashboard-description'
41+
test_env_vars:
42+
chronicle_id: 'CHRONICLE_ID'
43+
44+
autogen_status: TmF0aXZlRGFzaGJvYXJk
45+
parameters:
46+
- name: location
47+
type: String
48+
required: true
49+
immutable: true
50+
url_param_only: true
51+
description: The location of the Chronicle instance.
52+
- name: instance
53+
type: String
54+
required: true
55+
immutable: true
56+
url_param_only: true
57+
description: The ID of the Chronicle instance.
58+
59+
properties:
60+
- name: name
61+
type: String
62+
output: true
63+
description: The full resource name of the dashboard.
64+
65+
- name: dashboardId
66+
type: String
67+
output: true
68+
immutable: true
69+
default_from_api: true
70+
custom_flatten: 'templates/terraform/custom_flatten/id_from_name.tmpl'
71+
description: The unique ID of the Dashboard.
72+
73+
- name: access
74+
type: String
75+
description: |-
76+
The access level of the dashboard.
77+
Possible values:
78+
DASHBOARD_PRIVATE
79+
DASHBOARD_PUBLIC
80+
81+
- name: createTime
82+
type: String
83+
output: true
84+
description: The creation time of the dashboard.
85+
86+
- name: createUserId
87+
type: String
88+
output: true
89+
description: The ID of the user who created the dashboard.
90+
91+
- name: dashboardUserData
92+
type: NestedObject
93+
flatten_object: true
94+
description: User-specific data and preferences for the dashboard.
95+
properties:
96+
- name: isPinned
97+
type: Boolean
98+
description: Whether the dashboard is pinned by the user.
99+
- name: lastViewedTime
100+
type: String
101+
output: true
102+
description: The time when this dashboard was last viewed.
103+
104+
- name: definition
105+
type: NestedObject
106+
flatten_object: true
107+
description: |-
108+
The definition of the dashboard including filters, layout, and chart
109+
configurations.
110+
properties:
111+
- name: charts
112+
type: Array
113+
ignore_read: true
114+
description: A list of charts included in the dashboard definition.
115+
item_type:
116+
type: NestedObject
117+
properties:
118+
- name: chartLayout
119+
type: NestedObject
120+
ignore_read: true
121+
description: The visual layout parameters of this chart within the dashboard.
122+
properties:
123+
- name: startX
124+
type: Integer
125+
send_empty_value: true
126+
description: The starting X coordinate.
127+
- name: spanX
128+
type: Integer
129+
required: true
130+
description: The number of columns the chart spans.
131+
- name: startY
132+
type: Integer
133+
send_empty_value: true
134+
description: The starting Y coordinate.
135+
- name: spanY
136+
type: Integer
137+
required: true
138+
description: The number of rows the chart spans.
139+
- name: dashboardChart
140+
type: String
141+
description: The resource name of the associated DashboardChart.
142+
- name: filtersIds
143+
type: Array
144+
default_from_api: true
145+
item_type:
146+
type: String
147+
description: List of dashboard filter IDs applied to this chart.
148+
149+
- name: filters
150+
type: Array
151+
description: Global filters defined for the dashboard.
152+
item_type:
153+
type: NestedObject
154+
properties:
155+
- name: chartIds
156+
type: Array
157+
item_type:
158+
type: String
159+
description: The IDs of charts that this filter applies to.
160+
- name: dataSource
161+
type: String
162+
description: |-
163+
The data source for the filter.
164+
Possible values:
165+
UDM, ENTITY, INGESTION_METRICS, RULE_DETECTIONS, RULESETS, GLOBAL,
166+
IOC_MATCHES, RULES, SOAR_CASES, SOAR_PLAYBOOKS, SOAR_CASE_HISTORY,
167+
DATA_TABLE, INVESTIGATION, INVESTIGATION_FEEDBACK
168+
- name: displayName
169+
type: String
170+
description: The display name of the filter.
171+
- name: fieldPath
172+
type: String
173+
description: The UDM field path being filtered.
174+
- name: filterOperatorAndFieldValues
175+
type: Array
176+
description: The specific operator and value set for the filter.
177+
item_type:
178+
type: NestedObject
179+
properties:
180+
- name: fieldValues
181+
type: Array
182+
item_type:
183+
type: String
184+
description: |-
185+
The values for the modifier. All operators should have a single
186+
value other than 'IN' and 'BETWEEN'.
187+
- name: filterOperator
188+
type: String
189+
description: |-
190+
The operator to apply to the field.
191+
Possible values:
192+
EQUAL, NOT_EQUAL, IN, GREATER_THAN, GREATER_THAN_OR_EQUAL_TO,
193+
LESS_THAN, LESS_THAN_OR_EQUAL_TO, BETWEEN, PAST, IS_NULL,
194+
IS_NOT_NULL, STARTS_WITH, ENDS_WITH, DOES_NOT_STARTS_WITH,
195+
DOES_NOT_ENDS_WITH, NOT_IN, CONTAINS, DOES_NOT_CONTAIN
196+
- name: id
197+
type: String
198+
description: The unique ID of the filter.
199+
- name: isMandatory
200+
type: Boolean
201+
description: Whether the filter is mandatory for the dashboard consumer.
202+
- name: isStandardTimeRangeFilter
203+
type: Boolean
204+
description: Whether the filter is a standard time range filter.
205+
- name: isStandardTimeRangeFilterEnabled
206+
type: Boolean
207+
description: Whether the standard time range filter is currently enabled.
208+
209+
- name: fingerprint
210+
type: String
211+
output: true
212+
description: The server-generated fingerprint of the dashboard definition.
213+
214+
- name: description
215+
type: String
216+
description: A description of the dashboard.
217+
218+
- name: displayName
219+
type: String
220+
required: true
221+
description: The display name/title of the dashboard visible to users.
222+
223+
- name: etag
224+
type: String
225+
output: true
226+
description: |-
227+
Server-computed checksum for optimistic concurrency control,
228+
sent on update and delete requests.
229+
230+
- name: type
231+
type: String
232+
description: |-
233+
The type of dashboard.
234+
Possible values:
235+
CURATED, PRIVATE, PUBLIC, CUSTOM, MARKETPLACE
236+
237+
- name: updateTime
238+
type: String
239+
output: true
240+
description: The time when the dashboard was last edited.
241+
242+
- name: updateUserId
243+
type: String
244+
output: true
245+
description: The ID of the user who last edited the dashboard.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{{/*
2+
The license inside this block applies to this file
3+
Copyright 2024 Google Inc.
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/ -}}
13+
decodedRes := make(map[string]interface{})
14+
var chartMap map[string]interface{}
15+
var name string
16+
var ok bool
17+
18+
// Check if the response contains a top-level "dashboardChart" key.
19+
// This structure is returned by the :addChart API.
20+
if chartVal, exists := res["dashboardChart"]; exists {
21+
chartMap, ok = chartVal.(map[string]interface{})
22+
if !ok {
23+
return nil, fmt.Errorf("'dashboardChart' value is of type %T, expected map[string]interface{}", chartVal)
24+
}
25+
// The "name" is inside the "dashboardChart" map.
26+
nameVal, nameExists := chartMap["name"]
27+
if !nameExists {
28+
return nil, fmt.Errorf("missing key 'name' within 'dashboardChart'")
29+
}
30+
name, ok = nameVal.(string)
31+
if !ok {
32+
return nil, fmt.Errorf("'name' value within 'dashboardChart' is of type %T, expected string", nameVal)
33+
}
34+
} else {
35+
// If "dashboardChart" is not a top-level key, assume the entire 'res' map
36+
// represents the DashboardChart. This is the structure from the GET API.
37+
chartMap = res
38+
39+
// The "name" is at the top level of the 'res' map.
40+
nameVal, nameExists := chartMap["name"]
41+
if !nameExists {
42+
// If neither structure matches, return an error.
43+
return nil, fmt.Errorf("response does not contain 'dashboardChart' key or top-level 'name' key")
44+
}
45+
name, ok = nameVal.(string)
46+
if !ok {
47+
return nil, fmt.Errorf("top-level 'name' value is of type %T, expected string", nameVal)
48+
}
49+
}
50+
if val, ok := d.GetOk("dashboard_chart.0.chart_datasource.0.dashboard_query"); ok {
51+
rawList := d.Get("dashboard_query").([]interface{})
52+
53+
var dashboardQueryMap map[string]interface{}
54+
55+
// 2. Prepare the map for the first element
56+
if len(rawList) > 0 && rawList[0] != nil {
57+
dashboardQueryMap = make(map[string]interface{})
58+
for k, v := range rawList[0].(map[string]interface{}) {
59+
dashboardQueryMap[k] = v
60+
}
61+
} else {
62+
dashboardQueryMap = make(map[string]interface{})
63+
}
64+
dashboardQueryMap["name"] = val
65+
66+
updatedList := []interface{}{dashboardQueryMap}
67+
68+
if err := d.Set("dashboard_query", updatedList); err != nil {
69+
log.Printf("[ERROR] jdivyam: failed to set dashboard_query: %s", err)
70+
return nil, fmt.Errorf("error setting dashboard_query: %s", err)
71+
}
72+
}
73+
74+
// Return a consistent map structure:
75+
// "name": The resource name of the chart.
76+
// "dashboardChart": The complete map of the dashboard chart properties.
77+
decodedRes["name"] = name
78+
decodedRes["dashboardChart"] = chartMap
79+
return decodedRes, nil

0 commit comments

Comments
 (0)