Skip to content

BED-5278: Tiering list history records #1583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 18, 2025
Merged

Conversation

AD7ZJ
Copy link
Member

@AD7ZJ AD7ZJ commented Jun 12, 2025

Description

Creates an endpoint to return history records associated with the asset group tag feature.

Motivation and Context

Resolves https://specterops.atlassian.net/browse/BED-5278

How Has This Been Tested?

Tested locally using bruno. Unit tests have also been added to verify the endpoint and database functions.

Screenshots (optional):

Types of changes

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

Checklist:

Summary by CodeRabbit

  • New Features

    • Introduced a new API endpoint to retrieve asset group tag history with support for filtering, sorting, and pagination.
    • Added comprehensive OpenAPI documentation detailing query parameters and response schemas for the history endpoint.
  • Bug Fixes

    • Enhanced validation and error handling for invalid filters and query parameters in history retrieval requests.
  • Tests

    • Added extensive tests covering filtering, sorting, pagination, and error cases for the asset group tag history API.
  • Documentation

    • Updated API specifications and documentation to include the new asset group tag history endpoint and its features.

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch 2 times, most recently from 3c6a0a6 to 92bce29 Compare June 17, 2025 21:35
Copy link
Contributor

coderabbitai bot commented Jun 17, 2025

"""

Walkthrough

A new API endpoint /api/v2/asset-group-tags/history was introduced to provide paginated and filterable history records for asset group tags. This includes backend logic for filtering, sorting, and pagination, updates to database interfaces and implementations, comprehensive tests, and full OpenAPI documentation for the new endpoint and its response schema.

Changes

Files / Group Change Summary
cmd/api/src/api/registration/v2.go Registered new GET endpoint /api/v2/asset-group-tags/history with handler and permissions.
cmd/api/src/api/v2/assetgrouptags.go Added GetAssetGroupTagHistory method for handling history retrieval with filtering and pagination; introduced AssetGroupHistoryResp struct.
cmd/api/src/api/v2/assetgrouptags_test.go Added tests for the new history endpoint, covering filters, pagination, error handling, and response validation.
cmd/api/src/database/assetgrouphistory.go Updated GetAssetGroupHistoryRecords method signature and implementation to support filtering, sorting, and pagination, returning total count.
cmd/api/src/database/assetgrouphistory_test.go Expanded tests for asset group history to cover sorting, filtering, pagination, and multiple record scenarios.
cmd/api/src/database/assetgrouptags_test.go Updated test calls to GetAssetGroupHistoryRecords to include new filter, sort, skip, and limit parameters.
cmd/api/src/database/mocks/db.go Updated mock database method GetAssetGroupHistoryRecords signature and recorder to accept new parameters and return count.
cmd/api/src/model/assetgrouphistory.go Added methods IsSortable, IsStringColumn, and ValidFilters to define sortable fields and valid filter operators for AssetGroupHistory.
packages/go/openapi/doc/openapi.json Added new API endpoint /api/v2/asset-group-tags/history with query parameters and response schema documentation.
packages/go/openapi/src/openapi.yaml Added new path referencing the asset group tags history endpoint YAML.
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml Defined the new endpoint, parameters, and standard responses in OpenAPI YAML.
packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml Added schema for paginated asset group tag history responses with detailed typed properties.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API
    participant DB

    Client->>API: GET /api/v2/asset-group-tags/history?filters...
    API->>API: Parse & validate query parameters
    API->>DB: GetAssetGroupHistoryRecords(filter, sort, skip, limit)
    DB-->>API: Return filtered, sorted, paginated records and count
    API-->>Client: 200 OK with paginated history data
Loading

Suggested labels

documentation

Suggested reviewers

  • ALCooper12
  • mistahj67

Poem

🐇
A hop and a skip through history’s gate,
Now asset tag tales are easy to relate.
With filters and pages, the stories unfold,
In JSON arrays, their journeys are told.
So query away, let the records appear—
The rabbit’s new endpoint brings all history near!
🥕📜🐰
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad0f03 and 30fcf9e.

📒 Files selected for processing (12)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory.go (2 hunks)
  • cmd/api/src/database/assetgrouphistory_test.go (1 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (13 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/model/assetgrouphistory.go (2 hunks)
  • packages/go/openapi/doc/openapi.json (2 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/go/openapi/src/openapi.yaml
  • cmd/api/src/api/registration/v2.go
  • cmd/api/src/model/assetgrouphistory.go
  • packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml
  • cmd/api/src/database/assetgrouphistory.go
  • packages/go/openapi/doc/openapi.json
  • cmd/api/src/api/v2/assetgrouptags_test.go
  • cmd/api/src/database/assetgrouptags_test.go
  • cmd/api/src/api/v2/assetgrouptags.go
  • cmd/api/src/database/mocks/db.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
cmd/api/src/database/assetgrouphistory_test.go (4)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2197-2197
Timestamp: 2025-06-17T22:37:50.192Z
Learning: In Go tests using table-driven patterns, t.Parallel() at the function level allows test functions to run in parallel with other test functions, which is generally beneficial. However, when subtests contain deferred goroutines, the individual t.Run() calls should not use t.Parallel() to avoid flakiness. The comment "Tests will be flaky if run in parallel due to use of go routine in deferred function" specifically refers to subtest parallelization, not function-level parallelization.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
🧬 Code Graph Analysis (1)
cmd/api/src/database/assetgrouphistory_test.go (3)
cmd/api/src/model/assetgrouphistory.go (4)
  • AssetGroupHistoryActionCreateSelector (35-35)
  • AssetGroupHistoryActionDeleteSelector (37-37)
  • AssetGroupHistoryActionCreateTag (28-28)
  • AssetGroupHistoryActionDeleteTag (30-30)
cmd/api/src/model/filter.go (4)
  • SQLFilter (96-99)
  • Sort (461-461)
  • AscendingSortDirection (452-452)
  • DescendingSortDirection (453-453)
packages/go/graphschema/common/common.go (1)
  • Email (67-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
🔇 Additional comments (5)
cmd/api/src/database/assetgrouphistory_test.go (5)

44-59: LGTM - Well-structured test setup and verification

The initial record creation and verification subtest follows good testing practices with comprehensive field validation and proper error handling.


61-66: LGTM - Good test data setup

The additional record creation provides comprehensive test data with different actions and asset group tag IDs, enabling thorough testing of sorting, pagination, and filtering functionality.


94-100: Limit test is well-structured but affected by temporal ordering

The pagination limit test correctly verifies both the returned record count and total row count. However, it's still affected by the same temporal ordering issue as the sorting tests.


102-108: Skip test is well-designed but affected by temporal ordering

The pagination skip test correctly verifies that the expected records are returned after skipping. However, it's still affected by the same temporal ordering issue as the other tests.


110-116: LGTM - Well-implemented SQL filter test

The SQL filter test correctly uses parameterized queries and properly verifies that filtering works as expected. This test is robust and follows good security practices.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch 3 times, most recently from 16a2652 to 6450c89 Compare June 19, 2025 00:40
@AD7ZJ AD7ZJ self-assigned this Jun 19, 2025
@AD7ZJ AD7ZJ added enhancement New feature or request api A pull request containing changes affecting the API code. labels Jun 19, 2025
@AD7ZJ AD7ZJ marked this pull request as ready for review June 19, 2025 00:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1)

17-87: Comprehensive API endpoint definition with minor formatting issues.

The OpenAPI specification correctly defines all query parameters for filtering and includes appropriate response schemas. However, there are YAML formatting issues that should be addressed.

Apply these formatting fixes:

   parameters:
-  - name: created_at
+    - name: created_at
     in: query
     required: false
     schema:
       $ref: './../schemas/api.params.predicate.filter.time.yaml'
   responses:
-    200: 
+    200:
       description: OK
       content:
         application/json:
           schema:
             $ref: './../schemas/model.asset-group-tags-history.yaml'
-        $ref: './../responses/not-found.yaml'
+      $ref: './../responses/not-found.yaml'
packages/go/openapi/doc/openapi.json (1)

17770-17820: Extract item schema and declare required properties.

The inline object under data.items could be refactored into a reusable component (e.g. model.asset-group-tag-history-item) to improve maintainability. Also consider adding a required array on model.asset-group-tags-history for count, limit, skip, and data to enforce consistent responses.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91b913b and 6450c89.

📒 Files selected for processing (12)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory_test.go (1 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (10 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/model/assetgrouphistory.go (1 hunks)
  • packages/go/openapi/doc/openapi.json (2 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml

[warning] 29-29: wrong indentation: expected 4 but found 2

(indentation)


[error] 70-70: trailing spaces

(trailing-spaces)


[warning] 83-83: wrong indentation: expected 6 but found 8

(indentation)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
🔇 Additional comments (15)
packages/go/openapi/src/openapi.yaml (1)

355-356: LGTM! Endpoint follows established patterns.

The new history endpoint is correctly placed within the asset isolation section and follows the same $ref pattern used by other endpoints in the specification.

cmd/api/src/api/registration/v2.go (1)

176-176: LGTM! Route registration follows security best practices.

The new history endpoint is properly protected with the same feature flag and permissions as other asset group tag endpoints, ensuring consistent access control.

cmd/api/src/database/assetgrouptags_test.go (1)

71-71: LGTM! Consistent database method signature updates.

All calls to GetAssetGroupHistoryRecords have been systematically updated with the new parameters for filtering, sorting, and pagination. The empty filter, ascending sort, and zero pagination values maintain the original behavior while supporting the enhanced method signature needed for the new history endpoint.

Also applies to: 224-224, 291-291, 341-341, 531-531, 585-585, 651-651, 673-673, 981-981, 995-995

cmd/api/src/api/v2/assetgrouptags.go (1)

735-774: Well-implemented paginated history endpoint.

The new GetAssetGroupTagHistory method follows established patterns in the codebase for filter validation, pagination, and error handling. The implementation correctly validates query filters against the model's allowed predicates and returns appropriate HTTP status codes for various error conditions.

cmd/api/src/model/assetgrouphistory.go (1)

68-83: Solid implementation of filter metadata methods.

The IsStringColumn and ValidFilters methods provide essential metadata for the filtering system. The operator restrictions are appropriately applied based on field types (comparison operators for dates, equality/approximate matching for strings, equality only for integers).

packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1)

1-49: Comprehensive OpenAPI schema for paginated history response.

The schema correctly defines the response structure with proper pagination metadata and detailed history record objects. Field types and formats are appropriately specified, and the structure aligns with the Go model implementation.

cmd/api/src/database/assetgrouphistory.go (2)

30-30: Updated interface signature supports enhanced querying capabilities.

The new method signature properly adds filtering, sorting, and pagination parameters needed for the history endpoint functionality.


38-76: Secure and well-structured database implementation.

The implementation correctly handles dynamic SQL construction with proper parameterization to prevent SQL injection. The logic for building WHERE, ORDER BY, LIMIT, and OFFSET clauses is sound and follows established patterns.

cmd/api/src/database/assetgrouphistory_test.go (5)

44-57: LGTM! Good test setup with comprehensive verification.

The test properly creates a record and verifies all fields are correctly stored and retrieved with the new method signature.


59-68: Excellent test coverage for multiple record scenarios.

The test creates records with different actions (DeleteSelector, CreateTag, DeleteTag) which provides good coverage for the filtering functionality that follows.


70-79: Good verification of sort functionality.

The test properly verifies both ascending and descending sort orders by checking the first and last records in each case.


81-92: Comprehensive pagination testing.

The test covers both limit and skip parameters correctly, ensuring the pagination functionality works as expected.


94-100: Well-implemented SQL filter testing.

The test uses a proper SQL filter with parameterized query (action = ?) and verifies that exactly one record matching the filter criteria is returned.

cmd/api/src/database/mocks/db.go (1)

1046-1058: Appropriate mock method signature update.

The generated mock correctly reflects the updated interface with new parameters for SQL filtering (sqlFilter model.SQLFilter), sort direction (sortDirectionAscending bool), and pagination (skip, limit int). This aligns with the enhanced functionality being added.

cmd/api/src/api/v2/assetgrouptags_test.go (1)

2255-2393: Excellent test coverage with a minor clarification needed.

This test function provides comprehensive coverage of the new asset group tag history endpoint with proper error handling, filtering, and pagination scenarios. The test structure follows established patterns and uses appropriate mock expectations.

However, I notice in the "Success" test case (line 2363) you're setting api.URIPathVariableAssetGroupTagID as a URL variable, but based on the PR summary, this endpoint appears to be for general asset group tag history records rather than being filtered by a specific tag ID. Could you clarify if this URL variable is actually used by the endpoint, or if it should be removed from this test case?

#!/bin/bash
# Description: Verify if the GetAssetGroupTagHistory endpoint uses asset group tag ID as a URL parameter

# Check the route registration for this endpoint
rg -A 5 -B 5 "GetAssetGroupTagHistory"

# Check if the endpoint path includes asset group tag ID parameter
rg "asset-group-tags.*history" 

Comment on lines +6132 to +6105
"/api/v2/asset-group-tags/history": {
"parameters": [
{
"$ref": "#/components/parameters/header.prefer"
}
],
"get": {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing security definition for the endpoint.

This new route doesn’t include a security section—unlike other v2 endpoints that enforce bearer authentication. Add a security block (e.g., bearerAuth: []) at the path or operation level to protect this API.

🤖 Prompt for AI Agents
In packages/go/openapi/doc/openapi.json around lines 6132 to 6138, the new
endpoint /api/v2/asset-group-tags/history is missing a security definition. Add
a security block with bearerAuth (e.g., "security": [{"bearerAuth": []}]) either
at the path level or inside the get operation to enforce bearer token
authentication consistent with other v2 endpoints.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
cmd/api/src/database/assetgrouphistory_test.go (3)

70-79: Consider testing sort order more reliably.

The sorting tests assume that the creation order matches the timestamp-based sort order. This could be flaky in concurrent environments or systems with low timestamp precision.

Consider explicitly verifying the timestamp order instead of relying on creation sequence:

 // verify ascending sort
-require.Equal(t, model.AssetGroupHistoryActionCreateSelector, records[0].Action)
-require.Equal(t, model.AssetGroupHistoryActionDeleteTag, records[3].Action)
+// Verify ascending order by timestamp
+for i := 1; i < len(records); i++ {
+    require.True(t, records[i-1].CreatedAt.Before(records[i].CreatedAt) || records[i-1].CreatedAt.Equal(records[i].CreatedAt))
+}

61-64: Consider using more descriptive test data.

The hardcoded asset group tag IDs (1, 2) could be made more explicit or use constants for better test readability.

-err = dbInst.CreateAssetGroupHistoryRecord(testCtx, testActor.ID.String(), testActor.EmailAddress.ValueOrZero(), testTarget, model.AssetGroupHistoryActionCreateTag, 2, null.String{}, null.String{})
+testAssetGroupTag2 := 2
+err = dbInst.CreateAssetGroupHistoryRecord(testCtx, testActor.ID.String(), testActor.EmailAddress.ValueOrZero(), testTarget, model.AssetGroupHistoryActionCreateTag, testAssetGroupTag2, null.String{}, null.String{})

95-101: Consider adding edge case testing for filtering.

The SQL filter test only covers a basic positive case. Consider adding tests for edge cases like invalid filters, empty results, or malformed SQL.

Add additional filter test cases:

// Test empty filter results
records, _, err = dbInst.GetAssetGroupHistoryRecords(testCtx, model.SQLFilter{SQLString: "action = ?", Params: []any{"NonExistentAction"}}, true, 0, 0)
require.NoError(t, err)
require.Len(t, records, 0)

// Test multiple conditions
records, _, err = dbInst.GetAssetGroupHistoryRecords(testCtx, model.SQLFilter{SQLString: "action = ? AND asset_group_tag_id = ?", Params: []any{model.AssetGroupHistoryActionCreateSelector, testAssetGroupTag}}, true, 0, 0)
require.NoError(t, err)
require.Len(t, records, 1)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6450c89 and c2d55fd.

📒 Files selected for processing (9)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory_test.go (1 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (10 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • packages/go/openapi/doc/openapi.json (2 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/go/openapi/src/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (7)
  • cmd/api/src/api/registration/v2.go
  • cmd/api/src/database/assetgrouptags_test.go
  • cmd/api/src/api/v2/assetgrouptags.go
  • cmd/api/src/database/assetgrouphistory.go
  • cmd/api/src/api/v2/assetgrouptags_test.go
  • packages/go/openapi/doc/openapi.json
  • cmd/api/src/database/mocks/db.go
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
  • GitHub Check: run-tests
🔇 Additional comments (4)
cmd/api/src/database/assetgrouphistory_test.go (4)

44-44: LGTM: Test data initialization updated correctly.

The change from DeleteSelector to CreateSelector aligns well with the expanded test structure that now tests multiple action types in sequence.


81-86: LGTM: Pagination testing is comprehensive.

The limit and total count verification properly tests the pagination functionality.


88-93: LGTM: Skip functionality tested correctly.

The skip test properly verifies that the correct records are returned when skipping the first 2 records.


47-47: Verify the new method signature is correctly implemented.

The method signature has been updated to include filtering, sorting, and pagination parameters. The new signature appears to be GetAssetGroupHistoryRecords(ctx, filter, ascending, skip, limit) returning (records, totalCount, error).

#!/bin/bash
# Description: Verify the GetAssetGroupHistoryRecords method signature matches the test usage
# Expected: Method should accept SQLFilter, bool, int, int parameters and return records, int, error

# Search for the method definition
ast-grep --pattern $'func ($DB) GetAssetGroupHistoryRecords($$$) ($$$) {
  $$$
}'

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2d55fd and f41779c.

📒 Files selected for processing (7)
  • cmd/api/src/api/v2/assetgrouptags.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory.go (2 hunks)
  • cmd/api/src/database/assetgrouphistory_test.go (1 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (13 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/model/assetgrouphistory.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • cmd/api/src/database/assetgrouptags_test.go
  • cmd/api/src/api/v2/assetgrouptags.go
  • cmd/api/src/model/assetgrouphistory.go
  • cmd/api/src/database/mocks/db.go
  • cmd/api/src/api/v2/assetgrouptags_test.go
🧰 Additional context used
🧬 Code Graph Analysis (2)
cmd/api/src/database/assetgrouphistory_test.go (2)
cmd/api/src/model/assetgrouphistory.go (4)
  • AssetGroupHistoryActionCreateSelector (32-32)
  • AssetGroupHistoryActionDeleteSelector (34-34)
  • AssetGroupHistoryActionCreateTag (28-28)
  • AssetGroupHistoryActionDeleteTag (30-30)
cmd/api/src/model/filter.go (4)
  • SQLFilter (93-96)
  • Sort (384-384)
  • AscendingSortDirection (375-375)
  • DescendingSortDirection (376-376)
cmd/api/src/database/assetgrouphistory.go (4)
cmd/api/src/model/filter.go (3)
  • SQLFilter (93-96)
  • Sort (384-384)
  • DescendingSortDirection (376-376)
cmd/api/src/model/assetgrouphistory.go (3)
  • AssetGroupHistory (38-48)
  • AssetGroupHistory (50-52)
  • AssetGroupHistoryAction (25-25)
cmd/api/src/database/db.go (1)
  • BloodhoundDB (176-179)
cmd/api/src/database/helper.go (1)
  • CheckError (26-32)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
  • GitHub Check: run-analysis
🔇 Additional comments (7)
cmd/api/src/database/assetgrouphistory_test.go (2)

44-57: Well-structured test setup with comprehensive record creation.

The test correctly creates multiple records with different actions and verifies the basic functionality. The change from DeleteSelector to CreateSelector for the initial record is consistent with the test flow.


59-101: Excellent comprehensive testing of enhanced functionality.

The test thoroughly covers all the new features:

  • Sorting in both ascending and descending order
  • Pagination with limit and skip parameters
  • SQL filtering with parameterized queries
  • Total count verification

The test scenarios are well-designed and the assertions properly validate the expected behavior for each case.

cmd/api/src/database/assetgrouphistory.go (5)

22-22: Good addition of strings import for SQL construction.

The strings package is properly imported to support the new sorting functionality.


39-49: Solid method signature and filter handling.

The enhanced method signature properly supports the new filtering, sorting, and pagination requirements. The filter handling correctly prepends "WHERE " when a filter is provided.


51-58: Well-implemented dynamic sorting logic.

The sort column construction properly handles multiple sort fields and directions, building a clean ORDER BY clause.


84-90: Efficient count query optimization.

The implementation correctly optimizes by only executing the count query when pagination is applied, using the result set length otherwise. This is a good performance consideration.


68-78: ```bash
#!/bin/bash

Show context around sortString and sqlFilter in assetgrouphistory.go

echo "=== assetgrouphistory DB file snippet ==="
sed -n '1,120p' cmd/api/src/database/assetgrouphistory.go

Locate any SortableFields definitions across models

echo "=== Searching for SortableFields definitions ==="
rg -n "SortableFields" --type go

Search for generic sort builder functions

echo "=== Searching for sort builder usage ==="
rg -n "sortString" -n cmd/api/src/database/assetgrouphistory.go
rg -n "Sort" -n cmd/api/src/database/assetgrouphistory.go


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1)

33-34: Remove stray trailing whitespace

Lines 33 and 78 contain trailing spaces, again caught by yamllint. These will break “strict” linters in CI.

-    description:␠
+    description:-    200: ␠
+    200:

Also applies to: 78-79

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c57b65 and 93773c7.

📒 Files selected for processing (2)
  • packages/go/openapi/doc/openapi.json (3 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/go/openapi/doc/openapi.json
🧰 Additional context used
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml

[warning] 29-29: wrong indentation: expected 4 but found 2

(indentation)


[error] 33-33: trailing spaces

(trailing-spaces)


[error] 78-78: trailing spaces

(trailing-spaces)


[warning] 91-91: wrong indentation: expected 6 but found 8

(indentation)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: run-tests
🔇 Additional comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1)

31-36: Clarify sort_by schema to restrict valid values

You note in the description that only created_at is sortable, but the referenced generic sort-by schema likely exposes more fields. Either tighten the schema here (e.g., enum: [created_at]) or document that only created_at is honored to avoid confusing consumers.

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch from 75072bd to 7891430 Compare June 26, 2025 21:11
Copy link
Contributor

@ALCooper12 ALCooper12 left a comment

Choose a reason for hiding this comment

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

Just a few OpenAPI clarifying questions/corrections!

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch from 7891430 to e39d446 Compare July 8, 2025 18:17
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
cmd/api/src/database/assetgrouphistory_test.go (1)

32-108: Consider adding t.Parallel() for better test performance.

Based on the retrieved learnings, integration tests can benefit from function-level parallelization unless they have specific dependencies on shared state. Since this test uses a fresh database instance from integration.SetupDB(t), it might be safe to add t.Parallel() at the function level.

Add at the beginning of the test function:

func TestDatabase_CreateAndGetAssetGroupHistory(t *testing.T) {
+	t.Parallel()
	var (
		dbInst    = integration.SetupDB(t)

Note: Only add this if the integration test setup supports parallel execution.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e39d446 and caa321d.

📒 Files selected for processing (4)
  • cmd/api/src/api/v2/assetgrouptags.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory.go (2 hunks)
  • cmd/api/src/database/assetgrouphistory_test.go (1 hunks)
  • packages/go/openapi/doc/openapi.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • cmd/api/src/database/assetgrouphistory.go
  • cmd/api/src/api/v2/assetgrouptags.go
  • packages/go/openapi/doc/openapi.json
🧰 Additional context used
🧠 Learnings (1)
cmd/api/src/database/assetgrouphistory_test.go (3)
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2197-2197
Timestamp: 2025-06-17T22:37:50.192Z
Learning: In Go tests using table-driven patterns, t.Parallel() at the function level allows test functions to run in parallel with other test functions, which is generally beneficial. However, when subtests contain deferred goroutines, the individual t.Run() calls should not use t.Parallel() to avoid flakiness. The comment "Tests will be flaky if run in parallel due to use of go routine in deferred function" specifically refers to subtest parallelization, not function-level parallelization.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
🧬 Code Graph Analysis (1)
cmd/api/src/database/assetgrouphistory_test.go (3)
cmd/api/src/model/assetgrouphistory.go (4)
  • AssetGroupHistoryActionCreateSelector (32-32)
  • AssetGroupHistoryActionDeleteSelector (34-34)
  • AssetGroupHistoryActionCreateTag (28-28)
  • AssetGroupHistoryActionDeleteTag (30-30)
cmd/api/src/model/filter.go (4)
  • SQLFilter (93-96)
  • Sort (384-384)
  • AscendingSortDirection (375-375)
  • DescendingSortDirection (376-376)
packages/go/graphschema/common/common.go (1)
  • Email (67-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: run-tests
  • GitHub Check: build-ui
🔇 Additional comments (5)
cmd/api/src/database/assetgrouphistory_test.go (5)

44-44: Consider the test setup sequence for better readability.

The initial record creation uses CreateSelector action, which aligns with the test logic. However, consider documenting why this specific action was chosen as the first record in the test sequence.


47-59: Good verification of record fields.

The subtest thoroughly verifies all expected fields of the retrieved record, including proper handling of null values and timestamp validation.


85-91: Verify limit functionality correctly.

The limit test properly verifies that when limiting to 2 records, exactly 2 records are returned while the total count remains 4. This correctly tests the pagination functionality.


93-99: Verify skip functionality with proper assertions.

The skip test correctly verifies that when skipping the first 2 records, the remaining records are returned in the expected order. The assertions check the specific actions of the remaining records.


101-107: SQL filter test uses safe parameterized queries.

The SQL filter test correctly uses parameterized queries to prevent SQL injection and properly tests the filtering functionality by action type.

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch from af8c768 to 91395d1 Compare July 17, 2025 17:37
@AD7ZJ AD7ZJ requested review from ALCooper12 and mistahj67 July 17, 2025 18:29
Copy link
Contributor

@mistahj67 mistahj67 left a comment

Choose a reason for hiding this comment

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

Testing locally works, added block for the docs update and the db method feedback

}
sortColumns = append(sortColumns, fmt.Sprintf("%s %s", item.Column, dirString))
}
sortString := "ORDER BY " + strings.Join(sortColumns, ", ")
Copy link
Contributor

Choose a reason for hiding this comment

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

This variable assumes the sortColumns slice is never empty. This likely needs to be built out similar to the skipLimitString where if nothing is supplied it will be an empty string

Copy link
Contributor

Choose a reason for hiding this comment

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

Would also benefit from a db test for the empty sortColumns argument to prevent regression as well

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good catch, thanks!

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch from 91395d1 to 7ad0f03 Compare July 17, 2025 21:47
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1)

43-45: email still missing format: email – same issue flagged previously

A prior review already requested the email format hint; it has not been applied. Add the format so generators recognise the field as an RFC-5322 email.

               email:
                 $ref: './null.string.yaml'
+                format: email
🧹 Nitpick comments (1)
packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1)

49-51: Consider specifying format: int64 for asset_group_tag_id for consistency

id is declared with format: int64; declaring asset_group_tag_id the same way ensures uniform typing across record identifiers and avoids code-gen discrepancies.

-              asset_group_tag_id:
-                type: integer
+              asset_group_tag_id:
+                type: integer
+                format: int64
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91395d1 and 7ad0f03.

📒 Files selected for processing (12)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (1 hunks)
  • cmd/api/src/database/assetgrouphistory.go (2 hunks)
  • cmd/api/src/database/assetgrouphistory_test.go (1 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (13 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/model/assetgrouphistory.go (2 hunks)
  • packages/go/openapi/doc/openapi.json (2 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/go/openapi/src/openapi.yaml
  • cmd/api/src/database/assetgrouptags_test.go
  • cmd/api/src/database/assetgrouphistory_test.go
  • cmd/api/src/database/assetgrouphistory.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.history.yaml
  • cmd/api/src/model/assetgrouphistory.go
  • cmd/api/src/api/v2/assetgrouptags.go
  • cmd/api/src/api/registration/v2.go
  • cmd/api/src/database/mocks/db.go
  • packages/go/openapi/doc/openapi.json
  • cmd/api/src/api/v2/assetgrouptags_test.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (2)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
Learnt from: elikmiller
PR: SpecterOps/BloodHound#1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
  • GitHub Check: run-tests

Copy link
Contributor

@mistahj67 mistahj67 left a comment

Choose a reason for hiding this comment

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

Code LGTM, unblocking but I'd clarify the filter vs explicit start / end parameters before 🚀

@AD7ZJ AD7ZJ force-pushed the BED-5278--Tiering-list-history-recs branch from 7ad0f03 to fef08b6 Compare July 18, 2025 20:02
@AD7ZJ AD7ZJ dismissed ALCooper12’s stale review July 18, 2025 20:33

Product and team was OK with it as-is, so lets' run with this for now :)

@AD7ZJ AD7ZJ merged commit 3e86c2a into main Jul 18, 2025
9 checks passed
@AD7ZJ AD7ZJ deleted the BED-5278--Tiering-list-history-recs branch July 18, 2025 20:34
@github-actions github-actions bot locked and limited conversation to collaborators Jul 18, 2025
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. enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants