From 95649a08498888c487d3df191d3c81af04d6916c Mon Sep 17 00:00:00 2001 From: gearnode Date: Thu, 30 Jan 2025 14:48:04 -0800 Subject: [PATCH] Add people data model Signed-off-by: gearnode --- pkg/api/console/v1/schema.graphql | 25 + pkg/api/console/v1/schema/schema.go | 969 +++++++++++++++++- pkg/api/console/v1/types/people.go | 51 + pkg/api/console/v1/types/types.go | 26 + pkg/api/console/v1/v1_resolver.go | 14 +- .../coredata/migrations/20250130T144500Z.sql | 9 + pkg/probo/coredata/people.go | 113 ++ pkg/probo/probo.go | 27 + 8 files changed, 1231 insertions(+), 3 deletions(-) create mode 100644 pkg/api/console/v1/types/people.go create mode 100644 pkg/probo/coredata/migrations/20250130T144500Z.sql create mode 100644 pkg/probo/coredata/people.go diff --git a/pkg/api/console/v1/schema.graphql b/pkg/api/console/v1/schema.graphql index f56fd66aa..336d86f46 100644 --- a/pkg/api/console/v1/schema.graphql +++ b/pkg/api/console/v1/schema.graphql @@ -46,11 +46,36 @@ type Organization implements Node { before: CursorKey ): VendorConnection! @goField(forceResolver: true) + peoples( + first: Int + after: CursorKey + last: Int + before: CursorKey + ): PeopleConnection! @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! } +type PeopleConnection { + edges: [PeopleEdge!]! + pageInfo: PageInfo! +} + +type PeopleEdge { + cursor: CursorKey! + node: People! +} + +type People implements Node { + id: ID! + fullName: String! + primaryEmailAddress: String! + additionalEmailAddresses: [String!]! + createdAt: Datetime! + updatedAt: Datetime! +} + type VendorConnection { edges: [VendorEdge!]! pageInfo: PageInfo! diff --git a/pkg/api/console/v1/schema/schema.go b/pkg/api/console/v1/schema/schema.go index e7c1d2b80..ec99bc3a8 100644 --- a/pkg/api/console/v1/schema/schema.go +++ b/pkg/api/console/v1/schema/schema.go @@ -133,6 +133,7 @@ type ComplexityRoot struct { Frameworks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int ID func(childComplexity int) int Name func(childComplexity int) int + Peoples func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int UpdatedAt func(childComplexity int) int Vendors func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int } @@ -144,6 +145,25 @@ type ComplexityRoot struct { StartCursor func(childComplexity int) int } + People struct { + AdditionalEmailAddresses func(childComplexity int) int + CreatedAt func(childComplexity int) int + FullName func(childComplexity int) int + ID func(childComplexity int) int + PrimaryEmailAddress func(childComplexity int) int + UpdatedAt func(childComplexity int) int + } + + PeopleConnection struct { + Edges func(childComplexity int) int + PageInfo func(childComplexity int) int + } + + PeopleEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } + Query struct { Node func(childComplexity int, id gid.GID) int } @@ -195,6 +215,7 @@ type FrameworkResolver interface { type OrganizationResolver interface { Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error) Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.VendorConnection, error) + Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PeopleConnection, error) } type QueryResolver interface { Node(ctx context.Context, id gid.GID) (types.Node, error) @@ -550,6 +571,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Organization.Name(childComplexity), true + case "Organization.peoples": + if e.complexity.Organization.Peoples == nil { + break + } + + args, err := ec.field_Organization_peoples_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Organization.Peoples(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey)), true + case "Organization.updatedAt": if e.complexity.Organization.UpdatedAt == nil { break @@ -597,6 +630,76 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PageInfo.StartCursor(childComplexity), true + case "People.additionalEmailAddresses": + if e.complexity.People.AdditionalEmailAddresses == nil { + break + } + + return e.complexity.People.AdditionalEmailAddresses(childComplexity), true + + case "People.createdAt": + if e.complexity.People.CreatedAt == nil { + break + } + + return e.complexity.People.CreatedAt(childComplexity), true + + case "People.fullName": + if e.complexity.People.FullName == nil { + break + } + + return e.complexity.People.FullName(childComplexity), true + + case "People.id": + if e.complexity.People.ID == nil { + break + } + + return e.complexity.People.ID(childComplexity), true + + case "People.primaryEmailAddress": + if e.complexity.People.PrimaryEmailAddress == nil { + break + } + + return e.complexity.People.PrimaryEmailAddress(childComplexity), true + + case "People.updatedAt": + if e.complexity.People.UpdatedAt == nil { + break + } + + return e.complexity.People.UpdatedAt(childComplexity), true + + case "PeopleConnection.edges": + if e.complexity.PeopleConnection.Edges == nil { + break + } + + return e.complexity.PeopleConnection.Edges(childComplexity), true + + case "PeopleConnection.pageInfo": + if e.complexity.PeopleConnection.PageInfo == nil { + break + } + + return e.complexity.PeopleConnection.PageInfo(childComplexity), true + + case "PeopleEdge.cursor": + if e.complexity.PeopleEdge.Cursor == nil { + break + } + + return e.complexity.PeopleEdge.Cursor(childComplexity), true + + case "PeopleEdge.node": + if e.complexity.PeopleEdge.Node == nil { + break + } + + return e.complexity.PeopleEdge.Node(childComplexity), true + case "Query.node": if e.complexity.Query.Node == nil { break @@ -877,11 +980,36 @@ type Organization implements Node { before: CursorKey ): VendorConnection! @goField(forceResolver: true) + peoples( + first: Int + after: CursorKey + last: Int + before: CursorKey + ): PeopleConnection! @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! } +type PeopleConnection { + edges: [PeopleEdge!]! + pageInfo: PageInfo! +} + +type PeopleEdge { + cursor: CursorKey! + node: People! +} + +type People implements Node { + id: ID! + fullName: String! + primaryEmailAddress: String! + additionalEmailAddresses: [String!]! + createdAt: Datetime! + updatedAt: Datetime! +} + type VendorConnection { edges: [VendorEdge!]! pageInfo: PageInfo! @@ -892,7 +1020,7 @@ type VendorEdge { node: Vendor! } -type Vendor { +type Vendor implements Node { id: ID! name: String! createdAt: Datetime! @@ -1340,6 +1468,83 @@ func (ec *executionContext) field_Organization_frameworks_argsBefore( return zeroVal, nil } +func (ec *executionContext) field_Organization_peoples_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Organization_peoples_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Organization_peoples_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Organization_peoples_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Organization_peoples_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + return args, nil +} +func (ec *executionContext) field_Organization_peoples_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_peoples_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋproboᚋcoredataᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_peoples_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_peoples_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋproboᚋcoredataᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + func (ec *executionContext) field_Organization_vendors_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3435,6 +3640,55 @@ func (ec *executionContext) fieldContext_Organization_vendors(ctx context.Contex return fc, nil } +func (ec *executionContext) _Organization_peoples(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Organization_peoples(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Organization().Peoples(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.PeopleConnection) + fc.Result = res + return ec.marshalNPeopleConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Organization_peoples(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Organization", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_PeopleConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_PeopleConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PeopleConnection", field.Name) + }, + } + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Organization_peoples_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Organization_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Organization_createdAt(ctx, field) if err != nil { @@ -3657,6 +3911,416 @@ func (ec *executionContext) fieldContext_PageInfo_endCursor(_ context.Context, f return fc, nil } +func (ec *executionContext) _People_id(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_People_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(gid.GID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋproboᚋcoredataᚋgidᚐGID(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_People_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "People", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _People_fullName(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_People_fullName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.FullName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_People_fullName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "People", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _People_primaryEmailAddress(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_People_primaryEmailAddress(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PrimaryEmailAddress, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_People_primaryEmailAddress(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "People", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _People_additionalEmailAddresses(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_People_additionalEmailAddresses(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.AdditionalEmailAddresses, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + fc.Result = res + return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_People_additionalEmailAddresses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "People", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _People_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_People_createdAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.CreatedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNDatetime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_People_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "People", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Datetime does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _People_updatedAt(ctx context.Context, field graphql.CollectedField, obj *types.People) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_People_updatedAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.UpdatedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNDatetime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_People_updatedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "People", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Datetime does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PeopleConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.PeopleConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PeopleConnection_edges(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edges, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*types.PeopleEdge) + fc.Result = res + return ec.marshalNPeopleEdge2ᚕᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleEdgeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PeopleConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PeopleConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cursor": + return ec.fieldContext_PeopleEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_PeopleEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PeopleEdge", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _PeopleConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *types.PeopleConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PeopleConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PeopleConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PeopleConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + case "hasPreviousPage": + return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field) + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _PeopleEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *types.PeopleEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PeopleEdge_cursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Cursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(page.CursorKey) + fc.Result = res + return ec.marshalNCursorKey2githubᚗcomᚋgetproboᚋproboᚋpkgᚋproboᚋcoredataᚋpageᚐCursorKey(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PeopleEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PeopleEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type CursorKey does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PeopleEdge_node(ctx context.Context, field graphql.CollectedField, obj *types.PeopleEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PeopleEdge_node(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Node, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.People) + fc.Result = res + return ec.marshalNPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PeopleEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PeopleEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_People_id(ctx, field) + case "fullName": + return ec.fieldContext_People_fullName(ctx, field) + case "primaryEmailAddress": + return ec.fieldContext_People_primaryEmailAddress(ctx, field) + case "additionalEmailAddresses": + return ec.fieldContext_People_additionalEmailAddresses(ctx, field) + case "createdAt": + return ec.fieldContext_People_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_People_updatedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type People", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query_node(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_node(ctx, field) if err != nil { @@ -6128,6 +6792,20 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._Organization(ctx, sel, obj) + case types.People: + return ec._People(ctx, sel, &obj) + case *types.People: + if obj == nil { + return graphql.Null + } + return ec._People(ctx, sel, obj) + case types.Vendor: + return ec._Vendor(ctx, sel, &obj) + case *types.Vendor: + if obj == nil { + return graphql.Null + } + return ec._Vendor(ctx, sel, obj) case types.Framework: return ec._Framework(ctx, sel, &obj) case *types.Framework: @@ -6927,6 +7605,37 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "peoples": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + res = ec._Organization_peoples(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "createdAt": out.Values[i] = ec._Organization_createdAt(ctx, field, obj) @@ -7009,6 +7718,158 @@ func (ec *executionContext) _PageInfo(ctx context.Context, sel ast.SelectionSet, return out } +var peopleImplementors = []string{"People", "Node"} + +func (ec *executionContext) _People(ctx context.Context, sel ast.SelectionSet, obj *types.People) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, peopleImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("People") + case "id": + out.Values[i] = ec._People_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "fullName": + out.Values[i] = ec._People_fullName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "primaryEmailAddress": + out.Values[i] = ec._People_primaryEmailAddress(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "additionalEmailAddresses": + out.Values[i] = ec._People_additionalEmailAddresses(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._People_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "updatedAt": + out.Values[i] = ec._People_updatedAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var peopleConnectionImplementors = []string{"PeopleConnection"} + +func (ec *executionContext) _PeopleConnection(ctx context.Context, sel ast.SelectionSet, obj *types.PeopleConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, peopleConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PeopleConnection") + case "edges": + out.Values[i] = ec._PeopleConnection_edges(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._PeopleConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var peopleEdgeImplementors = []string{"PeopleEdge"} + +func (ec *executionContext) _PeopleEdge(ctx context.Context, sel ast.SelectionSet, obj *types.PeopleEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, peopleEdgeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PeopleEdge") + case "cursor": + out.Values[i] = ec._PeopleEdge_cursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "node": + out.Values[i] = ec._PeopleEdge_node(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var queryImplementors = []string{"Query"} func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { @@ -7254,7 +8115,7 @@ func (ec *executionContext) _TaskEdge(ctx context.Context, sel ast.SelectionSet, return out } -var vendorImplementors = []string{"Vendor"} +var vendorImplementors = []string{"Vendor", "Node"} func (ec *executionContext) _Vendor(ctx context.Context, sel ast.SelectionSet, obj *types.Vendor) graphql.Marshaler { fields := graphql.CollectFields(ec.OperationContext, sel, vendorImplementors) @@ -8100,6 +8961,78 @@ func (ec *executionContext) marshalNPageInfo2ᚖgithubᚗcomᚋgetproboᚋprobo return ec._PageInfo(ctx, sel, v) } +func (ec *executionContext) marshalNPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx context.Context, sel ast.SelectionSet, v *types.People) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._People(ctx, sel, v) +} + +func (ec *executionContext) marshalNPeopleConnection2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleConnection(ctx context.Context, sel ast.SelectionSet, v types.PeopleConnection) graphql.Marshaler { + return ec._PeopleConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPeopleConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleConnection(ctx context.Context, sel ast.SelectionSet, v *types.PeopleConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PeopleConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNPeopleEdge2ᚕᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.PeopleEdge) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNPeopleEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleEdge(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNPeopleEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeopleEdge(ctx context.Context, sel ast.SelectionSet, v *types.PeopleEdge) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PeopleEdge(ctx, sel, v) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) @@ -8115,6 +9048,38 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S return res } +func (ec *executionContext) unmarshalNString2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { + var vSlice []any + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNString2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNString2string(ctx, sel, v[i]) + } + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + func (ec *executionContext) marshalNTask2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐTask(ctx context.Context, sel ast.SelectionSet, v *types.Task) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { diff --git a/pkg/api/console/v1/types/people.go b/pkg/api/console/v1/types/people.go new file mode 100644 index 000000000..d35f20b43 --- /dev/null +++ b/pkg/api/console/v1/types/people.go @@ -0,0 +1,51 @@ +// Copyright (c) 2025 Probo Inc . +// +// 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/probo/coredata" + "github.com/getprobo/probo/pkg/probo/coredata/page" +) + +func NewPeopleConnection(p *page.Page[*coredata.People]) *PeopleConnection { + var edges = make([]*PeopleEdge, len(p.Data)) + + for i := range edges { + edges[i] = NewPeopleEdge(p.Data[i]) + } + + return &PeopleConnection{ + Edges: edges, + PageInfo: NewPageInfo(p), + } +} + +func NewPeopleEdge(p *coredata.People) *PeopleEdge { + return &PeopleEdge{ + Cursor: p.CursorKey(), + Node: NewPeople(p), + } +} + +func NewPeople(p *coredata.People) *People { + return &People{ + ID: p.ID, + FullName: p.FullName, + PrimaryEmailAddress: p.PrimaryEmailAddress, + AdditionalEmailAddresses: p.AdditionalEmailAddresses, + CreatedAt: p.CreatedAt, + UpdatedAt: p.UpdatedAt, + } +} diff --git a/pkg/api/console/v1/types/types.go b/pkg/api/console/v1/types/types.go index 6be765c50..ec4dfbb80 100644 --- a/pkg/api/console/v1/types/types.go +++ b/pkg/api/console/v1/types/types.go @@ -107,6 +107,7 @@ type Organization struct { Name string `json:"name"` Frameworks *FrameworkConnection `json:"frameworks"` Vendors *VendorConnection `json:"vendors"` + Peoples *PeopleConnection `json:"peoples"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } @@ -121,6 +122,28 @@ type PageInfo struct { EndCursor *page.CursorKey `json:"endCursor,omitempty"` } +type People struct { + ID gid.GID `json:"id"` + FullName string `json:"fullName"` + PrimaryEmailAddress string `json:"primaryEmailAddress"` + AdditionalEmailAddresses []string `json:"additionalEmailAddresses"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func (People) IsNode() {} +func (this People) GetID() gid.GID { return this.ID } + +type PeopleConnection struct { + Edges []*PeopleEdge `json:"edges"` + PageInfo *PageInfo `json:"pageInfo"` +} + +type PeopleEdge struct { + Cursor page.CursorKey `json:"cursor"` + Node *People `json:"node"` +} + type Query struct { } @@ -153,6 +176,9 @@ type Vendor struct { UpdatedAt time.Time `json:"updatedAt"` } +func (Vendor) IsNode() {} +func (this Vendor) GetID() gid.GID { return this.ID } + type VendorConnection struct { Edges []*VendorEdge `json:"edges"` PageInfo *PageInfo `json:"pageInfo"` diff --git a/pkg/api/console/v1/v1_resolver.go b/pkg/api/console/v1/v1_resolver.go index b5ad0bbea..f7707598b 100644 --- a/pkg/api/console/v1/v1_resolver.go +++ b/pkg/api/console/v1/v1_resolver.go @@ -70,12 +70,24 @@ func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organizat page, err := r.svc.ListOrganizationVendors(ctx, obj.ID, cursor) if err != nil { - return nil, fmt.Errorf("cannot list organization frameworks: %w", err) + return nil, fmt.Errorf("cannot list organization vendors: %w", err) } return types.NewVendorConnection(page), nil } +// Peoples is the resolver for the peoples field. +func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PeopleConnection, error) { + cursor := types.NewCursor(first, after, last, before) + + page, err := r.svc.ListOrganizationPeoples(ctx, obj.ID, cursor) + if err != nil { + return nil, fmt.Errorf("cannot list organization peoples: %w", err) + } + + return types.NewPeopleConnection(page), nil +} + // Node is the resolver for the node field. func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) { switch id.EntityType() { diff --git a/pkg/probo/coredata/migrations/20250130T144500Z.sql b/pkg/probo/coredata/migrations/20250130T144500Z.sql new file mode 100644 index 000000000..ff0defc58 --- /dev/null +++ b/pkg/probo/coredata/migrations/20250130T144500Z.sql @@ -0,0 +1,9 @@ +CREATE TABLE peoples ( + id TEXT PRIMARY KEY, + organization_id TEXT REFERENCES organizations(id) NOT NULL, + full_name TEXT NOT NULL, + primary_email_address TEXT NOT NULL, + additional_email_addresses TEXT[] NOT NULL, + created_at TIMESTAMP WITH TIME ZONE, + updated_at TIMESTAMP WITH TIME ZONE +); diff --git a/pkg/probo/coredata/people.go b/pkg/probo/coredata/people.go new file mode 100644 index 000000000..c22102be8 --- /dev/null +++ b/pkg/probo/coredata/people.go @@ -0,0 +1,113 @@ +// Copyright (c) 2025 Probo Inc . +// +// 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 coredata + +import ( + "context" + "fmt" + "maps" + "time" + + "github.com/getprobo/probo/pkg/probo/coredata/gid" + "github.com/getprobo/probo/pkg/probo/coredata/page" + "github.com/jackc/pgx/v5" + "go.gearno.de/crypto/uuid" + "go.gearno.de/kit/pg" +) + +type ( + People struct { + ID gid.GID + OrganizationID gid.GID + FullName string + PrimaryEmailAddress string + AdditionalEmailAddresses []string + CreatedAt time.Time + UpdatedAt time.Time + } + + Peoples []*People +) + +func (p People) CursorKey() page.CursorKey { + return page.NewCursorKey(uuid.UUID(p.ID), p.CreatedAt) +} + +func (p *People) scan(r pgx.Row) error { + return r.Scan( + &p.ID, + &p.OrganizationID, + &p.FullName, + &p.PrimaryEmailAddress, + &p.PrimaryEmailAddress, + &p.CreatedAt, + &p.UpdatedAt, + ) +} + +func (p *Peoples) LoadByOrganizationID( + ctx context.Context, + conn pg.Conn, + scope *Scope, + organizationID gid.GID, + cursor *page.Cursor, +) error { + q := ` +SELECT + id, + organization_id, + full_name, + primary_email_address, + additional_email_addresses, + created_at, + updated_at +FROM + peoples +WHERE + %s + AND organization_id = @organization_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.NamedArgs{"organization_id": organizationID} + maps.Copy(args, cursor.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + r, err := conn.Query(ctx, q, args) + if err != nil { + return err + } + defer r.Close() + + peoples := Peoples{} + for r.Next() { + people := &People{} + if err := people.scan(r); err != nil { + return err + } + + peoples = append(peoples, people) + } + + if err := r.Err(); err != nil { + return err + } + + *p = peoples + + return nil +} diff --git a/pkg/probo/probo.go b/pkg/probo/probo.go index 316f1f6fb..af67c00ac 100644 --- a/pkg/probo/probo.go +++ b/pkg/probo/probo.go @@ -230,3 +230,30 @@ func (s Service) ListOrganizationVendors( return page.NewPage(vendors, cursor), nil } + +func (s Service) ListOrganizationPeoples( + ctx context.Context, + organizationID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.People], error) { + var peoples coredata.Peoples + + err := s.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return peoples.LoadByOrganizationID( + ctx, + conn, + s.scope, + organizationID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(peoples, cursor), nil +}