From e2ddbf9fae63ade2ea6c1e42adffe9bae75ce494 Mon Sep 17 00:00:00 2001 From: gearnode Date: Wed, 19 Feb 2025 09:31:17 +0100 Subject: [PATCH] Make UI refreshing without invalidate all the cache Signed-off-by: gearnode --- apps/console/src/pages/CreatePeoplePage.tsx | 41 ++-- ...ePeoplePageCreatePeopleMutation.graphql.ts | 208 ++++++++++++------ pkg/api/console/v1/schema.graphql | 6 +- pkg/api/console/v1/schema/schema.go | 141 ++++++++++-- pkg/api/console/v1/types/types.go | 4 + pkg/api/console/v1/v1_resolver.go | 6 +- 6 files changed, 287 insertions(+), 119 deletions(-) diff --git a/apps/console/src/pages/CreatePeoplePage.tsx b/apps/console/src/pages/CreatePeoplePage.tsx index d7aa49d3f..5b3e7196b 100644 --- a/apps/console/src/pages/CreatePeoplePage.tsx +++ b/apps/console/src/pages/CreatePeoplePage.tsx @@ -6,25 +6,19 @@ import { usePreloadedQuery, useQueryLoader, useMutation, - ConnectionHandler, useRelayEnvironment, + ConnectionHandler, } from "react-relay"; import { Helmet } from "react-helmet-async"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; import type { CreatePeoplePageQuery as CreatePeoplePageQueryType } from "./__generated__/CreatePeoplePageQuery.graphql"; import { useToast } from "@/hooks/use-toast"; import { HelpCircle } from "lucide-react"; import { cn } from "@/lib/utils"; +import type { CreatePeoplePageCreatePeopleMutation as CreatePeoplePageCreatePeopleMutationType } from "./__generated__/CreatePeoplePageCreatePeopleMutation.graphql"; const createPeoplePageQuery = graphql` query CreatePeoplePageQuery { @@ -38,13 +32,17 @@ const createPeoplePageQuery = graphql` `; const createPeopleMutation = graphql` - mutation CreatePeoplePageCreatePeopleMutation($input: CreatePeopleInput!) { + mutation CreatePeoplePageCreatePeopleMutation($input: CreatePeopleInput!, $connections: [ID!]!) { createPeople(input: $input) { - id - fullName - primaryEmailAddress - additionalEmailAddresses - kind + peopleEdge @prependEdge(connections: $connections) { + node { + id + fullName + primaryEmailAddress + additionalEmailAddresses + kind + } + } } } `; @@ -91,7 +89,7 @@ function CreatePeoplePageContent({ const navigate = useNavigate(); const environment = useRelayEnvironment(); const data = usePreloadedQuery(createPeoplePageQuery, queryRef); - const [commit] = useMutation(createPeopleMutation); + const [commit] = useMutation(createPeopleMutation); const { toast } = useToast(); const [formData, setFormData] = useState({ fullName: '', @@ -109,8 +107,11 @@ function CreatePeoplePageContent({ const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + const connecttionId = ConnectionHandler.getConnectionID(data.node.id, "PeopleListPageQuery_peoples"); + commit({ variables: { + connections: [connecttionId], input: { organizationId: data.node.id, fullName: formData.fullName, @@ -120,18 +121,12 @@ function CreatePeoplePageContent({ }, }, onCompleted: (response) => { - environment.commitUpdate((store) => { - const organization = store.get(data.node.id); - if (organization) { - organization.invalidateRecord(); - } - }) toast({ title: "Success", description: "Person created successfully", variant: "default", }); - navigate(`/peoples/${(response as any).createPeople.id}`); + navigate(`/peoples/${response.createPeople.peopleEdge.node.id}`); }, onError: (error) => { toast({ diff --git a/apps/console/src/pages/__generated__/CreatePeoplePageCreatePeopleMutation.graphql.ts b/apps/console/src/pages/__generated__/CreatePeoplePageCreatePeopleMutation.graphql.ts index f4ff669fb..28ebebcf6 100644 --- a/apps/console/src/pages/__generated__/CreatePeoplePageCreatePeopleMutation.graphql.ts +++ b/apps/console/src/pages/__generated__/CreatePeoplePageCreatePeopleMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -18,15 +18,20 @@ export type CreatePeopleInput = { primaryEmailAddress: string; }; export type CreatePeoplePageCreatePeopleMutation$variables = { + connections: ReadonlyArray; input: CreatePeopleInput; }; export type CreatePeoplePageCreatePeopleMutation$data = { readonly createPeople: { - readonly additionalEmailAddresses: ReadonlyArray; - readonly fullName: string; - readonly id: string; - readonly kind: PeopleKind; - readonly primaryEmailAddress: string; + readonly peopleEdge: { + readonly node: { + readonly additionalEmailAddresses: ReadonlyArray; + readonly fullName: string; + readonly id: string; + readonly kind: PeopleKind; + readonly primaryEmailAddress: string; + }; + }; }; }; export type CreatePeoplePageCreatePeopleMutation = { @@ -35,95 +40,156 @@ export type CreatePeoplePageCreatePeopleMutation = { }; const node: ConcreteRequest = (function(){ -var v0 = [ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "connections" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" +}, +v2 = [ { - "defaultValue": null, - "kind": "LocalArgument", - "name": "input" + "kind": "Variable", + "name": "input", + "variableName": "input" } ], -v1 = [ - { - "alias": null, - "args": [ - { - "kind": "Variable", - "name": "input", - "variableName": "input" - } - ], - "concreteType": "People", - "kind": "LinkedField", - "name": "createPeople", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "fullName", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "primaryEmailAddress", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "additionalEmailAddresses", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "kind", - "storageKey": null - } - ], - "storageKey": null - } -]; +v3 = { + "alias": null, + "args": null, + "concreteType": "PeopleEdge", + "kind": "LinkedField", + "name": "peopleEdge", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "primaryEmailAddress", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "additionalEmailAddresses", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "kind", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null +}; return { "fragment": { - "argumentDefinitions": (v0/*: any*/), + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/) + ], "kind": "Fragment", "metadata": null, "name": "CreatePeoplePageCreatePeopleMutation", - "selections": (v1/*: any*/), + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "CreatePeoplePayload", + "kind": "LinkedField", + "name": "createPeople", + "plural": false, + "selections": [ + (v3/*: any*/) + ], + "storageKey": null + } + ], "type": "Mutation", "abstractKey": null }, "kind": "Request", "operation": { - "argumentDefinitions": (v0/*: any*/), + "argumentDefinitions": [ + (v1/*: any*/), + (v0/*: any*/) + ], "kind": "Operation", "name": "CreatePeoplePageCreatePeopleMutation", - "selections": (v1/*: any*/) + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "CreatePeoplePayload", + "kind": "LinkedField", + "name": "createPeople", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "filters": null, + "handle": "prependEdge", + "key": "", + "kind": "LinkedHandle", + "name": "peopleEdge", + "handleArgs": [ + { + "kind": "Variable", + "name": "connections", + "variableName": "connections" + } + ] + } + ], + "storageKey": null + } + ] }, "params": { - "cacheID": "6674304ecdd07af45a837b942faed8f3", + "cacheID": "3dc15f5a577685faa1c20d26a7f1d716", "id": null, "metadata": {}, "name": "CreatePeoplePageCreatePeopleMutation", "operationKind": "mutation", - "text": "mutation CreatePeoplePageCreatePeopleMutation(\n $input: CreatePeopleInput!\n) {\n createPeople(input: $input) {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n }\n}\n" + "text": "mutation CreatePeoplePageCreatePeopleMutation(\n $input: CreatePeopleInput!\n) {\n createPeople(input: $input) {\n peopleEdge {\n node {\n id\n fullName\n primaryEmailAddress\n additionalEmailAddresses\n kind\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "c2962b44f158d7899d7e5b4d071e9238"; +(node as any).hash = "5ac3b2fbe80bf8a35d5595c6658a39cc"; export default node; diff --git a/pkg/api/console/v1/schema.graphql b/pkg/api/console/v1/schema.graphql index 862780101..1306f92a8 100644 --- a/pkg/api/console/v1/schema.graphql +++ b/pkg/api/console/v1/schema.graphql @@ -317,7 +317,7 @@ type Mutation { createVendor(input: CreateVendorInput!): Vendor! updateVendor(input: UpdateVendorInput!): Vendor! deleteVendor(input: DeleteVendorInput!): Void! - createPeople(input: CreatePeopleInput!): People! + createPeople(input: CreatePeopleInput!): CreatePeoplePayload! updatePeople(input: UpdatePeopleInput!): People! deletePeople(input: DeletePeopleInput!): Void! } @@ -378,3 +378,7 @@ input UpdateVendorInput { termsOfServiceUrl: String privacyPolicyUrl: String } + +type CreatePeoplePayload { + peopleEdge: PeopleEdge! +} \ No newline at end of file diff --git a/pkg/api/console/v1/schema/schema.go b/pkg/api/console/v1/schema/schema.go index 6cf990e2c..a0a0a2095 100644 --- a/pkg/api/console/v1/schema/schema.go +++ b/pkg/api/console/v1/schema/schema.go @@ -96,6 +96,10 @@ type ComplexityRoot struct { Node func(childComplexity int) int } + CreatePeoplePayload struct { + PeopleEdge func(childComplexity int) int + } + Evidence struct { CreatedAt func(childComplexity int) int FileURL func(childComplexity int) int @@ -288,7 +292,7 @@ type MutationResolver interface { CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.Vendor, error) UpdateVendor(ctx context.Context, input types.UpdateVendorInput) (*types.Vendor, error) DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (string, error) - CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.People, error) + CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) UpdatePeople(ctx context.Context, input types.UpdatePeopleInput) (*types.People, error) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (string, error) } @@ -495,6 +499,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ControlStateTransitionEdge.Node(childComplexity), true + case "CreatePeoplePayload.peopleEdge": + if e.complexity.CreatePeoplePayload.PeopleEdge == nil { + break + } + + return e.complexity.CreatePeoplePayload.PeopleEdge(childComplexity), true + case "Evidence.createdAt": if e.complexity.Evidence.CreatedAt == nil { break @@ -1709,7 +1720,7 @@ type Mutation { createVendor(input: CreateVendorInput!): Vendor! updateVendor(input: UpdateVendorInput!): Vendor! deleteVendor(input: DeleteVendorInput!): Void! - createPeople(input: CreatePeopleInput!): People! + createPeople(input: CreatePeopleInput!): CreatePeoplePayload! updatePeople(input: UpdatePeopleInput!): People! deletePeople(input: DeletePeopleInput!): Void! } @@ -1770,7 +1781,10 @@ input UpdateVendorInput { termsOfServiceUrl: String privacyPolicyUrl: String } -`, BuiltIn: false}, + +type CreatePeoplePayload { + peopleEdge: PeopleEdge! +}`, BuiltIn: false}, } var parsedSchema = gqlparser.MustLoadSchema(sources...) @@ -3665,6 +3679,50 @@ func (ec *executionContext) fieldContext_ControlStateTransitionEdge_node(_ conte return fc, nil } +func (ec *executionContext) _CreatePeoplePayload_peopleEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreatePeoplePayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CreatePeoplePayload_peopleEdge(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.PeopleEdge, 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_CreatePeoplePayload_peopleEdge(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CreatePeoplePayload", + 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) _Evidence_id(ctx context.Context, field graphql.CollectedField, obj *types.Evidence) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Evidence_id(ctx, field) if err != nil { @@ -5196,9 +5254,9 @@ func (ec *executionContext) _Mutation_createPeople(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(*types.People) + res := resTmp.(*types.CreatePeoplePayload) fc.Result = res - return ec.marshalNPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx, field.Selections, res) + return ec.marshalNCreatePeoplePayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreatePeoplePayload(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_createPeople(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -5209,24 +5267,10 @@ func (ec *executionContext) fieldContext_Mutation_createPeople(ctx context.Conte IsResolver: true, 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 "kind": - return ec.fieldContext_People_kind(ctx, field) - case "createdAt": - return ec.fieldContext_People_createdAt(ctx, field) - case "updatedAt": - return ec.fieldContext_People_updatedAt(ctx, field) - case "version": - return ec.fieldContext_People_version(ctx, field) + case "peopleEdge": + return ec.fieldContext_CreatePeoplePayload_peopleEdge(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type People", field.Name) + return nil, fmt.Errorf("no field named %q was found under type CreatePeoplePayload", field.Name) }, } ctx = graphql.WithFieldContext(ctx, fc) @@ -10353,6 +10397,45 @@ func (ec *executionContext) _ControlStateTransitionEdge(ctx context.Context, sel return out } +var createPeoplePayloadImplementors = []string{"CreatePeoplePayload"} + +func (ec *executionContext) _CreatePeoplePayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreatePeoplePayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, createPeoplePayloadImplementors) + + 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("CreatePeoplePayload") + case "peopleEdge": + out.Values[i] = ec._CreatePeoplePayload_peopleEdge(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 evidenceImplementors = []string{"Evidence", "Node"} func (ec *executionContext) _Evidence(ctx context.Context, sel ast.SelectionSet, obj *types.Evidence) graphql.Marshaler { @@ -12434,6 +12517,20 @@ func (ec *executionContext) unmarshalNCreatePeopleInput2githubᚗcomᚋgetprobo return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNCreatePeoplePayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreatePeoplePayload(ctx context.Context, sel ast.SelectionSet, v types.CreatePeoplePayload) graphql.Marshaler { + return ec._CreatePeoplePayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCreatePeoplePayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreatePeoplePayload(ctx context.Context, sel ast.SelectionSet, v *types.CreatePeoplePayload) 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._CreatePeoplePayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNCreateVendorInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateVendorInput(ctx context.Context, v any) (types.CreateVendorInput, error) { res, err := ec.unmarshalInputCreateVendorInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/api/console/v1/types/types.go b/pkg/api/console/v1/types/types.go index 4215d37c6..1fb6fc6fe 100644 --- a/pkg/api/console/v1/types/types.go +++ b/pkg/api/console/v1/types/types.go @@ -67,6 +67,10 @@ type CreatePeopleInput struct { Kind coredata.PeopleKind `json:"kind"` } +type CreatePeoplePayload struct { + PeopleEdge *PeopleEdge `json:"peopleEdge"` +} + type CreateVendorInput struct { OrganizationID gid.GID `json:"organizationId"` Name string `json:"name"` diff --git a/pkg/api/console/v1/v1_resolver.go b/pkg/api/console/v1/v1_resolver.go index a6b12c733..516c5e7f1 100644 --- a/pkg/api/console/v1/v1_resolver.go +++ b/pkg/api/console/v1/v1_resolver.go @@ -111,7 +111,7 @@ func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteV } // CreatePeople is the resolver for the createPeople field. -func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.People, error) { +func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) { people, err := r.svc.CreatePeople(ctx, probo.CreatePeopleRequest{ OrganizationID: input.OrganizationID, FullName: input.FullName, @@ -124,7 +124,9 @@ func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreateP return nil, fmt.Errorf("cannot create people: %w", err) } - return types.NewPeople(people), nil + return &types.CreatePeoplePayload{ + PeopleEdge: types.NewPeopleEdge(people), + }, nil } // UpdatePeople is the resolver for the updatePeople field.