Skip to content

Commit 3297bf3

Browse files
authored
[POFSP-222] Fix regressions in events table and annotations query (#305)
* Fix regressions in the events table and annotations query * Re-added hints/examples for the annotation query syntax
1 parent 6529dca commit 3297bf3

File tree

5 files changed

+231
-6
lines changed

5 files changed

+231
-6
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,26 @@
22

33
This article documents the ongoing improvements we're making to the **Cognite Data Source for Grafana**.
44

5+
6+
## 4.0.1 - November 7th, 2023
7+
8+
### Bug fixes
9+
- Patched regressions introduced after the migration to React:
10+
* Events table `activeAtTime` filter is now working as before
11+
* Annotations filters are applied correctly as before
12+
* Added back hints and examples for annotation query
13+
- Multiple dependencies have been updated to fix security vulnerabilities
14+
515
## 4.0.0 - October 12th, 2023
16+
17+
### Features
618
- Migrate Annotation editor from Angular to React
719
- Bumped minimum Grafana version requirement to v10
820
- Events are returned in dataframe format
921

1022
## 3.1.0 - May 10th, 2023
23+
24+
### Features
1125
- Added support for the new version of CDF Data Models (GraphQL)
1226
- Added an option to sort Events table
1327

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@cognite/cognite-grafana-datasource",
3-
"version": "4.0.0",
3+
"version": "4.0.1",
44
"description": "Cognite Data Fusion datasource",
55
"repository": "https://github.com/cognitedata/cognite-grafana-datasource",
66
"author": "Cognite AS",

src/__tests__/datasource.events.unit.test.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,43 @@ describe('events datasource test', () => {
3636
},
3737
}
3838

39+
it('simple event query outside of time range', async () => {
40+
41+
fetchItemsMock.mockResolvedValueOnce([])
42+
43+
const query: CogniteQuery = {
44+
...eventQueryBase,
45+
eventQuery: {
46+
...eventQueryBase.eventQuery,
47+
activeAtTimeRange: false,
48+
}
49+
}
50+
const res = await eventsDatasource.fetchEventTargets(
51+
[query],
52+
[startTime, endTime]
53+
);
54+
55+
expect(fetchItemsMock).toHaveBeenCalledTimes(1)
56+
expect(connector.fetchItems).toHaveBeenCalledWith({
57+
data: {
58+
filter: {
59+
},
60+
limit: 1000,
61+
},
62+
headers: undefined,
63+
method: "POST",
64+
path: "/events/list"
65+
})
66+
67+
expect(res).toEqual([
68+
{
69+
fields: [],
70+
length: 0,
71+
name: "Events",
72+
refId: undefined,
73+
},
74+
]);
75+
});
3976

4077
it('simple event query with sort', async () => {
4178

@@ -134,4 +171,131 @@ describe('events datasource test', () => {
134171
},
135172
]);
136173
});
174+
175+
it('annotations event query (empty)', async () => {
176+
177+
fetchItemsMock.mockResolvedValueOnce([])
178+
179+
const query: CogniteQuery = {
180+
...defaultCogniteQuery,
181+
refId: "Anno",
182+
tab: null,
183+
query: "events{}",
184+
}
185+
const res = await eventsDatasource.fetchEventTargets(
186+
[query],
187+
[startTime, endTime]
188+
);
189+
190+
expect(fetchItemsMock).toHaveBeenCalledTimes(1)
191+
expect(connector.fetchItems).toHaveBeenCalledWith({
192+
data: {
193+
filter: {
194+
activeAtTime: {
195+
max: endTime,
196+
min: startTime,
197+
},
198+
},
199+
limit: 1000,
200+
},
201+
headers: undefined,
202+
method: "POST",
203+
path: "/events/list"
204+
})
205+
206+
expect(res).toEqual([
207+
{
208+
fields: [],
209+
length: 0,
210+
name: "Events",
211+
refId: "Anno",
212+
},
213+
]);
214+
});
215+
216+
it('annotations event query (with end time override)', async () => {
217+
218+
fetchItemsMock.mockResolvedValueOnce([])
219+
220+
const query: CogniteQuery = {
221+
...defaultCogniteQuery,
222+
refId: "Anno",
223+
tab: null,
224+
query: "events{endTime={isNull=false}}",
225+
}
226+
const res = await eventsDatasource.fetchEventTargets(
227+
[query],
228+
[startTime, endTime]
229+
);
230+
231+
expect(fetchItemsMock).toHaveBeenCalledTimes(1)
232+
expect(connector.fetchItems).toHaveBeenCalledWith({
233+
data: {
234+
filter: {
235+
activeAtTime: {
236+
max: endTime,
237+
min: startTime,
238+
},
239+
endTime: {
240+
isNull: false
241+
}
242+
},
243+
limit: 1000,
244+
},
245+
headers: undefined,
246+
method: "POST",
247+
path: "/events/list"
248+
})
249+
250+
expect(res).toEqual([
251+
{
252+
fields: [],
253+
length: 0,
254+
name: "Events",
255+
refId: "Anno",
256+
},
257+
]);
258+
});
259+
260+
it('annotations event query (with filters)', async () => {
261+
262+
fetchItemsMock.mockResolvedValueOnce([])
263+
264+
const query: CogniteQuery = {
265+
...defaultCogniteQuery,
266+
refId: "Anno",
267+
tab: null,
268+
query: "events{subtype='test'}",
269+
}
270+
const res = await eventsDatasource.fetchEventTargets(
271+
[query],
272+
[startTime, endTime]
273+
);
274+
275+
expect(fetchItemsMock).toHaveBeenCalledTimes(1)
276+
expect(connector.fetchItems).toHaveBeenCalledWith({
277+
data: {
278+
filter: {
279+
activeAtTime: {
280+
max: endTime,
281+
min: startTime,
282+
},
283+
subtype: 'test'
284+
},
285+
limit: 1000,
286+
},
287+
headers: undefined,
288+
method: "POST",
289+
path: "/events/list"
290+
})
291+
292+
expect(res).toEqual([
293+
{
294+
fields: [],
295+
length: 0,
296+
name: "Events",
297+
refId: "Anno",
298+
},
299+
]);
300+
});
137301
});

