Skip to content

feat: Add KindInfo to api/v2/nodes/node_id - BED-8762#2982

Merged
brandonshearin merged 12 commits into
mainfrom
BED-8762
Jul 14, 2026
Merged

feat: Add KindInfo to api/v2/nodes/node_id - BED-8762#2982
brandonshearin merged 12 commits into
mainfrom
BED-8762

Conversation

@brandonshearin

@brandonshearin brandonshearin commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

  • Parse out query param and add KindInfoView to handlers/node.go
  • Wire up GetNode() service to pull KindInfos
  • Swagger documentation updated with examples

Motivation and Context

Resolves BED-8762
We want to extend the existing api/v2/nodes/{node_id} endpoint with a query param named include-info. When set to true, it pulls the associated entity panels for the node's kinds.

How Has This Been Tested?

You can see the API shape in the Swagger UI.

  1. Load some graph data into the app.
  2. Pick a node.
  3. Populate the schema_node_kind table with a few records that correspond to a kind on the node you picked in step 2.
  4. Ping the endpoint! Either through the API explorer or any other API tool.

Screenshots (optional):

Example API response:
Screenshot 2026-07-10 at 3 05 34 PM

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

Summary by CodeRabbit

  • New Features
    • GET /api/v2/nodes/{node_id} can now optionally include per-kind “info” metadata (with markdown content) when include-info=true.
  • Bug Fixes
    • Malformed include-info values now return HTTP 400.
    • Returned kind info is sorted deterministically (position/title) for consistent responses.
  • Documentation
    • Updated the OpenAPI spec for GET /api/v2/nodes/{node_id} to document the new optional response shape and examples.

@brandonshearin brandonshearin self-assigned this Jul 10, 2026
@brandonshearin brandonshearin added documentation Improvements or additions to documentation api A pull request containing changes affecting the API code. go Pull requests that update go code labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The node retrieval path accepts an optional include-info query parameter, loads kind information by kind name when requested, sorts it, and renders it with decoded markdown content.

Changes

Node kind-information persistence

Layer / File(s) Summary
Kind-info persistence contract and query
server/graphdb/internal/services/services.go, server/graphdb/internal/appdb/kindinfo.go, server/graphdb/internal/services/mocks/database.go
The database interface, SQL query, and mock support retrieval by kind name.

Conditional node aggregation

Layer / File(s) Summary
Conditional service aggregation
server/graphdb/internal/services/node.go, server/graphdb/internal/services/node_test.go
Service.GetNode conditionally retrieves kind information and sorts it by position, title, and node-kind ID; tests cover population and ordering.

HTTP query and response rendering

Layer / File(s) Summary
HTTP query and response rendering
server/graphdb/internal/handlers/handlers.go, server/graphdb/internal/handlers/node.go, server/graphdb/internal/handlers/mocks/graphdb.go, server/graphdb/internal/handlers/node_test.go
The handler validates and forwards include-info, optionally renders kind information and markdown, and tests enabled, omitted, and malformed values.

Node response API contract

Layer / File(s) Summary
OpenAPI node response contract
packages/go/openapi/doc/openapi.json
The specification documents the query parameter, polymorphic responses, kind-information schemas, markdown schemas, and examples.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GetNodeByID
  participant Service.GetNode
  participant Database.GetKindInfos
  Client->>GetNodeByID: Request node with include-info=true
  GetNodeByID->>Service.GetNode: GetNode(id, true)
  Service.GetNode->>Database.GetKindInfos: Fetch kind information by name
  Database.GetKindInfos-->>Service.GetNode: Return kind information
  Service.GetNode-->>GetNodeByID: Return node with KindInfos
  GetNodeByID-->>Client: Return NodeView with info
Loading

Possibly related PRs

Suggested labels: enhancement, dbmigration

Suggested reviewers: lawsonwillard, seanjso, stephanieslamb, superlinkx, urangel

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding KindInfo support to the node endpoint.
Description check ✅ Passed The description covers the change, motivation, testing, screenshots, type, and links the associated ticket.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch BED-8762

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
server/graphdb/internal/services/node.go (2)

76-76: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wrap the underlying error with %w to preserve the error chain.

fmt.Errorf("failed to get kind info for %s", nodeKind.Name) discards the original error from GetKindInfos. This impairs debugging and prevents errors.Is/errors.As from matching any sentinel errors the DB layer might return.

