Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ plugins/TransportForLondon/* @clarkd
plugins/UniFi/* @adamkinniburgh
plugins/UptimeRobot/* @kieranlangton
plugins/WorldCup2026/* @TimWheeler-SQUP
plugins/Algolia/* @andrewmumblebee


# Fallback – if a plugin has no specified author
Expand Down
11 changes: 11 additions & 0 deletions plugins/Algolia/v1/configValidation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"steps": [
{
"displayName": "Authenticate",
"dataStream": { "name": "indexes" },
"required": true,
"error": "Could not connect to Algolia. Check your Application ID is correct and that your API key has the 'listIndexes' ACL (a Search-Only key will not work).",
"success": "Connected to Algolia."
}
]
}
9 changes: 9 additions & 0 deletions plugins/Algolia/v1/custom_types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"name": "Index",
"sourceType": "Index",
"icon": "magnifying-glass",
"singular": "Index",
"plural": "Indices"
}
]
47 changes: 47 additions & 0 deletions plugins/Algolia/v1/dataStreams/indexes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "indexes",
"displayName": "Indices",
"description": "Algolia indices in the application, with record counts, sizes, last build time and pending tasks",
"tags": ["Indices"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"baseUrl": "https://{{dataSource.applicationId}}.algolia.net",
"httpMethod": "get",
"endpointPath": "/1/indexes",
"pathToData": "items",
"paging": { "mode": "none" }
},
"matches": "none",
"metadata": [
{ "name": "name", "displayName": "Index", "role": "label" },
{
"name": "entries",
"displayName": "Records",
"shape": ["number", { "decimalPlaces": 0 }]
},
{
"name": "dataSize",
"displayName": "Data Size",
"shape": ["bytes", { "thousandsSeparator": true }]
},
{
"name": "fileSize",
"displayName": "File Size",
"shape": ["bytes", { "thousandsSeparator": true }]
},
{
"name": "lastBuildTimeS",
"displayName": "Last Build Time",
"shape": ["seconds", { "thousandsSeparator": true }]
},
{
"name": "numberOfPendingTasks",
"displayName": "Pending Tasks",
"shape": ["number", { "decimalPlaces": 0 }]
},
{ "name": "pendingTask", "displayName": "Has Pending Task" },

Check warning on line 42 in plugins/Algolia/v1/dataStreams/indexes.json

View check run for this annotation

Claude / Claude Code Review

Missing shape: boolean on pendingTask column

Nit: the `pendingTask` column on `plugins/Algolia/v1/dataStreams/indexes.json:42` has no `shape` declaration, while every other typed column in this same file (`entries`, `dataSize`, `fileSize`, `lastBuildTimeS`, `numberOfPendingTasks`, `updatedAt`) is shaped. Algolia returns `pendingTask` as a boolean and the displayName "Has Pending Task" reads as a yes/no question, so adding `"shape": "boolean"` would make the column render as a formatted indicator rather than a raw `true`/`false` string. The

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟑 Nit: the pendingTask column on plugins/Algolia/v1/dataStreams/indexes.json:42 has no shape declaration, while every other typed column in this same file (entries, dataSize, fileSize, lastBuildTimeS, numberOfPendingTasks, updatedAt) is shaped. Algolia returns pendingTask as a boolean and the displayName "Has Pending Task" reads as a yes/no question, so adding "shape": "boolean" would make the column render as a formatted indicator rather than a raw true/false string. The neighbouring primary column on line 44 is in the same situation. Pure cosmetic β€” the data flows correctly today.

Extended reasoning...

What the bug is

plugins/Algolia/v1/dataStreams/indexes.json:42 declares the pendingTask column with only a name and displayName:

{ "name": "pendingTask", "displayName": "Has Pending Task" },

Algolia's /1/indexes API returns pendingTask as a boolean (true if the index has tasks pending, false otherwise). The displayName "Has Pending Task" is phrased as a yes/no question, reinforcing the boolean intent.

Internal inconsistency within the same file

Every other typed column in this same stream is shaped β€” entries as number, dataSize/fileSize as bytes, lastBuildTimeS as seconds, numberOfPendingTasks as number, updatedAt as date (the last of which was specifically added in response to a prior review nudge in this same PR). So the shape-discipline is otherwise applied throughout the file; pendingTask is the lone exception. The neighbouring primary column on line 44 has the same omission.

Addressing the refutation

A reviewer correctly pointed out that the shape: boolean pattern is not universal across the repo β€” NinjaOne uses it consistently (smartCapable, isDynamic, etc.), but AutoTask boolean columns ship with no shape, and MicrosoftDefender uses shape: number for booleans. That's a fair point: there is no single repo-wide standard, and REVIEW.md's shapes guidance lists bytes/timestamp/duration as examples without naming boolean. This is why the issue is a nit, not a normal-severity bug β€” there's no documented rule being violated, only an internal inconsistency within this one file and a missed opportunity to use a more semantic shape.

Why it's still worth flagging

The column is rendered in the per-index Algolia Index dashboard's "Index Details" tile (a transposed datastream-properties table with no hiddenColumns filter), so users will see the raw value there β€” it's not entirely hidden behind the Overview dashboard's hide list. And the cost of the fix is two characters on a brand-new plugin where the author has otherwise been meticulous about shapes.

How to fix

Optionally apply the same to primary on line 44.

Step-by-step proof

  1. Open plugins/Algolia/v1/dataStreams/indexes.json:42 β€” { "name": "pendingTask", "displayName": "Has Pending Task" } with no shape.
  2. Open lines 17–41 of the same file β€” every other typed column declares an appropriate shape.
  3. Cross-reference Algolia's /1/indexes API documentation β€” pendingTask is a boolean indicator.
  4. Cross-reference plugins/NinjaOne/v1/dataStreams/disks.json:88 β€” { "name": "smartCapable", "displayName": "SMART Capable", "shape": "boolean" } confirms the pattern exists in the repo.
  5. Result: the column will display as a raw true/false string in the per-index dashboard rather than as a formatted boolean indicator.

{ "name": "updatedAt", "displayName": "Last Updated", "shape": "date" },
{ "name": "primary", "displayName": "Primary Index" }
],
"timeframes": false
}
43 changes: 43 additions & 0 deletions plugins/Algolia/v1/dataStreams/noResultRate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "noResultRate",
"displayName": "No-Result Rate",
"description": "Daily proportion of searches returning no results for an index",
"tags": ["Analytics"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "/2/searches/noResultRate",
"getArgs": [
{ "key": "index", "value": "{{object.rawId}}" },
{ "key": "startDate", "value": "{{timeframe.start.slice(0,10)}}" },
{ "key": "endDate", "value": "{{timeframe.end.slice(0,10)}}" }
],
"pathToData": "dates"
},
"matches": { "sourceType": { "type": "equals", "value": "Index" } },
"metadata": [
{
"name": "date",
"displayName": "Date",
"shape": "date",
"role": "timestamp"
},
{
"name": "rate",
"displayName": "No-result rate",
"shape": ["percent", { "asZeroToOne": true }],
"role": "value"
},
{
"name": "count",
"displayName": "Searches",
"shape": ["number", { "decimalPlaces": 0 }]
},
{
"name": "noResultCount",
"displayName": "No-result searches",
"shape": ["number", { "decimalPlaces": 0 }]
}
],
"timeframes": true
}
39 changes: 39 additions & 0 deletions plugins/Algolia/v1/dataStreams/noResultSearches.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "noResultSearches",
"displayName": "Top Searches With No Results",
"description": "Most frequent searches that returned no results for an index",
"tags": ["Analytics"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "/2/searches/noResults",
"getArgs": [
{ "key": "index", "value": "{{object.rawId}}" },
{ "key": "startDate", "value": "{{timeframe.start.slice(0,10)}}" },
{ "key": "endDate", "value": "{{timeframe.end.slice(0,10)}}" },
{ "key": "limit", "value": "50" }
],
"pathToData": "searches",
"paging": { "mode": "none" }
},
"matches": { "sourceType": { "type": "equals", "value": "Index" } },
"metadata": [
{
"name": "search",
"displayName": "Search",
"shape": "string",
"role": "label"
},
{
"name": "count",
"displayName": "Count",
"shape": ["number", { "decimalPlaces": 0 }]
},
{
"name": "withFilterCount",
"displayName": "With filters",
"shape": ["number", { "decimalPlaces": 0 }]
}
],
"timeframes": true
}
36 changes: 36 additions & 0 deletions plugins/Algolia/v1/dataStreams/searchCount.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "searchCount",
"displayName": "Search Count",
"description": "Daily number of searches for an index over the selected timeframe",
"tags": ["Analytics"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "/2/searches/count",
"getArgs": [
{ "key": "index", "value": "{{object.rawId}}" },
{ "key": "startDate", "value": "{{timeframe.start.slice(0,10)}}" },
{ "key": "endDate", "value": "{{timeframe.end.slice(0,10)}}" }
],
"pathToData": "dates"
},
"matches": { "sourceType": { "type": "equals", "value": "Index" } },
"metadata": [
{
"name": "date",
"displayName": "Date",
"shape": "date",
"role": "timestamp"
},
{
"name": "count",
"displayName": "Searches",
"shape": ["number", { "decimalPlaces": 0 }],
"role": "value"
},
{
"pattern": ".*"
}
],
"timeframes": true
}
39 changes: 39 additions & 0 deletions plugins/Algolia/v1/dataStreams/topSearches.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "topSearches",
"displayName": "Top Searches",
"description": "Most frequent search queries for an index over the selected timeframe",
"tags": ["Analytics"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "/2/searches",
"getArgs": [
{ "key": "index", "value": "{{object.rawId}}" },
{ "key": "startDate", "value": "{{timeframe.start.slice(0,10)}}" },
{ "key": "endDate", "value": "{{timeframe.end.slice(0,10)}}" },
{ "key": "limit", "value": "50" }
],
"pathToData": "searches",
"paging": { "mode": "none" }
},
"matches": { "sourceType": { "type": "equals", "value": "Index" } },
"metadata": [
{
"name": "search",
"displayName": "Search",
"shape": "string",
"role": "label"
},
{
"name": "count",
"displayName": "Count",
"shape": ["number", { "decimalPlaces": 0 }]
},
{
"name": "nbHits",
"displayName": "Results",
"shape": ["number", { "decimalPlaces": 0 }]
}
],
"timeframes": true
}
Loading
Loading