Skip to content

feat(reconciler): add GraphBuilder for recursive entity extraction#471

Merged
mfiedorowicz merged 8 commits into
developfrom
feat/graph-builder
Jan 26, 2026
Merged

feat(reconciler): add GraphBuilder for recursive entity extraction#471
mfiedorowicz merged 8 commits into
developfrom
feat/graph-builder

Conversation

@mfiedorowicz
Copy link
Copy Markdown
Member

Adds GraphBuilder for recursive entity extraction with bidirectional edge relationships and confidence-based entity matching support. Some of methods/functions not used here yet, to be utilized in next PR.

Key Features

  • Recursive Entity Extraction: GraphBuilder processes entities and all nested entities recursively, creating graph nodes and edges
  • Bidirectional Edges: Implements both relationship directions:
    • BELONGS_TO_X (e.g., Device → BELONGS_TO_SITE → Site)
    • HAS_X (e.g., Site → HAS_DEVICE → Device)
  • Confidence-Based Matching: Integration with EntityMatcher for finding existing nodes based on configurable matching rules
  • Snapshot Support: Creates snapshots of full entity data while storing only matching-relevant data in nodes
  • Duplicate Tracking: Tracks duplicate observations per ingestion request

New Packages

  • strcase: Shared string case conversion utilities (ToSnakeCase, ToUpperSnakeCase)
  • entitymatcher: Confidence-based entity matching with primary/secondary rules

🤖

Introduces GraphBuilder that recursively extracts nested entities from
protobuf messages and builds graph representations. Key features:

- Recursive entity extraction using reflection on proto messages
- Entity matching integration for deduplication via EntityMatcher
- Complete node data assembly with metadata and relationships
- Support for all NetBox entity types with proper edge relationships

Also refactors entitymatcher to use minimal Repository interface
following interface segregation principle, and applies idiomatic Go
improvements (any vs interface{}, require.NotNil for lint).
- Add strcase package with ToSnakeCase and ToUpperSnakeCase utilities
- Implement bidirectional edge relationships (BELONGS_TO/HAS)
- Use errors.Is(err, pgx.ErrNoRows) instead of string comparison
- Add error context wrapping for better debugging
- Replace interface{} with any in templates (Go 1.18+)
- Remove unused function parameters
- Use strings.Builder for string concatenation
- Pre-allocate slices with known capacity
- Fix test naming for empty string edge cases
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 23, 2026

Go test coverage

STATUS ELAPSED PACKAGE COVER PASS FAIL SKIP
🟢 PASS 1.40s github.com/netboxlabs/diode/diode-server/auth 44.7% 42 0 0
🟢 PASS 1.57s github.com/netboxlabs/diode/diode-server/auth/cli 0.0% 0 0 0
🟢 PASS 1.02s github.com/netboxlabs/diode/diode-server/authutil 82.8% 5 0 0
🟢 PASS 0.14s github.com/netboxlabs/diode/diode-server/dbstore/postgres 0.0% 0 0 0
🟢 PASS 1.09s github.com/netboxlabs/diode/diode-server/entityhash 86.7% 16 0 0
🟢 PASS 1.22s github.com/netboxlabs/diode/diode-server/entitymatcher 83.0% 80 0 0
🟢 PASS 0.23s github.com/netboxlabs/diode/diode-server/errors 0.0% 0 0 0
🟢 PASS 1.28s github.com/netboxlabs/diode/diode-server/ingester 82.7% 25 0 0
🟢 PASS 1.09s github.com/netboxlabs/diode/diode-server/matching 61.6% 17 0 0
🟢 PASS 1.06s github.com/netboxlabs/diode/diode-server/migrator 70.4% 4 0 0
🟢 PASS 4.19s github.com/netboxlabs/diode/diode-server/netboxdiodeplugin 83.6% 40 0 0
🟢 PASS 2.65s github.com/netboxlabs/diode/diode-server/reconciler 56.8% 124 0 0
🟢 PASS 1.01s github.com/netboxlabs/diode/diode-server/reconciler/applier 85.7% 1 0 0
🟢 PASS 0.10s github.com/netboxlabs/diode/diode-server/reconciler/changeset 0.0% 0 0 0
🟢 PASS 1.09s github.com/netboxlabs/diode/diode-server/reconciler/differ 63.8% 6 0 0
🟢 PASS 1.03s github.com/netboxlabs/diode/diode-server/server 85.7% 14 0 0
🟢 PASS 1.01s github.com/netboxlabs/diode/diode-server/strcase 100.0% 24 0 0
🟢 PASS 1.03s github.com/netboxlabs/diode/diode-server/telemetry 28.0% 26 0 0
🟢 PASS 1.03s github.com/netboxlabs/diode/diode-server/telemetry/otel 91.7% 25 0 0
🟢 PASS 0.15s github.com/netboxlabs/diode/diode-server/tls 0.0% 0 0 0
🟢 PASS 1.01s github.com/netboxlabs/diode/diode-server/version 100.0% 2 0 0

Total coverage: 55.4%

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70fa33abf1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread diode-server/reconciler/graph_builder.go
Comment thread diode-server/reconciler/graph_builder.go
- Add canBeNil helper to check if reflect.Value supports nil checks
- Skip scalar slice items (string, int) that would panic on IsNil
- Handle protobuf oneof fields (reflect.Interface) when extracting edges
- Unwrap interface values to process CableTermination and similar entities
- Add comprehensive tests for GraphBuilder functions
- Add tests for generated entity_mappings.go functions
- Improve reconciler package coverage from 38% to 57%
- Cover edge type generation, entity creation, and node operations
Add assertion to verify the entity type matches expected value when
the result is non-nil.
Copy link
Copy Markdown
Contributor

@paulstuart paulstuart left a comment

Choose a reason for hiding this comment

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

LGTM

… functions

Break up the 212-line processEntityRecursively function into smaller,
single-responsibility functions:

- findEntityMatch: confidence-based matching attempt
- updateMatchedNode: handle matched node updates
- maybeUpdateNodeSchema: lazy schema migration check
- upsertMatchedNodeData: duplicate count handling
- createEdgesForNode: edge extraction and creation

The main function is now ~62 lines and delegates to focused helpers,
improving readability and testability.
Comment thread diode-server/strcase/strcase_test.go Outdated
Comment thread diode-server/cmd/protograph/parser.go Outdated
Copy link
Copy Markdown
Contributor

@jajeffries jajeffries left a comment

Choose a reason for hiding this comment

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

Overall looks good, couple of minor comments, but happy for them to be tidied up later

- Move snakeToPascal from cmd/protograph/parser.go to strcase package
  as ToPascalCase for reusability
- Improve ToSnakeCase to handle acronyms correctly:
  "IPAddress" -> "ip_address", "DeviceID" -> "device_id"
- Simplify ToUpperSnakeCase to reuse ToSnakeCase
- NewGraphBuilder now delegates to NewGraphBuilderWithMatcher
- Rename shadowed variable in extractEdgeProperties (fieldValue -> fv)
@mfiedorowicz mfiedorowicz merged commit 343b589 into develop Jan 26, 2026
8 checks passed
@mfiedorowicz mfiedorowicz deleted the feat/graph-builder branch January 26, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants