diff --git a/apps/console/src/pages/organizations/people/dialogs/CreatePeopleDialog.tsx b/apps/console/src/pages/organizations/people/dialogs/CreatePeopleDialog.tsx index 40b5f3c0b..940e9d343 100644 --- a/apps/console/src/pages/organizations/people/dialogs/CreatePeopleDialog.tsx +++ b/apps/console/src/pages/organizations/people/dialogs/CreatePeopleDialog.tsx @@ -77,6 +77,7 @@ export function CreatePeopleDialog({ children, connectionId }: Props) { input: { ...data, organizationId, + additionalEmailAddresses: data.additionalEmailAddresses ?? [], }, connections: [connectionId], }, diff --git a/apps/console/src/pages/organizations/people/dialogs/__generated__/CreatePeopleDialogMutation.graphql.ts b/apps/console/src/pages/organizations/people/dialogs/__generated__/CreatePeopleDialogMutation.graphql.ts index 9eae59580..7814a5e8d 100644 --- a/apps/console/src/pages/organizations/people/dialogs/__generated__/CreatePeopleDialogMutation.graphql.ts +++ b/apps/console/src/pages/organizations/people/dialogs/__generated__/CreatePeopleDialogMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<22054346a606e1ce4a26b02218f87af9>> + * @generated SignedSource<<9acf6a96c0398ea5ae6fec887e8d01b1>> * @lightSyntaxTransform * @nogrep */ @@ -11,7 +11,7 @@ import { ConcreteRequest } from 'relay-runtime'; export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT"; export type CreatePeopleInput = { - additionalEmailAddresses?: ReadonlyArray | null | undefined; + additionalEmailAddresses: ReadonlyArray; contractEndDate?: any | null | undefined; contractStartDate?: any | null | undefined; fullName: string; diff --git a/e2e/console/people_test.go b/e2e/console/people_test.go index 6e4def587..87205a0f7 100644 --- a/e2e/console/people_test.go +++ b/e2e/console/people_test.go @@ -58,11 +58,12 @@ func TestPeople_Create(t *testing.T) { err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "organizationId": owner.GetOrganizationID().String(), - "fullName": "John Doe", - "primaryEmailAddress": "john.doe@example.com", - "kind": "EMPLOYEE", - "position": "Software Engineer", + "organizationId": owner.GetOrganizationID().String(), + "fullName": "John Doe", + "primaryEmailAddress": "john.doe@example.com", + "additionalEmailAddresses": []string{}, + "kind": "EMPLOYEE", + "position": "Software Engineer", }, }, &result) require.NoError(t, err) @@ -253,9 +254,10 @@ func TestPeople_RequiredFields(t *testing.T) { { name: "missing organizationId", input: map[string]any{ - "fullName": "Test Person", - "primaryEmailAddress": "test@example.com", - "kind": "EMPLOYEE", + "fullName": "Test Person", + "primaryEmailAddress": "test@example.com", + "additionalEmailAddresses": []string{}, + "kind": "EMPLOYEE", }, skipOrganization: true, wantErrorContains: "organizationId", @@ -263,42 +265,47 @@ func TestPeople_RequiredFields(t *testing.T) { { name: "missing fullName", input: map[string]any{ - "primaryEmailAddress": "test@example.com", - "kind": "EMPLOYEE", + "primaryEmailAddress": "test@example.com", + "additionalEmailAddresses": []string{}, + "kind": "EMPLOYEE", }, wantErrorContains: "fullName", }, { name: "missing primaryEmailAddress", input: map[string]any{ - "fullName": "Test Person", - "kind": "EMPLOYEE", + "fullName": "Test Person", + "additionalEmailAddresses": []string{}, + "kind": "EMPLOYEE", }, wantErrorContains: "primaryEmailAddress", }, { name: "missing kind", input: map[string]any{ - "fullName": "Test Person", - "primaryEmailAddress": "test@example.com", + "fullName": "Test Person", + "primaryEmailAddress": "test@example.com", + "additionalEmailAddresses": []string{}, }, wantErrorContains: "kind", }, { name: "empty fullName", input: map[string]any{ - "fullName": "", - "primaryEmailAddress": "test@example.com", - "kind": "EMPLOYEE", + "fullName": "", + "primaryEmailAddress": "test@example.com", + "additionalEmailAddresses": []string{}, + "kind": "EMPLOYEE", }, wantErrorContains: "full_name", }, { name: "invalid kind enum", input: map[string]any{ - "fullName": "Test Person", - "primaryEmailAddress": "test@example.com", - "kind": "INVALID_KIND", + "fullName": "Test Person", + "primaryEmailAddress": "test@example.com", + "additionalEmailAddresses": []string{}, + "kind": "INVALID_KIND", }, wantErrorContains: "kind", }, diff --git a/e2e/console/rbac_test.go b/e2e/console/rbac_test.go index 57c662454..da4e55b64 100644 --- a/e2e/console/rbac_test.go +++ b/e2e/console/rbac_test.go @@ -908,7 +908,7 @@ func TestRBAC(t *testing.T) { client: owner, query: createPeopleMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "fullName": factory.SafeName("Person"), "primaryEmailAddress": factory.SafeEmail(), "kind": "EMPLOYEE"}} + return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "fullName": factory.SafeName("Person"), "primaryEmailAddress": factory.SafeEmail(), "additionalEmailAddresses": []string{}, "kind": "EMPLOYEE"}} }, shouldAllow: true, }, @@ -918,7 +918,7 @@ func TestRBAC(t *testing.T) { client: admin, query: createPeopleMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "fullName": factory.SafeName("Person"), "primaryEmailAddress": factory.SafeEmail(), "kind": "EMPLOYEE"}} + return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "fullName": factory.SafeName("Person"), "primaryEmailAddress": factory.SafeEmail(), "additionalEmailAddresses": []string{}, "kind": "EMPLOYEE"}} }, shouldAllow: true, }, @@ -928,7 +928,7 @@ func TestRBAC(t *testing.T) { client: viewer, query: createPeopleMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "fullName": factory.SafeName("Person"), "primaryEmailAddress": factory.SafeEmail(), "kind": "EMPLOYEE"}} + return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "fullName": factory.SafeName("Person"), "primaryEmailAddress": factory.SafeEmail(), "additionalEmailAddresses": []string{}, "kind": "EMPLOYEE"}} }, shouldAllow: false, }, @@ -1259,4 +1259,3 @@ func TestRBAC(t *testing.T) { ) } } - diff --git a/e2e/internal/factory/factory.go b/e2e/internal/factory/factory.go index 0e5d24e02..bfe6fb8ed 100644 --- a/e2e/internal/factory/factory.go +++ b/e2e/internal/factory/factory.go @@ -92,6 +92,18 @@ func (a Attrs) getBool(key string, defaultVal bool) bool { return defaultVal } +func (a Attrs) getSlice(key string, defaultVal []string) []string { + if a == nil { + return defaultVal + } + if v, ok := a[key]; ok { + if slice, ok := v.([]string); ok { + return slice + } + } + return defaultVal +} + func CreateVendor(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() @@ -375,10 +387,11 @@ func CreatePeople(c *testutil.Client, attrs ...Attrs) string { ` input := map[string]any{ - "organizationId": c.GetOrganizationID().String(), - "fullName": a.getString("fullName", SafeName("Person")), - "primaryEmailAddress": a.getString("primaryEmailAddress", SafeEmail()), - "kind": a.getString("kind", "EMPLOYEE"), + "organizationId": c.GetOrganizationID().String(), + "fullName": a.getString("fullName", SafeName("Person")), + "primaryEmailAddress": a.getString("primaryEmailAddress", SafeEmail()), + "additionalEmailAddresses": a.getSlice("additionalEmailAddresses", []string{}), + "kind": a.getString("kind", "EMPLOYEE"), } var result struct { diff --git a/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts index 4c2e760e3..7dbe91753 100644 --- a/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts @@ -159,6 +159,8 @@ export async function execute( }; if (additionalFields.additionalEmailAddresses) { input.additionalEmailAddresses = additionalFields.additionalEmailAddresses.split(',').map((e) => e.trim()).filter(Boolean); + } else { + input.additionalEmailAddresses = []; } if (additionalFields.position) input.position = additionalFields.position; if (additionalFields.contractStartDate) input.contractStartDate = new Date(additionalFields.contractStartDate).toISOString(); diff --git a/pkg/coredata/migrations/20260102T093953Z.sql b/pkg/coredata/migrations/20260102T093953Z.sql new file mode 100644 index 000000000..d17ca01bf --- /dev/null +++ b/pkg/coredata/migrations/20260102T093953Z.sql @@ -0,0 +1,5 @@ +UPDATE peoples +SET additional_email_addresses = ARRAY[]::TEXT[] +WHERE additional_email_addresses IS NULL; + +ALTER TABLE peoples ALTER COLUMN additional_email_addresses SET NOT NULL; diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index 5b8a15bb5..648a3b2ae 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -3523,7 +3523,7 @@ input CreatePeopleInput { organizationId: ID! fullName: String! primaryEmailAddress: EmailAddr! - additionalEmailAddresses: [EmailAddr!] + additionalEmailAddresses: [EmailAddr!]! kind: PeopleKind! position: String contractStartDate: Datetime diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 88c5732fd..00dbb3781 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -13522,7 +13522,7 @@ input CreatePeopleInput { organizationId: ID! fullName: String! primaryEmailAddress: EmailAddr! - additionalEmailAddresses: [EmailAddr!] + additionalEmailAddresses: [EmailAddr!]! kind: PeopleKind! position: String contractStartDate: Datetime @@ -60752,7 +60752,7 @@ func (ec *executionContext) unmarshalInputCreatePeopleInput(ctx context.Context, it.PrimaryEmailAddress = data case "additionalEmailAddresses": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("additionalEmailAddresses")) - data, err := ec.unmarshalOEmailAddr2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx, v) + data, err := ec.unmarshalNEmailAddr2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx, v) if err != nil { return it, err } diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index f10f152b4..a0a103bd9 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -425,7 +425,7 @@ type CreatePeopleInput struct { OrganizationID gid.GID `json:"organizationId"` FullName string `json:"fullName"` PrimaryEmailAddress mail.Addr `json:"primaryEmailAddress"` - AdditionalEmailAddresses []mail.Addr `json:"additionalEmailAddresses,omitempty"` + AdditionalEmailAddresses []mail.Addr `json:"additionalEmailAddresses"` Kind coredata.PeopleKind `json:"kind"` Position *string `json:"position,omitempty"` ContractStartDate *time.Time `json:"contractStartDate,omitempty"` diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index c32962508..71f52ef12 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -2073,7 +2073,7 @@ func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreateP OrganizationID: input.OrganizationID, FullName: input.FullName, PrimaryEmailAddress: input.PrimaryEmailAddress, - AdditionalEmailAddresses: []mail.Addr{}, + AdditionalEmailAddresses: input.AdditionalEmailAddresses, Kind: input.Kind, Position: input.Position, ContractStartDate: input.ContractStartDate, diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index 7d219f5cd..5d14e0b0a 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -11,6 +11,7 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" "go.probo.inc/probo/pkg/authz" "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/mail" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/probo" "go.probo.inc/probo/pkg/server/api/mcp/v1/types" @@ -171,13 +172,18 @@ func (r *Resolver) AddPeopleTool(ctx context.Context, req *mcp.CallToolRequest, svc := r.ProboService(ctx, input.OrganizationID) + additionalEmails := []mail.Addr{} + if input.AdditionalEmailAddresses != nil { + additionalEmails = input.AdditionalEmailAddresses + } + people, err := svc.Peoples.Create( ctx, probo.CreatePeopleRequest{ OrganizationID: input.OrganizationID, FullName: input.FullName, PrimaryEmailAddress: input.PrimaryEmailAddress, - AdditionalEmailAddresses: input.AdditionalEmailAddresses, + AdditionalEmailAddresses: additionalEmails, Kind: input.Kind, Position: input.Position, ContractStartDate: input.ContractStartDate,