src/components/annotationsQueryEditor.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,48 @@ type AnnotationQueryEditorProps<TQuery extends DataQuery> = QueryEditorProps<any
1010
onAnnotationChange?: (annotation: AnnotationQuery<TQuery>) => void;
1111
};
1212

13+
const help = (
14+
<pre>
15+
Annotation query uses the <a className="query-keyword" href="https://docs.cognite.com/api/v1/#operation/advancedListEvents" target="_blank" rel="noreferrer">events/list</a> endpoint to fetch data.
16+
<br />
17+
<br />
18+
Use <code className="query-keyword">&apos;=&apos;</code> operator to provide parameters for the request.
19+
<br />
20+
Format: <code className="query-keyword">{`events{param=number, ...}`}</code>
21+
<br />
22+
Example: <code className="query-keyword">{`events{externalIdPrefix='PT', type='WORKORDER', assetSubtreeIds=[{id=12}, {externalId='external'}]}`}</code>
23+
<br />
24+
<br />
25+
26+
By default, the query displays all events that are active in the time range.
27+
<br />
28+
You can customize this with the additional time filters <code className="query-keyword">startTime</code>, <code className="query-keyword">endTime</code>.
29+
<br />
30+
This example shows how to display all finished events that started in the current time range:
31+
<br />
32+
<code className="query-keyword">{`events{startTime={min=$__from}, endTime={isNull=false}}`}</code>
33+
<br />
34+
<br />
35+
36+
You can specify additional client-side filtering with the <code className="query-keyword">&apos;=~&apos;</code>, <code className="query-keyword">&apos;!~&apos;</code> and <code className="query-keyword">&apos;!=&apos;</code> operators. Comma between multiple filters acts as logic <code className="query-keyword">AND</code>.<br />
37+
Format:<br />
38+
<code className="query-keyword">&apos;=~&apos;</code> – regex equality, returns results satisfying the regular expression.
39+
<br />
40+
<code className="query-keyword">&apos;!~&apos;</code> – regex inequality, excludes results satisfying the regular expression.
41+
<br />
42+
<code className="query-keyword">&apos;!=&apos;</code> – strict inequality, returns items where a property doesn&apos;t equal a given value.
43+
<br />
44+
Example: <code className="query-keyword">{`events{type='WORKORDER', subtype=~'SUB.*'}`}</code>
45+
<br />
46+
<br />
47+
Templating is available by using the <code className="query-keyword">$variable</code> syntax.
48+
<br />
49+
Example: <code className="query-keyword">{`events{type='WORKORDER', subtype=$variable}`}</code>.
50+
<br />
51+
To learn more about the querying capabilities of Cognite Data Source for Grafana, please visit our <a className="query-keyword" href="https://docs.cognite.com/cdf/dashboards/guides/grafana/getting_started.html">documentation</a>.
52+
</pre>
53+
);
54+
1355
export class AnnotationsQueryEditor extends React.PureComponent<AnnotationQueryEditorProps<CogniteQuery>, AnnotationQueryData> {
1456

1557
defaults = {
@@ -58,6 +100,7 @@ export class AnnotationsQueryEditor extends React.PureComponent<AnnotationQueryE
58100
</div>
59101
<div className="gf-form--grow">
60102
{this.state.error ? <pre className="gf-formatted-error">{this.state.error}</pre> : null}
103+
{help}
61104
</div>
62105
</div>
63106

src/datasources/EventsDatasource.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,19 @@ export class EventsDatasource {
3636
}
3737

3838
async fetchEventsForTarget(
39-
{ refId, eventQuery, query }: CogniteQuery,
39+
target: CogniteQuery,
4040
[rangeStart, rangeEnd]: Tuple<number>
4141
) {
42-
const timeFrame = {
42+
const { refId, eventQuery, query } = target;
43+
const isAnnotation = isAnnotationTarget(target);
44+
const activeAtTimeRange = isAnnotation || target.eventQuery?.activeAtTimeRange;
45+
const timeFrame = activeAtTimeRange ? {
4346
activeAtTime: { min: rangeStart, max: rangeEnd },
44-
};
45-
const finalEventQuery = eventQuery || {
47+
} : {};
48+
const finalEventQuery = isAnnotation ? {
4649
expr: query,
47-
}
50+
} : eventQuery
51+
4852
try {
4953
const { items, hasMore } = await this.fetchEvents(finalEventQuery, timeFrame);
5054
if (hasMore) {

0 commit comments

Comments
 (0)