Add collector PC->printer links and searchable custom fields
All checks were successful
CI / backend (push) Successful in 1m24s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 7s

Collector: the computers collector schema gains defaultprinter and
printers; apply_collector_payload resolves each reported identifier to
a printer asset (windowsname/hostname/sharename/assetnumber/IP,
first-hit case-insensitive) and idempotently syncs relationships -
defaultprinter (directional) for the default, connectedto for the
rest. Collector-created rows are tagged so a re-report archives dropped
links while manual relationships are never touched; unresolved
identifiers warn instead of failing. Both PC and printer detail pages
show the links via the shared relationships card (no frontend change).
GE-Enforce Win32_Printer collection snippet documented.

Searchable custom fields: a per-field searchable flag (migration 7d24);
global search matches custom-field values on flagged active fields and
routes each hit to the asset detail page, reusing the existing
(type,id) dedupe and search_<type>_enabled domain filter. Searchable
toggle on the Custom Fields settings page.

822 tests pass; both verified live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-12 14:01:20 -04:00
parent 9daf1578a7
commit 275224822e
12 changed files with 664 additions and 6 deletions

View File

@@ -0,0 +1,50 @@
"""Custom field searchable flag (customfields.searchable)
Adds a boolean customfields.searchable column (default false). When true, the
field's stored values are matched by global search and a hit routes to the
owning asset's detail page.
Idempotent guard so it is safe on a partially-migrated box; real downgrade.
Revision ID: 7d24_customfield_searchable
Revises: 7d23_user_mustchangepassword
Create Date: 2026-07-12
"""
from alembic import op
import sqlalchemy as sa
revision = '7d24_customfield_searchable'
down_revision = '7d23_user_mustchangepassword'
branch_labels = None
depends_on = None
def upgrade():
bind = op.get_bind()
insp = sa.inspect(bind)
if 'customfields' not in insp.get_table_names():
return
columns = {c['name'] for c in insp.get_columns('customfields')}
if 'searchable' in columns:
return
op.add_column(
'customfields',
sa.Column('searchable', sa.Boolean(),
nullable=False, server_default=sa.false()))
def downgrade():
bind = op.get_bind()
insp = sa.inspect(bind)
if 'customfields' not in insp.get_table_names():
return
columns = {c['name'] for c in insp.get_columns('customfields')}
if 'searchable' not in columns:
return
op.drop_column('customfields', 'searchable')