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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

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

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 caa321d and 8ee792c.

📒 Files selected for processing (1)
  • packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/go/openapi/src/schemas/model.asset-group-tags-history.yaml
⏰ 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

🪧 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 +6138
"/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!

Comment on lines +31 to +76
- name: sort_by
in: query
description:
Sortable columns are `created_at`.
schema:
$ref: './../schemas/api.params.query.sort-by.yaml'
- name: created_at
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.time.yaml'
- name: actor
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.string.yaml'
- name: email
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.string.yaml'
- name: action
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.string.yaml'
- name: target
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.string.yaml'
- name: asset_group_tag_id
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.integer.yaml'
- name: environment_id
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.string.yaml'
- name: note
in: query
required: false
schema:
$ref: './../schemas/api.params.predicate.filter.string.yaml'
Copy link
Contributor

Choose a reason for hiding this comment

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

(I could be wrong) But according to your ticket: BED-5278, your inputs are supposed to be: Start Date and End Date, however the response variables are all listed as input parameters instead. I'm comparing your endpoint in the UI of the API Explorer to GET /api/v2/posture-history/{data_type} for reference

Copy link
Member Author

Choose a reason for hiding this comment

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

I implemented the Start Date and End Date requirement by doing a filter on created_at - you would pass in created_at=gt:<start time>&created_at=lt:<end time>. This is the best way I know to document the filters, I just referenced how it was done on other endpoints like /paths/asset-isolation.asset-group-tags.yaml

Comment on lines 30 to 49
id:
type: integer
created_at:
type: string
format: date-time
actor:
type: string
email:
type: string
format: email
action:
type: string
target:
type: string
asset_group_tag_id:
type: integer
environment_id:
type: integer
note:
type: string
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a few baby corrections!

Suggested change
id:
type: integer
created_at:
type: string
format: date-time
actor:
type: string
email:
type: string
format: email
action:
type: string
target:
type: string
asset_group_tag_id:
type: integer
environment_id:
type: integer
note:
type: string
id:
type: integer
format: int64
created_at:
type: string
format: date-time
actor:
type: string
email:
$ref: './null.string.yaml'
format: email
action:
type: string
target:
type: string
asset_group_tag_id:
type: integer
environment_id:
$ref: './null.string.yaml'
note:
$ref: './null.string.yaml'

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, ty! Fixed!

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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