Scope sub-third-parties per parent

Replace the many-to-many junction table with a direct
parent_third_party_id foreign key on third_parties. Each
sub-third-party now belongs to exactly one parent, making
duplicates across parents independent entities.

Replace the firstLevel boolean with an integer level field
(1 = direct, 2+ = parent level + 1) to support arbitrary
nesting depth.

Remove the createThirdPartyThirdPartyMapping and
deleteThirdPartyThirdPartyMapping mutations, the CLI
link/unlink commands, and the corresponding MCP tools.
Creating a child third party now just requires passing
parentThirdPartyId on the existing createThirdParty mutation.

The frontend walks the parentThirdParty chain to build
display names like "Name (Ancestor1/Ancestor2)" and shows
clickable ancestor links on the detail page.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-05-29 16:22:54 +02:00
parent ec858e58df
commit b6781d3de0
36 changed files with 1276 additions and 1192 deletions

View File

@@ -198,7 +198,7 @@ input ThirdPartyOrder
}
input ThirdPartyFilter {
firstLevel: Boolean
level: Int
query: String
}
@@ -303,7 +303,10 @@ type ThirdParty implements Node {
legalName: String
websiteUrl: String
showOnTrustCenter: Boolean!
firstLevel: Boolean!
level: Int!
parentThirdParty: ThirdParty @goField(forceResolver: true)
ancestors: [ThirdParty!]! @goField(forceResolver: true)
childThirdParties(
first: Int
@@ -506,12 +509,6 @@ extend type Mutation {
publishThirdPartyList(
input: PublishThirdPartyListInput!
): PublishThirdPartyListPayload!
createThirdPartyThirdPartyMapping(
input: CreateThirdPartyThirdPartyMappingInput!
): CreateThirdPartyThirdPartyMappingPayload!
deleteThirdPartyThirdPartyMapping(
input: DeleteThirdPartyThirdPartyMappingInput!
): DeleteThirdPartyThirdPartyMappingPayload!
}
input PublishThirdPartyListInput {
@@ -546,7 +543,7 @@ input CreateThirdPartyInput {
termsOfServiceUrl: String
businessOwnerId: ID
securityOwnerId: ID
firstLevel: Boolean
parentThirdPartyId: ID
}
input UpdateThirdPartyInput {
@@ -571,7 +568,6 @@ input UpdateThirdPartyInput {
businessOwnerId: ID @goField(omittable: true)
securityOwnerId: ID @goField(omittable: true)
showOnTrustCenter: Boolean
firstLevel: Boolean
}
input DeleteThirdPartyInput {
@@ -755,21 +751,3 @@ type CreateThirdPartyRiskAssessmentPayload {
type VetThirdPartyPayload {
thirdParty: ThirdParty!
}
input CreateThirdPartyThirdPartyMappingInput {
parentThirdPartyId: ID!
childThirdPartyId: ID!
}
type CreateThirdPartyThirdPartyMappingPayload {
thirdPartyEdge: ThirdPartyEdge!
}
input DeleteThirdPartyThirdPartyMappingInput {
parentThirdPartyId: ID!
childThirdPartyId: ID!
}
type DeleteThirdPartyThirdPartyMappingPayload {
removedThirdPartyId: ID!
}