Add importThirdPartyFromCommon GraphQL mutation

Expose the explicit import action over the console API. The mutation
takes an organization and a common third party, authorizes as a
third-party create, and delegates to ThirdPartyService.ImportFromCommon,
returning the org ThirdParty edge plus a created flag so the client can
tell a fresh import from a re-import.

Add an end-to-end test covering the two behaviours that matter: the
first import seeds the org vendor from the catalog and backfills the
linked tracker pattern's third_party_id, and a second import is
idempotent, returning the same row with created=false.

The gqlgen-generated types and execution code are build artifacts (not
tracked), so only the schema and the resolver change here.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-10 15:32:55 +02:00
parent 11c74617d0
commit e2d6972da5
3 changed files with 187 additions and 0 deletions

View File

@@ -454,6 +454,9 @@ type ThirdPartyRiskAssessmentEdge {
extend type Mutation {
createThirdParty(input: CreateThirdPartyInput!): CreateThirdPartyPayload!
importThirdPartyFromCommon(
input: ImportThirdPartyFromCommonInput!
): ImportThirdPartyFromCommonPayload!
updateThirdParty(input: UpdateThirdPartyInput!): UpdateThirdPartyPayload!
deleteThirdParty(input: DeleteThirdPartyInput!): DeleteThirdPartyPayload!
createThirdPartyContact(
@@ -672,10 +675,20 @@ input VetThirdPartyInput {
procedure: String
}
input ImportThirdPartyFromCommonInput {
organizationId: ID!
commonThirdPartyId: ID!
}
type CreateThirdPartyPayload {
thirdPartyEdge: ThirdPartyEdge!
}
type ImportThirdPartyFromCommonPayload {
thirdPartyEdge: ThirdPartyEdge!
created: Boolean!
}
type UpdateThirdPartyPayload {
thirdParty: ThirdParty!
}

View File

@@ -83,6 +83,36 @@ func (r *mutationResolver) CreateThirdParty(ctx context.Context, input types.Cre
}, nil
}
// ImportThirdPartyFromCommon is the resolver for the importThirdPartyFromCommon field.
func (r *mutationResolver) ImportThirdPartyFromCommon(ctx context.Context, input types.ImportThirdPartyFromCommonInput) (*types.ImportThirdPartyFromCommonPayload, error) {
scope, err := r.authorize(ctx, input.OrganizationID, probo.ActionThirdPartyCreate)
if err != nil {
return nil, err
}
thirdParty, created, err := r.probo.ThirdParties.ImportFromCommon(
ctx, scope,
probo.ImportThirdPartyFromCommonRequest{
OrganizationID: input.OrganizationID,
CommonThirdPartyID: input.CommonThirdPartyID,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot import thirdParty from common", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ImportThirdPartyFromCommonPayload{
ThirdPartyEdge: types.NewThirdPartyEdge(thirdParty, coredata.ThirdPartyOrderFieldName),
Created: created,
}, nil
}
// UpdateThirdParty is the resolver for the updateThirdParty field.
func (r *mutationResolver) UpdateThirdParty(ctx context.Context, input types.UpdateThirdPartyInput) (*types.UpdateThirdPartyPayload, error) {
scope, err := r.authorize(ctx, input.ID, probo.ActionThirdPartyUpdate)