From 3dae1733464275f646c5bb26cbf060f112423d0d Mon Sep 17 00:00:00 2001 From: Wesley Maffly-Kipp Date: Tue, 24 Feb 2026 10:35:05 -0800 Subject: [PATCH 1/2] fix: Remove hardcoded built-in edge types from UI - BED-7488 (#2402) * added is_builtin to api/v2/graph_schema/edges response to filter by in UI * added missed fields in integration tests * updated openapi definition * updated UI tests --- cmd/api/src/api/v2/graphschema_test.go | 11 +++--- cmd/api/src/database/graphschema.go | 2 +- .../database/graphschema_integration_test.go | 34 +++++++++++++++++-- cmd/api/src/model/graphschema.go | 1 + packages/go/openapi/doc/openapi.json | 3 +- .../model.graph-schema-edge-kinds.yaml | 2 ++ .../bh-shared-ui/src/components/index.ts | 2 +- .../EdgeFilter/useEdgeCategories.test.ts | 19 +++++++++-- .../Explore/ExploreSearch/EdgeFilter/utils.ts | 5 +-- .../js-client-library/src/responses.ts | 1 + 10 files changed, 64 insertions(+), 16 deletions(-) diff --git a/cmd/api/src/api/v2/graphschema_test.go b/cmd/api/src/api/v2/graphschema_test.go index bcc5e7f570d..364ff1e7bb4 100644 --- a/cmd/api/src/api/v2/graphschema_test.go +++ b/cmd/api/src/api/v2/graphschema_test.go @@ -146,7 +146,7 @@ func TestResources_ListEdgeTypes(t *testing.T) { Value: "true", }}, }, model.Sort{}, 0, 0).Return(model.GraphSchemaRelationshipKindsWithNamedSchema{ - model.GraphSchemaRelationshipKindWithNamedSchema{ID: 1, Name: "Edge_Kind_1", Description: "Edge Kind 1", IsTraversable: true, SchemaName: "extension_a"}, model.GraphSchemaRelationshipKindWithNamedSchema{ID: 2, Name: "Edge_Kind_2", Description: "Edge Kind 2", IsTraversable: true, SchemaName: "extension_a"}, model.GraphSchemaRelationshipKindWithNamedSchema{ID: 3, Name: "Edge_Kind_3", Description: "Edge Kind 3", IsTraversable: true, SchemaName: "extension_a"}, + model.GraphSchemaRelationshipKindWithNamedSchema{ID: 1, Name: "Edge_Kind_1", Description: "Edge Kind 1", IsTraversable: true, SchemaName: "extension_a", IsBuiltin: true}, model.GraphSchemaRelationshipKindWithNamedSchema{ID: 2, Name: "Edge_Kind_2", Description: "Edge Kind 2", IsTraversable: true, SchemaName: "extension_a", IsBuiltin: true}, model.GraphSchemaRelationshipKindWithNamedSchema{ID: 3, Name: "Edge_Kind_3", Description: "Edge Kind 3", IsTraversable: true, SchemaName: "extension_a", IsBuiltin: true}, }, 3, nil) }, expected: expected{ @@ -159,21 +159,24 @@ func TestResources_ListEdgeTypes(t *testing.T) { "name": "Edge_Kind_1", "description": "Edge Kind 1", "is_traversable": true, - "schema_name": "extension_a" + "schema_name": "extension_a", + "is_builtin": true }, { "id": 2, "name": "Edge_Kind_2", "description": "Edge Kind 2", "is_traversable": true, - "schema_name": "extension_a" + "schema_name": "extension_a", + "is_builtin": true }, { "id": 3, "name": "Edge_Kind_3", "description": "Edge Kind 3", "is_traversable": true, - "schema_name": "extension_a" + "schema_name": "extension_a", + "is_builtin": true }]}`, }, }} diff --git a/cmd/api/src/database/graphschema.go b/cmd/api/src/database/graphschema.go index 6a3e69d0b92..508b113724a 100644 --- a/cmd/api/src/database/graphschema.go +++ b/cmd/api/src/database/graphschema.go @@ -568,7 +568,7 @@ func (s *BloodhoundDB) GetGraphSchemaRelationshipKindsWithSchemaName(ctx context if filterAndPagination, err := parseFiltersAndPagination(relationshipKindFilters, sort, skip, limit); err != nil { return schemaRelationshipKinds, 0, err } else { - sqlStr := fmt.Sprintf(`SELECT edge.id, k.name, edge.description, edge.is_traversable, schema.name as schema_name + sqlStr := fmt.Sprintf(`SELECT edge.id, k.name, edge.description, edge.is_traversable, schema.name as schema_name, schema.is_builtin FROM %s edge JOIN %s schema ON edge.schema_extension_id = schema.id JOIN %s k ON edge.kind_id = k.id %s %s %s`, model.GraphSchemaRelationshipKind{}.TableName(), model.GraphSchemaExtension{}.TableName(), diff --git a/cmd/api/src/database/graphschema_integration_test.go b/cmd/api/src/database/graphschema_integration_test.go index 77622a54c1c..e76b7b71811 100644 --- a/cmd/api/src/database/graphschema_integration_test.go +++ b/cmd/api/src/database/graphschema_integration_test.go @@ -3609,7 +3609,8 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { if rk.Name == want.Name && rk.Description == want.Description && rk.IsTraversable == want.IsTraversable && - rk.SchemaName == want.SchemaName { + rk.SchemaName == want.SchemaName && + rk.IsBuiltin == want.IsBuiltin { // Additional validations for the found item assert.Greater(t, rk.ID, int32(0), "RelationshipKind %v - ID is invalid", rk.Name) @@ -3629,7 +3630,8 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { if rk.Name == want.Name && rk.Description == want.Description && rk.IsTraversable == want.IsTraversable && - rk.SchemaName == want.SchemaName { + rk.SchemaName == want.SchemaName && + rk.IsBuiltin == want.IsBuiltin { assert.Failf(t, "Unexpected relationship kind found", "Relationship kind %v should not be present", want.Name) } @@ -3692,6 +3694,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind2.ID, @@ -3699,6 +3702,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want3 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -3707,6 +3711,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind3.Name, Description: edgeKind3.Description, IsTraversable: edgeKind3.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } want4 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind4.ID, @@ -3714,6 +3719,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind4.Name, Description: edgeKind4.Description, IsTraversable: edgeKind4.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -3774,6 +3780,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind2.ID, @@ -3781,6 +3788,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want3 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -3789,6 +3797,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind3.Name, Description: edgeKind3.Description, IsTraversable: edgeKind3.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } want4 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind4.ID, @@ -3796,6 +3805,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind4.Name, Description: edgeKind4.Description, IsTraversable: edgeKind4.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -3844,6 +3854,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -3852,6 +3863,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -3907,6 +3919,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind2.ID, @@ -3914,6 +3927,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want3 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -3922,6 +3936,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind3.Name, Description: edgeKind3.Description, IsTraversable: edgeKind3.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } want4 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind4.ID, @@ -3929,6 +3944,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind4.Name, Description: edgeKind4.Description, IsTraversable: edgeKind4.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -3991,6 +4007,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind2.ID, @@ -3998,6 +4015,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want3 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -4006,6 +4024,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind3.Name, Description: edgeKind3.Description, IsTraversable: edgeKind3.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } want4 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind4.ID, @@ -4013,6 +4032,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind4.Name, Description: edgeKind4.Description, IsTraversable: edgeKind4.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -4068,6 +4088,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind2.ID, @@ -4075,6 +4096,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want3 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -4083,6 +4105,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind3.Name, Description: edgeKind3.Description, IsTraversable: edgeKind3.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } want4 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind4.ID, @@ -4090,6 +4113,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind4.Name, Description: edgeKind4.Description, IsTraversable: edgeKind4.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -4145,6 +4169,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind2.ID, @@ -4152,6 +4177,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want3 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -4160,6 +4186,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind3.Name, Description: edgeKind3.Description, IsTraversable: edgeKind3.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } want4 := model.GraphSchemaRelationshipKindWithNamedSchema{ ID: edgeKind4.ID, @@ -4167,6 +4194,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind4.Name, Description: edgeKind4.Description, IsTraversable: edgeKind4.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Validate edge kinds are as expected @@ -4264,6 +4292,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind1.Name, Description: edgeKind1.Description, IsTraversable: edgeKind1.IsTraversable, + IsBuiltin: extensionA.IsBuiltin, } want2 := model.GraphSchemaRelationshipKindWithNamedSchema{ @@ -4272,6 +4301,7 @@ func TestDatabase_GetGraphSchemaRelationshipKindsWithSchemaName(t *testing.T) { Name: edgeKind2.Name, Description: edgeKind2.Description, IsTraversable: edgeKind2.IsTraversable, + IsBuiltin: extensionB.IsBuiltin, } // Assert total is as expected diff --git a/cmd/api/src/model/graphschema.go b/cmd/api/src/model/graphschema.go index 42616e7fd56..f695be3fffb 100644 --- a/cmd/api/src/model/graphschema.go +++ b/cmd/api/src/model/graphschema.go @@ -224,6 +224,7 @@ type GraphSchemaRelationshipKindWithNamedSchema struct { Description string `json:"description"` IsTraversable bool `json:"is_traversable"` SchemaName string `json:"schema_name"` + IsBuiltin bool `json:"is_builtin"` } type GraphSchemaRelationshipKindsWithNamedSchema []GraphSchemaRelationshipKindWithNamedSchema diff --git a/packages/go/openapi/doc/openapi.json b/packages/go/openapi/doc/openapi.json index 6650236dbb3..606342f558a 100644 --- a/packages/go/openapi/doc/openapi.json +++ b/packages/go/openapi/doc/openapi.json @@ -22337,7 +22337,8 @@ "Groups", "Data Quality", "Datapipe", - "Cypher" + "Cypher", + "OpenGraph" ] }, { diff --git a/packages/go/openapi/src/schemas/model.graph-schema-edge-kinds.yaml b/packages/go/openapi/src/schemas/model.graph-schema-edge-kinds.yaml index 5206a7c9f2a..3ba37c61a29 100644 --- a/packages/go/openapi/src/schemas/model.graph-schema-edge-kinds.yaml +++ b/packages/go/openapi/src/schemas/model.graph-schema-edge-kinds.yaml @@ -27,3 +27,5 @@ properties: type: boolean schema_name: type: string + is_builtin: + type: boolean diff --git a/packages/javascript/bh-shared-ui/src/components/index.ts b/packages/javascript/bh-shared-ui/src/components/index.ts index c5d39e9c5d2..d82c283a010 100644 --- a/packages/javascript/bh-shared-ui/src/components/index.ts +++ b/packages/javascript/bh-shared-ui/src/components/index.ts @@ -33,9 +33,9 @@ export { default as CollectorCard } from './CollectorCard'; export * from './CollectorCardList'; export { default as CollectorCardList } from './CollectorCardList'; export * from './ColumnHeaders'; -export * from './ConditionalTooltip'; export * from './CommunityIcon'; export { default as CommunityIcon } from './CommunityIcon'; +export * from './ConditionalTooltip'; export * from './ConfirmationDialog'; export { default as ConfirmationDialog } from './ConfirmationDialog'; export * from './CreateMenu'; diff --git a/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.test.ts b/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.test.ts index 85971cdf557..63426af016b 100644 --- a/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.test.ts +++ b/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/useEdgeCategories.test.ts @@ -13,6 +13,7 @@ // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 +import { EdgeType } from 'js-client-library'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { renderHook, waitFor } from '../../../../test-utils'; @@ -127,13 +128,14 @@ const createOpenGraphFeatureFlag = (enabled: boolean = false) => { }; // all these edges are traversable and should be present in the OpenGraph edge category -const edgeSchemaA = [ +const edgeSchemaA: EdgeType[] = [ { id: 1, name: 'SchemaA_EdgeA', description: '', is_traversable: true, schema_name: 'SchemaA', + is_builtin: false, }, { id: 2, @@ -141,6 +143,7 @@ const edgeSchemaA = [ description: '', is_traversable: true, schema_name: 'SchemaA', + is_builtin: false, }, { id: 3, @@ -148,6 +151,7 @@ const edgeSchemaA = [ description: '', is_traversable: true, schema_name: 'SchemaA', + is_builtin: false, }, { id: 4, @@ -155,6 +159,7 @@ const edgeSchemaA = [ description: '', is_traversable: true, schema_name: 'SchemaA', + is_builtin: false, }, { id: 5, @@ -162,17 +167,19 @@ const edgeSchemaA = [ description: '', is_traversable: true, schema_name: 'SchemaA', + is_builtin: false, }, ]; // this group of edges includes two non-traversable edges that should not appear in the resulting OpenGraph category -const edgeSchemaB = [ +const edgeSchemaB: EdgeType[] = [ { id: 6, name: 'SchemaB_EdgeA', description: '', is_traversable: true, schema_name: 'SchemaB', + is_builtin: false, }, { id: 7, @@ -180,6 +187,7 @@ const edgeSchemaB = [ description: '', is_traversable: true, schema_name: 'SchemaB', + is_builtin: false, }, { id: 8, @@ -187,6 +195,7 @@ const edgeSchemaB = [ description: '', is_traversable: true, schema_name: 'SchemaB', + is_builtin: false, }, { id: 9, @@ -194,6 +203,7 @@ const edgeSchemaB = [ description: '', is_traversable: false, schema_name: 'SchemaB', + is_builtin: false, }, { id: 10, @@ -201,17 +211,19 @@ const edgeSchemaB = [ description: '', is_traversable: false, schema_name: 'SchemaE', + is_builtin: false, }, ]; // these edges are part of the built-in schema and should be overridden by the hardcoded edge categories in edgeCategories.ts -const edgeSchemaBuiltin = [ +const edgeSchemaBuiltin: EdgeType[] = [ { id: 11, name: 'AdEdge_ShouldntAppear', description: '', is_traversable: true, schema_name: 'ad', + is_builtin: true, }, { id: 12, @@ -219,5 +231,6 @@ const edgeSchemaBuiltin = [ description: '', is_traversable: true, schema_name: 'az', + is_builtin: true, }, ]; diff --git a/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/utils.ts b/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/utils.ts index 95028140a5e..8765c2d4e68 100644 --- a/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/utils.ts +++ b/packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/EdgeFilter/utils.ts @@ -16,9 +16,6 @@ import { EdgeType } from 'js-client-library'; import { BUILTIN_EDGE_CATEGORIES, Category, Subcategory } from './edgeCategories'; -// these are the schema types that should be ignored from the API in favor of our built-in categories -export const BUILTIN_TYPES = ['ad', 'az']; - /** * Maps from our API EdgeType format to a single Category type that can be consumed by our edge filter dialog */ @@ -48,7 +45,7 @@ export const mapEdgeTypesToCategory = (edgeTypes: EdgeType[], categoryName: stri * Removes all built-in and non-traversable edges from a list of edge types */ export const filterUnneededTypes = (data: EdgeType[] | undefined): EdgeType[] | undefined => { - return data?.filter((edge) => !BUILTIN_TYPES.includes(edge.schema_name) && edge.is_traversable); + return data?.filter((edge) => !edge.is_builtin && edge.is_traversable); }; /** diff --git a/packages/javascript/js-client-library/src/responses.ts b/packages/javascript/js-client-library/src/responses.ts index 277826c5f3b..8f2da363140 100644 --- a/packages/javascript/js-client-library/src/responses.ts +++ b/packages/javascript/js-client-library/src/responses.ts @@ -323,6 +323,7 @@ export type EdgeType = { description: string; is_traversable: boolean; schema_name: string; + is_builtin: boolean; }; export type GetEdgeTypesResponse = BasicResponse; From 630890c184ee0cb6338b0c91c5da31cd764495d5 Mon Sep 17 00:00:00 2001 From: Ben Waples Date: Wed, 25 Feb 2026 12:13:47 -0800 Subject: [PATCH 2/2] chore: migrate GM selectors to PZ BED-7388 (#2417) --- .../database/migration/migrations/v8.7.0.sql | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/cmd/api/src/database/migration/migrations/v8.7.0.sql b/cmd/api/src/database/migration/migrations/v8.7.0.sql index d1b033af4d5..7e264e93225 100644 --- a/cmd/api/src/database/migration/migrations/v8.7.0.sql +++ b/cmd/api/src/database/migration/migrations/v8.7.0.sql @@ -146,3 +146,50 @@ CREATE TABLE IF NOT EXISTS schema_list_findings ( CREATE INDEX IF NOT EXISTS idx_schema_list_findings_extension_id ON schema_list_findings (schema_extension_id); CREATE INDEX IF NOT EXISTS idx_schema_list_findings_environment_id ON schema_list_findings(environment_id); + +-- Drop unique name constraint before migrating to PZ in case AGT names are not unique +ALTER TABLE IF EXISTS asset_group_tag_selectors DROP CONSTRAINT IF EXISTS asset_group_tag_selectors_unique_name_asset_group_tag; + +-- Remigrate old custom AGI selectors to PZ selectors for any instances without PZ feature flag enabled +DO $$ + BEGIN + IF + (SELECT enabled FROM feature_flags WHERE key = 'tier_management_engine') = false + THEN + -- Delete custom selectors + DELETE FROM asset_group_tag_selectors WHERE is_default = false AND asset_group_tag_id IN ((SELECT id FROM asset_group_tags WHERE position = 1), (SELECT id FROM asset_group_tags WHERE type = 3)); + + -- Re-Migrate existing Tier Zero selectors + WITH inserted_selector AS ( + INSERT INTO asset_group_tag_selectors (asset_group_tag_id, created_at, created_by, updated_at, updated_by, name, description, is_default, allow_disable, auto_certify) + SELECT (SELECT id FROM asset_group_tags WHERE position = 1), current_timestamp, 'BloodHound', current_timestamp, 'BloodHound', s.name, s.selector, false, true, 2 + FROM asset_group_selectors s JOIN asset_groups ag ON ag.id = s.asset_group_id + WHERE ag.tag = 'admin_tier_0' and NOT EXISTS(SELECT 1 FROM asset_group_tag_selectors WHERE name = s.name) + RETURNING id, description + ) + INSERT INTO asset_group_tag_selector_seeds (selector_id, type, value) SELECT id, 1, description FROM inserted_selector; + + -- Re-Migrate existing Owned selectors + WITH inserted_selector AS ( + INSERT INTO asset_group_tag_selectors (asset_group_tag_id, created_at, created_by, updated_at, updated_by, name, description, is_default, allow_disable, auto_certify) + SELECT (SELECT id FROM asset_group_tags WHERE type = 3), current_timestamp, 'BloodHound', current_timestamp, 'BloodHound', s.name, s.selector, false, true, 0 + FROM asset_group_selectors s JOIN asset_groups ag ON ag.id = s.asset_group_id + WHERE ag.tag = 'owned' and NOT EXISTS(SELECT 1 FROM asset_group_tag_selectors WHERE name = s.name) + RETURNING id, description + ) + INSERT INTO asset_group_tag_selector_seeds (selector_id, type, value) SELECT id, 1, description FROM inserted_selector; + END IF; + END; +$$; + +-- Before we add unique constraint, rename any duplicates with `_X` to prevent constraint failing +WITH duplicate_selectors AS ( + SELECT id, name, asset_group_tag_id, ROW_NUMBER() OVER (PARTITION BY name, asset_group_tag_id ORDER BY id) AS rowNumber + FROM asset_group_tag_selectors +) +UPDATE asset_group_tag_selectors agts +SET name = agts.name || '_' || rowNumber FROM duplicate_selectors +WHERE agts.id = duplicate_selectors.id AND duplicate_selectors.rowNumber > 1; + +-- Reinstate unique constraint for asset group tag selectors name per asset group tag +ALTER TABLE IF EXISTS asset_group_tag_selectors ADD CONSTRAINT asset_group_tag_selectors_unique_name_asset_group_tag UNIQUE ("name",asset_group_tag_id,is_default); \ No newline at end of file