Add vendor services

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-08-18 18:26:17 +02:00
parent 0262bd1c3d
commit 54c14a1816
25 changed files with 4638 additions and 68 deletions

View File

@@ -342,6 +342,20 @@ enum VendorContactOrderField
)
}
enum VendorServiceOrderField
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.VendorServiceOrderField"
) {
CREATED_AT
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.VendorServiceOrderFieldCreatedAt"
)
NAME
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.VendorServiceOrderFieldName"
)
}
enum OrganizationOrderField
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.OrganizationOrderField"
@@ -801,6 +815,14 @@ input VendorContactOrder
field: VendorContactOrderField!
}
input VendorServiceOrder
@goModel(
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.VendorServiceOrderBy"
) {
direction: OrderDirection!
field: VendorServiceOrderField!
}
input OrganizationOrder {
direction: OrderDirection!
field: OrganizationOrderField!
@@ -1064,6 +1086,14 @@ type Vendor implements Node {
orderBy: VendorContactOrder
): VendorContactConnection! @goField(forceResolver: true)
services(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorServiceOrder
): VendorServiceConnection! @goField(forceResolver: true)
riskAssessments(
first: Int
after: CursorKey
@@ -1128,6 +1158,15 @@ type VendorContact implements Node {
updatedAt: Datetime!
}
type VendorService implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
name: String!
description: String
createdAt: Datetime!
updatedAt: Datetime!
}
type VendorDataPrivacyAgreement implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
@@ -1652,6 +1691,16 @@ type VendorContactEdge {
node: VendorContact!
}
type VendorServiceConnection {
edges: [VendorServiceEdge!]!
pageInfo: PageInfo!
}
type VendorServiceEdge {
cursor: CursorKey!
node: VendorService!
}
type ConnectorConnection {
edges: [ConnectorEdge!]!
pageInfo: PageInfo!
@@ -1796,6 +1845,11 @@ type Mutation {
updateVendorContact(input: UpdateVendorContactInput!): UpdateVendorContactPayload!
deleteVendorContact(input: DeleteVendorContactInput!): DeleteVendorContactPayload!
# Vendor Service mutations
createVendorService(input: CreateVendorServiceInput!): CreateVendorServicePayload!
updateVendorService(input: UpdateVendorServiceInput!): UpdateVendorServicePayload!
deleteVendorService(input: DeleteVendorServiceInput!): DeleteVendorServicePayload!
# Framework mutations
createFramework(input: CreateFrameworkInput!): CreateFrameworkPayload!
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
@@ -2089,6 +2143,26 @@ input DeleteVendorContactInput {
vendorContactId: ID!
}
input CreateVendorServiceInput {
vendorId: ID!
name: String!
description: String
url: String
type: String
}
input UpdateVendorServiceInput {
id: ID!
name: String
description: String
url: String
type: String
}
input DeleteVendorServiceInput {
vendorServiceId: ID!
}
input CreatePeopleInput {
organizationId: ID!
fullName: String!
@@ -2570,6 +2644,18 @@ type DeleteVendorContactPayload {
deletedVendorContactId: ID!
}
type CreateVendorServicePayload {
vendorServiceEdge: VendorServiceEdge!
}
type UpdateVendorServicePayload {
vendorService: VendorService!
}
type DeleteVendorServicePayload {
deletedVendorServiceId: ID!
}
type CreatePeoplePayload {
peopleEdge: PeopleEdge!
}

File diff suppressed because it is too large Load Diff

View File

@@ -505,6 +505,18 @@ type CreateVendorRiskAssessmentPayload struct {
VendorRiskAssessmentEdge *VendorRiskAssessmentEdge `json:"vendorRiskAssessmentEdge"`
}
type CreateVendorServiceInput struct {
VendorID gid.GID `json:"vendorId"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
URL *string `json:"url,omitempty"`
Type *string `json:"type,omitempty"`
}
type CreateVendorServicePayload struct {
VendorServiceEdge *VendorServiceEdge `json:"vendorServiceEdge"`
}
type Datum struct {
ID gid.GID `json:"id"`
Name string `json:"name"`
@@ -750,6 +762,14 @@ type DeleteVendorPayload struct {
DeletedVendorID gid.GID `json:"deletedVendorId"`
}
type DeleteVendorServiceInput struct {
VendorServiceID gid.GID `json:"vendorServiceId"`
}
type DeleteVendorServicePayload struct {
DeletedVendorServiceID gid.GID `json:"deletedVendorServiceId"`
}
type Document struct {
ID gid.GID `json:"id"`
Title string `json:"title"`
@@ -1519,6 +1539,18 @@ type UpdateVendorPayload struct {
Vendor *Vendor `json:"vendor"`
}
type UpdateVendorServiceInput struct {
ID gid.GID `json:"id"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
URL *string `json:"url,omitempty"`
Type *string `json:"type,omitempty"`
}
type UpdateVendorServicePayload struct {
VendorService *VendorService `json:"vendorService"`
}
type UploadAuditReportInput struct {
AuditID gid.GID `json:"auditId"`
File graphql.Upload `json:"file"`
@@ -1614,6 +1646,7 @@ type Vendor struct {
BusinessAssociateAgreement *VendorBusinessAssociateAgreement `json:"businessAssociateAgreement,omitempty"`
DataPrivacyAgreement *VendorDataPrivacyAgreement `json:"dataPrivacyAgreement,omitempty"`
Contacts *VendorContactConnection `json:"contacts"`
Services *VendorServiceConnection `json:"services"`
RiskAssessments *VendorRiskAssessmentConnection `json:"riskAssessments"`
BusinessOwner *People `json:"businessOwner,omitempty"`
SecurityOwner *People `json:"securityOwner,omitempty"`
@@ -1753,6 +1786,28 @@ type VendorRiskAssessmentOrder struct {
Direction page.OrderDirection `json:"direction"`
}
type VendorService struct {
ID gid.GID `json:"id"`
Vendor *Vendor `json:"vendor"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (VendorService) IsNode() {}
func (this VendorService) GetID() gid.GID { return this.ID }
type VendorServiceConnection struct {
Edges []*VendorServiceEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type VendorServiceEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *VendorService `json:"node"`
}
type Viewer struct {
ID gid.GID `json:"id"`
User *User `json:"user"`

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/page"
)
type (
VendorServiceOrderBy OrderBy[coredata.VendorServiceOrderField]
)
func NewVendorServiceConnection(p *page.Page[*coredata.VendorService, coredata.VendorServiceOrderField]) *VendorServiceConnection {
var edges = make([]*VendorServiceEdge, len(p.Data))
for i := range edges {
edges[i] = NewVendorServiceEdge(p.Data[i], p.Cursor.OrderBy.Field)
}
return &VendorServiceConnection{
Edges: edges,
PageInfo: NewPageInfo(p),
}
}
func NewVendorServiceEdge(s *coredata.VendorService, orderBy coredata.VendorServiceOrderField) *VendorServiceEdge {
return &VendorServiceEdge{
Cursor: s.CursorKey(orderBy),
Node: NewVendorService(s),
}
}
func NewVendorService(s *coredata.VendorService) *VendorService {
return &VendorService{
ID: s.ID,
Name: s.Name,
Description: s.Description,
CreatedAt: s.CreatedAt,
UpdatedAt: s.UpdatedAt,
}
}

View File

@@ -1430,6 +1430,60 @@ func (r *mutationResolver) DeleteVendorContact(ctx context.Context, input types.
}, nil
}
// CreateVendorService is the resolver for the createVendorService field.
func (r *mutationResolver) CreateVendorService(ctx context.Context, input types.CreateVendorServiceInput) (*types.CreateVendorServicePayload, error) {
prb := r.ProboService(ctx, input.VendorID.TenantID())
req := probo.CreateVendorServiceRequest{
VendorID: input.VendorID,
Name: input.Name,
Description: input.Description,
}
vendorService, err := prb.VendorServices.Create(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to create vendor service: %w", err)
}
return &types.CreateVendorServicePayload{
VendorServiceEdge: types.NewVendorServiceEdge(vendorService, coredata.VendorServiceOrderFieldCreatedAt),
}, nil
}
// UpdateVendorService is the resolver for the updateVendorService field.
func (r *mutationResolver) UpdateVendorService(ctx context.Context, input types.UpdateVendorServiceInput) (*types.UpdateVendorServicePayload, error) {
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateVendorServiceRequest{
ID: input.ID,
Name: input.Name,
Description: &input.Description,
}
vendorService, err := prb.VendorServices.Update(ctx, req)
if err != nil {
return nil, fmt.Errorf("failed to update vendor service: %w", err)
}
return &types.UpdateVendorServicePayload{
VendorService: types.NewVendorService(vendorService),
}, nil
}
// DeleteVendorService is the resolver for the deleteVendorService field.
func (r *mutationResolver) DeleteVendorService(ctx context.Context, input types.DeleteVendorServiceInput) (*types.DeleteVendorServicePayload, error) {
prb := r.ProboService(ctx, input.VendorServiceID.TenantID())
err := prb.VendorServices.Delete(ctx, input.VendorServiceID)
if err != nil {
return nil, fmt.Errorf("failed to delete vendor service: %w", err)
}
return &types.DeleteVendorServicePayload{
DeletedVendorServiceID: input.VendorServiceID,
}, nil
}
// CreateFramework is the resolver for the createFramework field.
func (r *mutationResolver) CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) {
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
@@ -3507,6 +3561,12 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
panic(fmt.Errorf("cannot get vendor contact: %w", err))
}
return types.NewVendorContact(vendorContact), nil
case coredata.VendorServiceEntityType:
vendorService, err := prb.VendorServices.Get(ctx, id)
if err != nil {
panic(fmt.Errorf("cannot get vendor service: %w", err))
}
return types.NewVendorService(vendorService), nil
case coredata.DocumentVersionEntityType:
documentVersion, err := prb.Documents.GetVersion(ctx, id)
if err != nil {
@@ -3993,6 +4053,31 @@ func (r *vendorResolver) Contacts(ctx context.Context, obj *types.Vendor, first
return types.NewVendorContactConnection(page), nil
}
// Services is the resolver for the services field.
func (r *vendorResolver) Services(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorServiceOrderBy) (*types.VendorServiceConnection, error) {
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.VendorServiceOrderField]{
Field: coredata.VendorServiceOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.VendorServiceOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.VendorServices.List(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("failed to list vendor services: %w", err))
}
return types.NewVendorServiceConnection(page), nil
}
// RiskAssessments is the resolver for the riskAssessments field.
func (r *vendorResolver) RiskAssessments(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorRiskAssessmentOrder) (*types.VendorRiskAssessmentConnection, error) {
prb := r.ProboService(ctx, obj.ID.TenantID())
@@ -4206,6 +4291,24 @@ func (r *vendorRiskAssessmentResolver) AssessedBy(ctx context.Context, obj *type
return types.NewPeople(people), nil
}
// Vendor is the resolver for the vendor field.
func (r *vendorServiceResolver) Vendor(ctx context.Context, obj *types.VendorService) (*types.Vendor, error) {
prb := r.ProboService(ctx, obj.ID.TenantID())
// Get the vendor service to access the VendorID
vendorService, err := prb.VendorServices.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("failed to get vendor service: %w", err))
}
vendor, err := prb.Vendors.Get(ctx, vendorService.VendorID)
if err != nil {
panic(fmt.Errorf("failed to get vendor: %w", err))
}
return types.NewVendor(vendor), nil
}
// Organizations is the resolver for the organizations field.
func (r *viewerResolver) Organizations(ctx context.Context, obj *types.Viewer, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrganizationOrder, filter *types.OrganizationFilter) (*types.OrganizationConnection, error) {
user := UserFromContext(ctx)
@@ -4391,6 +4494,9 @@ func (r *Resolver) VendorRiskAssessment() schema.VendorRiskAssessmentResolver {
return &vendorRiskAssessmentResolver{r}
}
// VendorService returns schema.VendorServiceResolver implementation.
func (r *Resolver) VendorService() schema.VendorServiceResolver { return &vendorServiceResolver{r} }
// Viewer returns schema.ViewerResolver implementation.
func (r *Resolver) Viewer() schema.ViewerResolver { return &viewerResolver{r} }
@@ -4434,4 +4540,5 @@ type vendorConnectionResolver struct{ *Resolver }
type vendorContactResolver struct{ *Resolver }
type vendorDataPrivacyAgreementResolver struct{ *Resolver }
type vendorRiskAssessmentResolver struct{ *Resolver }
type vendorServiceResolver struct{ *Resolver }
type viewerResolver struct{ *Resolver }