🔧 Proposed fix
-			return Node{}, fmt.Errorf("failed to get kind info for %s", nodeKind.Name)
+			return Node{}, fmt.Errorf("failed to get kind info for %s: %w", nodeKind.Name, err)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/internal/services/node.go` at line 76, Update the error return
in the GetKindInfos handling within the node service to capture the underlying
error and wrap it with %w, while retaining nodeKind.Name in the contextual
message so errors.Is and errors.As can traverse the original error.

69-80: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider batching GetKindInfos calls to avoid sequential per-kind DB round-trips.

Each kind triggers a separate GetKindInfos query. For nodes with many kinds, this adds latency on the request path. A batch query accepting multiple kind names would reduce this to a single round-trip.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/internal/services/node.go` around lines 69 - 80, Replace the
sequential per-kind GetKindInfos calls in the node processing loop with a
batched database call that accepts all non-nil nodeKind.Name values and returns
their kind information in one round-trip. Preserve the existing filtering and
error context, then append the batched results to allKindInfos.
server/graphdb/internal/services/node_test.go (1)

164-164: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Deriving includeKindInfo from wantResult.KindInfos limits test coverage.

This coupling means the test can't verify the includeKindInfo=true path when no kind infos are found (empty result). Consider adding an explicit includeKindInfo field to the test struct for independent control.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/internal/services/node_test.go` at line 164, The GetNode test
currently derives includeKindInfo from wantResult.KindInfos, preventing coverage
of the true path with empty kind information. Add an explicit includeKindInfo
field to the test cases and pass it to svc.GetNode, updating existing cases to
preserve their intended behavior and adding coverage for includeKindInfo=true
with no kind infos.
server/graphdb/internal/handlers/node.go (1)

98-106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

buildMarkdownView silently swallows unmarshal errors.

Returning an empty MarkdownView{} on failure is reasonable for graceful degradation, but a log at debug/warn level would help diagnose malformed KindInfo.Content in the database without affecting the response.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/internal/handlers/node.go` around lines 98 - 106, Add debug-
or warn-level logging in buildMarkdownView when json.Unmarshal fails, including
the unmarshal error and enough context to identify the malformed content, while
preserving the existing graceful fallback of returning an empty MarkdownView{}.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/graphdb/internal/appdb/kindinfo.go`:
- Line 89: Update the OrderBy call in the relevant query builder to qualify both
sort columns with the ki. alias, using ki.position and ki.title, consistent with
the existing SELECT expressions.

In `@server/graphdb/internal/handlers/node_test.go`:
- Around line 103-105: Cover the nil NodeKindID case in the test fixture and add
an assertion for the resulting node view. Update BuildNodeView to check
kindInfo.NodeKindID before dereferencing it, handling nil safely while
preserving the existing behavior for non-nil IDs.

In `@server/graphdb/internal/handlers/node.go`:
- Line 89: Guard against nil KindInfo.NodeKindID values in the service-layer
merge logic before entries reach the handler; filter out relationship kind infos
with nil IDs. Verify the handler’s NodeKindID conversion is only performed for
entries passing this guard.

In `@server/graphdb/internal/services/node_test.go`:
- Around line 60-126: The commented-out cases in the node test table leave error
and edge-case behavior untested. Update their mocks and expectations to match
the current GetNode signature, then re-enable tests for ErrNodeNotFound,
unexpected database errors, single-kind nodes, and mixed resolved/unresolved
kinds; remove them only if equivalent replacement coverage exists.

In `@server/graphdb/internal/services/node.go`:
- Around line 82-90: Prevent nil NodeKindID dereferences in both the
allKindInfos sorting logic and the downstream handler conversion. Filter out
KindInfo entries with nil NodeKindID before sorting in the relevant node service
function, and ensure handlers/node.go only converts validated non-nil IDs,
preserving valid node kinds while avoiding relationship-kind entries.

---

Nitpick comments:
In `@server/graphdb/internal/handlers/node.go`:
- Around line 98-106: Add debug- or warn-level logging in buildMarkdownView when
json.Unmarshal fails, including the unmarshal error and enough context to
identify the malformed content, while preserving the existing graceful fallback
of returning an empty MarkdownView{}.

In `@server/graphdb/internal/services/node_test.go`:
- Line 164: The GetNode test currently derives includeKindInfo from
wantResult.KindInfos, preventing coverage of the true path with empty kind
information. Add an explicit includeKindInfo field to the test cases and pass it
to svc.GetNode, updating existing cases to preserve their intended behavior and
adding coverage for includeKindInfo=true with no kind infos.

In `@server/graphdb/internal/services/node.go`:
- Line 76: Update the error return in the GetKindInfos handling within the node
service to capture the underlying error and wrap it with %w, while retaining
nodeKind.Name in the contextual message so errors.Is and errors.As can traverse
the original error.
- Around line 69-80: Replace the sequential per-kind GetKindInfos calls in the
node processing loop with a batched database call that accepts all non-nil
nodeKind.Name values and returns their kind information in one round-trip.
Preserve the existing filtering and error context, then append the batched
results to allKindInfos.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: cc451e67-3e68-40f5-8856-e55d00483624

📥 Commits

Reviewing files that changed from the base of the PR and between 9e2f82b and 61e738a.

📒 Files selected for processing (9)
  • server/graphdb/internal/appdb/kindinfo.go
  • server/graphdb/internal/handlers/handlers.go
  • server/graphdb/internal/handlers/mocks/graphdb.go
  • server/graphdb/internal/handlers/node.go
  • server/graphdb/internal/handlers/node_test.go
  • server/graphdb/internal/services/mocks/database.go
  • server/graphdb/internal/services/node.go
  • server/graphdb/internal/services/node_test.go
  • server/graphdb/internal/services/services.go

Comment thread server/graphdb/internal/appdb/kindinfo.go Outdated
Comment thread server/graphdb/internal/handlers/node_test.go
Comment thread server/graphdb/internal/handlers/node.go
Comment thread server/graphdb/internal/services/node_test.go Outdated
Comment thread server/graphdb/internal/services/node.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/go/openapi/doc/openapi.json`:
- Around line 23220-23230: Update the InfoMarkdown schema so its markdown
property and additionalProperties: false constraint are defined in the same
object schema; avoid placing the constraint in a separate allOf branch. Preserve
the InfoMarkdownContent fields, either by inlining them or merging the object
definition, so KindInfoContent and NodeDetailsWithInfo validate real markdown
payloads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 25ff9e96-5607-48fa-83b0-9c1f831256e6

📥 Commits

Reviewing files that changed from the base of the PR and between 61e738a and a528fbc.

📒 Files selected for processing (3)
  • packages/go/openapi/doc/openapi.json
  • server/graphdb/internal/handlers/node.go
  • server/graphdb/internal/handlers/node_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/graphdb/internal/handlers/node_test.go
  • server/graphdb/internal/handlers/node.go

Comment thread packages/go/openapi/doc/openapi.json Outdated
@brandonshearin brandonshearin changed the title feat: Add KindInfo to api/v2/nodes/node_id: BED-8762 feat: Add KindInfo to api/v2/nodes/node_id - BED-8762 Jul 10, 2026
@coderabbitai coderabbitai Bot added the infrastructure A pull request containing changes affecting the infrastructure code. label Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server/graphdb/internal/services/node_test.go (1)

54-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit includeKindInfo field and error-path test cases.

Deriving includeKindInfo from tt.wantResult.KindInfos != nil makes it impossible to test includeKindInfo=true when the expected result has nil KindInfos — precisely the scenario for GetKindInfos error propagation. The GetNode implementation has two untested error paths: GetNodeKindsByNames failures and GetKindInfos failures.

Note: the GetKindInfos error wrapping in node.go uses fmt.Errorf("failed to get kind info for %s", nodeKind.Name) without %w, so errors.Is won't match the original error. Adding the test below would expose this — the wrapping should use %w to preserve the error chain.

♻️ Proposed refactor
 	tests := []struct {
 		name       string
 		setupMock  func(databaseMock *mocks.MockDatabase)
 		wantResult services.Node
 		wantErr    error
+		includeKindInfo bool
 	}{
-		result, err := svc.GetNode(ctx, nodeID, tt.wantResult.KindInfos != nil)
+		result, err := svc.GetNode(ctx, nodeID, tt.includeKindInfo)

Add these test cases to cover the missing error paths:

+		{
+			name: "error_-_propagates_get_node_kinds_by_names_error",
+			setupMock: func(databaseMock *mocks.MockDatabase) {
+				databaseMock.EXPECT().GetNode(ctx, nodeID).Return(baseNode, nil)
+				databaseMock.EXPECT().GetNodeKindsByNames(ctx, []string{"User", "Group"}).Return(nil, unexpectedErr)
+			},
+			wantErr: unexpectedErr,
+		},
+		{
+			name: "error_-_propagates_get_kind_infos_error",
+			setupMock: func(databaseMock *mocks.MockDatabase) {
+				databaseMock.EXPECT().GetNode(ctx, nodeID).Return(baseNode, nil)
+				databaseMock.EXPECT().GetNodeKindsByNames(ctx, []string{"User", "Group"}).Return(resolvedKinds, nil)
+				databaseMock.EXPECT().GetKindInfos(ctx, "User").Return(nil, unexpectedErr)
+			},
+			wantErr:          unexpectedErr,
+			includeKindInfo:  true,
+		},

Also applies to: 164-164

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/graphdb/internal/services/node_test.go` around lines 54 - 59, Add an
explicit includeKindInfo boolean to the node test table and use it when calling
GetNode, rather than deriving it from wantResult.KindInfos. Add cases covering
GetNodeKindsByNames and GetKindInfos failures, including the scenario where
includeKindInfo is true but expected KindInfos is nil. Update GetKindInfos error
wrapping in GetNode to use %w so errors.Is preserves the original error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/graphdb/internal/services/node_test.go`:
- Around line 54-59: Add an explicit includeKindInfo boolean to the node test
table and use it when calling GetNode, rather than deriving it from
wantResult.KindInfos. Add cases covering GetNodeKindsByNames and GetKindInfos
failures, including the scenario where includeKindInfo is true but expected
KindInfos is nil. Update GetKindInfos error wrapping in GetNode to use %w so
errors.Is preserves the original error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 0b1d9293-0129-4f18-855b-d81fe7d5f577

📥 Commits

Reviewing files that changed from the base of the PR and between a528fbc and 638a681.

📒 Files selected for processing (1)
  • server/graphdb/internal/services/node_test.go

@coderabbitai coderabbitai Bot removed the infrastructure A pull request containing changes affecting the infrastructure code. label Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/go/openapi/doc/openapi.json (1)

23066-23230: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Inline InfoMarkdown before closing the schema packages/go/openapi/doc/openapi.json: InfoMarkdown still wraps InfoMarkdownContent in a separate allOf branch with additionalProperties: false, so markdown is treated as an extra property. Inline the content into the same object or switch to unevaluatedProperties: false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/go/openapi/doc/openapi.json` around lines 23066 - 23230, Fix the
InfoMarkdown schema so its inherited markdown field is not rejected as an extra
property: update InfoMarkdown to inline the InfoMarkdownContent fields into one
object, or replace additionalProperties: false with unevaluatedProperties: false
while preserving the existing required markdown/content structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/go/openapi/doc/openapi.json`:
- Around line 23066-23230: Fix the InfoMarkdown schema so its inherited markdown
field is not rejected as an extra property: update InfoMarkdown to inline the
InfoMarkdownContent fields into one object, or replace additionalProperties:
false with unevaluatedProperties: false while preserving the existing required
markdown/content structure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 7df9ec56-d9f0-4f61-a51e-811275e7c72a

📥 Commits

Reviewing files that changed from the base of the PR and between 638a681 and 0cf5635.

📒 Files selected for processing (1)
  • packages/go/openapi/doc/openapi.json

@seanjSO

seanjSO commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

does this still need openapi doc updates?

@brandonshearin

Copy link
Copy Markdown
Contributor Author

@seanjSO i don't think so

@seanjSO seanjSO left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm 🚀

@LawsonWillard

Copy link
Copy Markdown
Contributor

Just a heads up, I think we will want to update the open api docs, it didnt include the kind info object in the original implementation.

// GetKindInfos returns all KindInfo's associated with the given kind name,
// ordered by position then title. An empty slice is returned when no rows match.
func (s *Store) GetKindInfos(ctx context.Context, kindID int32) ([]services.KindInfo, error) {
func (s *Store) GetKindInfos(ctx context.Context, kindName string) ([]services.KindInfo, error) {

@LawsonWillard LawsonWillard Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not blocking: Any reason why we are using name instead of the kind id?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes -- i waffled between a couple approaches to this.

The reason I picked kind name: in order to avoid a GetKindInfos() method signature that had nullable *nodeKindID, *relationshipKindID parameters.

More background: The consumer of this function is services.GetNode. That caller does not have a dawgs ID in scope, but it does have a 1. kind name or 2. schema_node_kind ID. Either would work, but the latter would require the services.GetRelationship function to pass its ID into a different slot, which was too messy.

@brandonshearin

Copy link
Copy Markdown
Contributor Author

Just a heads up, I think we will want to update the open api docs, it didnt include the kind info object in the original implementation.

@LawsonWillard I updated the docs -- the response definition and examples include kind infos. is there something else i left out?

@brandonshearin brandonshearin merged commit a1c6fc2 into main Jul 14, 2026
12 checks passed
@brandonshearin brandonshearin deleted the BED-8762 branch July 14, 2026 18:49
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

api A pull request containing changes affecting the API code. dbmigration documentation Improvements or additions to documentation enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants