Skip to content

feat: add device custom fields#1

Merged
danielgatis merged 8 commits intomasterfrom
feat/device-custom-fields
Apr 28, 2026
Merged

feat: add device custom fields#1
danielgatis merged 8 commits intomasterfrom
feat/device-custom-fields

Conversation

@danielgatis
Copy link
Copy Markdown
Owner

@danielgatis danielgatis commented Apr 28, 2026

Motivation

As device fleets grow, operators often need to attach context-specific metadata to individual devices — things like physical location, environment tags, asset identifiers, or team ownership. Without a dedicated mechanism, this information ends up scattered across external spreadsheets or encoded into hostnames, making it hard to query and maintain.

This feature introduces custom fields: a simple key-value map attached to each device. Operators can freely define any metadata they need, and that metadata becomes a first-class citizen in the dashboard — visible in the device list, searchable alongside the hostname, and fully manageable from the device detail page without touching the API directly.

Screen.Recording.2026-04-28.at.8.17.06.AM.mov

Summary

  • Add custom_fields map[string]string to the Device model, stored as JSONB in PostgreSQL and an embedded document in MongoDB
  • Expose via the existing PUT /devices/{uid} endpoint — nil means no change, empty map clears all fields
  • Search in the device list also queries custom field values (OR with hostname search)
  • Display and manage custom fields in the device detail page (add, delete with confirmation)
  • Add custom fields column in the devices list table

Changes

Scope Description
pkg Add CustomFields to Device model and DeviceUpdate request DTO
api Service logic, MongoDB $objectToArray filter, PostgreSQL JSONB filter, migration 002
openapi Add custom_fields to device schema and PUT request body
docker Add binutils-gold to Alpine images for ARM64 linker support (see note below)
ui (Vue) Display custom fields on device detail page
ui-react Custom fields column in list, management UI in detail page, search integration
test(api) Unit tests for PG/Mongo filters and service UpdateDevice cases
test(ui-react) Tests for devices list and device details custom fields behavior

Note: binutils-gold in Alpine Dockerfiles

During local development on an ARM64 machine (Apple Silicon), the Docker builds were failing with:

/usr/lib/gcc/x86_64-alpine-linux-musl/14.2.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find 'ld'

The root cause is that golangci-lint is compiled with -fuse-ld=gold, which requires the gold linker (binutils-gold package) to be present on the system. Alpine's default binutils does not include it. Adding binutils-gold to the Alpine images that install golangci-lint resolves the build failure on ARM64 hosts.

Test plan

  • go test ./api/services/... ./api/store/pg/internal/... ./api/store/mongo/internal/...
  • cd ui-react/apps/console && npx vitest run src/pages/devices/__tests__
  • PUT /devices/{uid} with {"name": "x", "custom_fields": {"env": "prod"}} → 200
  • GET /devices/{uid} → response includes custom_fields
  • Search on /devices by custom field value returns matching devices
  • Device detail page shows custom fields with add/delete UI

Add `CustomFields map[string]string` to the Device model (BSON/JSON tagged)
and a `*map[string]string` pointer field to DeviceUpdate so that omitting the
field in a PUT payload leaves existing values untouched.
- Service: apply CustomFields from request when non-nil; register
  "custom_fields" as a valid filter field with "contains" operator
- MongoDB: route custom_fields filter via ParseCustomFieldsFilter using
  \$objectToArray + \$regexMatch to search across all map values
- PostgreSQL entity: add JSONB column mapping to Device entity
- PostgreSQL filters: add fromCustomFieldsFilter that generates an
  EXISTS (jsonb_each_text) subquery for ILIKE value matching
- Migration 002: add custom_fields jsonb column with default '{}'
golangci-lint uses -fuse-ld=gold which requires binutils-gold on Alpine
ARM64. Without it the build fails with "cannot find 'ld'".
- Device list: new Custom Fields column showing key/value badges with
  pill styling; search also queries custom field values
- Device detail: CustomFieldsSection component with add form, inline
  delete confirmation, and useUpdateDeviceCustomFields mutation
- useDevices: extend buildFilter to OR-search across custom_fields values
- useDeviceMutations: export useUpdateDeviceCustomFields hook
- pg/internal: TestFromCustomFieldsFilter and TestParseFilterProperty_CustomFields
- mongo/internal: TestParseCustomFieldsFilter covering contains, unsupported
  operators, and non-string value error
- services: extend TestDeviceUpdate with cases for setting, clearing, and
  nil (no-op) custom fields
…fields

- Devices list: rendering, loading, empty state, row navigation, custom
  fields column (empty, single field, multiple fields), error state
- DeviceDetails: loading spinner, device data rendering, custom fields
  section display, add form, delete confirmation flow, mutation calls,
  duplicate key validation
@danielgatis danielgatis merged commit 8eecd6a into master Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant