Repository navigation
feat: support automated hw probe adoption - #151
Conversation
|
Important Review skippedThis PR was authored by the user configured for CodeRabbit reviews. By default, CodeRabbit skips reviewing PRs authored by this user. It's recommended to use a dedicated user account to post CodeRabbit review feedback. To trigger a single review, invoke the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughAdds local probe adoption support: introduces a Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@src/extensions/endpoints/unadopted-probes/.mocharc.json`:
- Around line 5-8: Remove the deprecated Node flag
"experimental-specifier-resolution=node" from the "node-option" array in the
.mocharc.json for unadopted-probes so tests run on Node 22; leave the
"loader=ts-node/esm" entry (or remove the entire "node-option" array if nothing
else is needed) and ensure any custom CommonJS resolution is handled by a proper
ESM loader instead of that flag.
In `@src/extensions/endpoints/unadopted-probes/package.json`:
- Line 3: The package.json currently contains a placeholder description value
("Please enter a description for your extension") which must be replaced with a
meaningful description; open the package.json and update the "description" field
to a concise, accurate summary of the extension's purpose and functionality
(replace the placeholder string in the "description" property) so the manifest
conveys what the unadopted-probes extension does.
In `@src/extensions/endpoints/unadopted-probes/src/index.ts`:
- Around line 81-108: The adoption flow currently constructs new
services.ItemsService('gp_probes', { schema: await getSchema() }) without
passing accountability and does a read-then-update which allows race conditions;
fix by constructing ItemsService with the current request accountability (pass
accountability: req.accountability) and replace the separate read + updateOne
sequence with an atomic conditional update (or transaction/row-lock) that only
sets userId when the probe is still unclaimed—e.g. perform an update where id =
probe.id AND userId IS NULL (and optionally verify
JSON_VALUE(localAdoptionServer,"$.token") = req.body.token) and check the
affected row count to determine success, then readOne only after a successful
conditional update.
In `@src/extensions/endpoints/unadopted-probes/test/index.test.ts`:
- Around line 149-156: The test currently compares the entire probeData object
(probeData) to the response body for the GET '/valid-token-123' request, but the
endpoint only returns a subset of fields (country, city, network, ip); update
the assertion to stub/expect only those selected fields instead of the full
probeData. Locate the test case in index.test.ts (the "should return probe
details if token matches" test), replace the full-body equality check with an
assertion that res.body has only the keys country, city, network, ip and that
their values match the corresponding values from probeData (or construct an
expected object with just those four fields) so accidental data exposure is not
masked. Ensure you still assert status 200 and use the existing clientIp header
setup.
- Around line 11-20: The linter flags the intentional then property on the test
stub (dbStub.then), so leave the then stub as-is but add a Biome suppression
comment directly above the then property declaration to silence
lint/suspicious/noThenProperty; locate the dbStub object (symbol: dbStub) and
add the exact comment "// biome-ignore lint/suspicious/noThenProperty:
intentional thenable for test stub" immediately before the "then:
sandbox.stub()," entry so tests can use dbStub.then.yields(...) without lint
errors.
In `@src/extensions/lib/src/client-ip.ts`:
- Line 6: The direct JSON.parse of process.env.SERVER_TRUSTED_PROXIES (assigned
to trustedProxies) can throw at module load and crash the app; wrap the parse in
a try/catch, validate the input is present, and on parse failure set
trustedProxies to an empty array and emit a clear warning/error that includes
the problematic env value and parse error details so startup continues safely.
🧹 Nitpick comments (4)
test/e2e/tests/probes.spec.ts (3)
204-205: FixedwaitForTimeoutmakes tests slow and potentially flaky.Instead of waiting a fixed 5 seconds, wait for the manual adoption button to become visible or enabled. This makes the test faster when the condition is met early and more reliable when it takes longer.
♻️ Proposed fix
- await page.waitForTimeout(5000); - await page.getByRole('button', { name: 'Adopt the probe manually' }).click(); + await page.getByRole('button', { name: 'Adopt the probe manually' }).waitFor({ state: 'visible' }); + await page.getByRole('button', { name: 'Adopt the probe manually' }).click();
219-220: Same fixed timeout issue as above.Replace the fixed wait with an explicit wait for the button.
♻️ Proposed fix
- await page.waitForTimeout(5000); - await page.getByRole('button', { name: 'Adopt the probe manually' }).click(); + await page.getByRole('button', { name: 'Adopt the probe manually' }).waitFor({ state: 'visible' }); + await page.getByRole('button', { name: 'Adopt the probe manually' }).click();
235-236: Same fixed timeout issue as above.Replace the fixed wait with an explicit wait for the button.
♻️ Proposed fix
- await page.waitForTimeout(5000); - await page.getByRole('button', { name: 'Adopt the probe manually' }).click(); + await page.getByRole('button', { name: 'Adopt the probe manually' }).waitFor({ state: 'visible' }); + await page.getByRole('button', { name: 'Adopt the probe manually' }).click();src/extensions/migrations/20260202-add-local-adoption-server-index.js (1)
8-9: Add a reversible down migration.Line 8-Line 9 currently does nothing. At minimum, drop the index to restore the previous schema state on rollback.
Proposed fix
-export async function down () { - console.log('There is no down operation for that migration.'); -} +export async function down (knex) { + await knex.raw(`ALTER TABLE gp_probes DROP INDEX IF EXISTS gp_probes_localadoptionserver_index;`); + console.log('Index gp_probes_localadoptionserver_index removed'); +}
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Dockerfile`:
- Around line 72-73: The Dockerfile copies the built extension named
"local-adoption" but places it into /directus/extensions/unadopted-probes/,
causing a name mismatch; update the two COPY instructions that reference the
built artifacts (the lines copying from
/builder/src/extensions/endpoints/local-adoption/dist/* and the package.json) to
use the destination folder /directus/extensions/local-adoption/ so the runtime
folder matches the package.json "name": "local-adoption".
In `@src/extensions/endpoints/local-adoption/test/index.test.ts`:
- Around line 11-20: The test's Knex mock defines a literal "then" property on
knexQueryBuilder which trips Biome's noThenProperty rule; fix by adding a scoped
disable comment immediately above the then property in the knexQueryBuilder mock
(i.e., a single-line biome/noThenProperty disable for that property) so the
linter ignores only that occurrence, or alternatively replace the stubbed then
with a Promise-based mock (e.g., have the query builder methods return
Promise.resolve(...) or a function that returns a Promise) to avoid a raw then
property; update the entry named then on knexQueryBuilder accordingly.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
Dockerfilesrc/extensions/endpoints/local-adoption/.gitignoresrc/extensions/endpoints/local-adoption/.mocharc.jsonsrc/extensions/endpoints/local-adoption/package.jsonsrc/extensions/endpoints/local-adoption/src/index.tssrc/extensions/endpoints/local-adoption/test/index.test.tssrc/extensions/endpoints/local-adoption/tsconfig.jsonsrc/extensions/endpoints/local-adoption/wallaby.js
✅ Files skipped from review due to trivial changes (1)
- src/extensions/endpoints/local-adoption/.gitignore
There was a problem hiding this comment.
🧹 Nitpick comments (1)
test/e2e/tests/probes.spec.ts (1)
204-205: ReplacewaitForTimeouthard waits with condition-based assertions.
page.waitForTimeout()results in brittle, slow tests — it forces a perfectly fixed delay that can't be interrupted. Since the "Adopt the probe manually" button is conditionally rendered after thelocal-adoptionendpoint responds, instead of hard-coding awaitForTimeout(), use a locator with a web-first assertion —expect(locator).toBeVisible()will wait for the element to be visible using Playwright's built-in retry mechanism.♻️ Proposed fix (same pattern applies to all three affected tests)
- await page.waitForTimeout(5000); - await page.getByRole('button', { name: 'Adopt the probe manually' }).click(); + await expect(page.getByRole('button', { name: 'Adopt the probe manually' })).toBeVisible(); + await page.getByRole('button', { name: 'Adopt the probe manually' }).click();Alternatively, if the button's visibility is the only condition needed before
click():- await page.waitForTimeout(5000); await page.getByRole('button', { name: 'Adopt the probe manually' }).click();Playwright's
click()auto-waits for actionability, but only when the element exists in the DOM. Since the button is conditionally rendered, the explicittoBeVisibleassertion above is the safer approach. If you want a single call that covers both,page.waitForSelectorwith a role selector also works.Note: the pre-existing
waitForTimeout(10000)at line 188 (Software probe adoption (code) test) has the same issue and is worth addressing in a follow-up.Also applies to: 219-220, 235-236
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/e2e/tests/probes.spec.ts` around lines 204 - 205, Replace brittle hard-coded waits: where the test calls page.waitForTimeout(...) immediately before clicking the "Adopt the probe manually" button, remove the waitForTimeout and assert the button's visibility using Playwright's web-first assertions (e.g., use expect(page.getByRole('button', { name: 'Adopt the probe manually' })).toBeVisible() ), then perform the click; apply the same change to the other instances that use page.waitForTimeout before interacting with that button (same pattern also applies to the similar wait before the adoption code test).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@test/e2e/tests/probes.spec.ts`:
- Around line 204-205: Replace brittle hard-coded waits: where the test calls
page.waitForTimeout(...) immediately before clicking the "Adopt the probe
manually" button, remove the waitForTimeout and assert the button's visibility
using Playwright's web-first assertions (e.g., use
expect(page.getByRole('button', { name: 'Adopt the probe manually'
})).toBeVisible() ), then perform the click; apply the same change to the other
instances that use page.waitForTimeout before interacting with that button (same
pattern also applies to the similar wait before the adoption code test).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/extensions/migrations/20260202-add-local-adoption-server-index.jstest/e2e/tests/probes.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/extensions/migrations/20260202-add-local-adoption-server-index.js
Required for jsdelivr/globalping-dash#134