From 4b26513acf7fbe096f9602f8046a0cc5f003acb8 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 15:32:58 -0600 Subject: [PATCH 1/8] add search select api --- graphql2/generated.go | 1274 +++++++++++++++++------ graphql2/graph/destinations.graphqls | 23 +- graphql2/graphqlapp/destinationtypes.go | 124 +++ graphql2/models_gen.go | 20 +- web/src/schema.d.ts | 18 + 5 files changed, 1129 insertions(+), 330 deletions(-) diff --git a/graphql2/generated.go b/graphql2/generated.go index db25f0d63c..6aec523cae 100644 --- a/graphql2/generated.go +++ b/graphql2/generated.go @@ -68,6 +68,7 @@ type ResolverRoot interface { AlertMetric() AlertMetricResolver EscalationPolicy() EscalationPolicyResolver EscalationPolicyStep() EscalationPolicyStepResolver + FieldValuePair() FieldValuePairResolver GQLAPIKey() GQLAPIKeyResolver HeartbeatMonitor() HeartbeatMonitorResolver IntegrationKey() IntegrationKeyResolver @@ -279,9 +280,16 @@ type ComplexityRoot struct { Targets func(childComplexity int) int } + FieldValueConnection struct { + Nodes func(childComplexity int) int + PageInfo func(childComplexity int) int + } + FieldValuePair struct { - FieldID func(childComplexity int) int - Value func(childComplexity int) int + FieldID func(childComplexity int) int + IsFavorite func(childComplexity int) int + Label func(childComplexity int) int + Value func(childComplexity int) int } GQLAPIKey struct { @@ -457,51 +465,53 @@ type ComplexityRoot struct { } Query struct { - Alert func(childComplexity int, id int) int - Alerts func(childComplexity int, input *AlertSearchOptions) int - AuthSubjectsForProvider func(childComplexity int, first *int, after *string, providerID string) int - CalcRotationHandoffTimes func(childComplexity int, input *CalcRotationHandoffTimesInput) int - Config func(childComplexity int, all *bool) int - ConfigHints func(childComplexity int) int - DebugMessageStatus func(childComplexity int, input DebugMessageStatusInput) int - DebugMessages func(childComplexity int, input *DebugMessagesInput) int - DestinationDisplayInfo func(childComplexity int, input DestinationInput) int - DestinationFieldValidate func(childComplexity int, input DestinationFieldValidateInput) int - DestinationTypes func(childComplexity int) int - EscalationPolicies func(childComplexity int, input *EscalationPolicySearchOptions) int - EscalationPolicy func(childComplexity int, id string) int - ExperimentalFlags func(childComplexity int) int - GenerateSlackAppManifest func(childComplexity int) int - GqlAPIKeys func(childComplexity int) int - HeartbeatMonitor func(childComplexity int, id string) int - IntegrationKey func(childComplexity int, id string) int - IntegrationKeyTypes func(childComplexity int) int - IntegrationKeys func(childComplexity int, input *IntegrationKeySearchOptions) int - LabelKeys func(childComplexity int, input *LabelKeySearchOptions) int - LabelValues func(childComplexity int, input *LabelValueSearchOptions) int - Labels func(childComplexity int, input *LabelSearchOptions) int - LinkAccountInfo func(childComplexity int, token string) int - MessageLogs func(childComplexity int, input *MessageLogSearchOptions) int - PhoneNumberInfo func(childComplexity int, number string) int - Rotation func(childComplexity int, id string) int - Rotations func(childComplexity int, input *RotationSearchOptions) int - Schedule func(childComplexity int, id string) int - Schedules func(childComplexity int, input *ScheduleSearchOptions) int - Service func(childComplexity int, id string) int - Services func(childComplexity int, input *ServiceSearchOptions) int - SlackChannel func(childComplexity int, id string) int - SlackChannels func(childComplexity int, input *SlackChannelSearchOptions) int - SlackUserGroup func(childComplexity int, id string) int - SlackUserGroups func(childComplexity int, input *SlackUserGroupSearchOptions) int - SwoStatus func(childComplexity int) int - SystemLimits func(childComplexity int) int - TimeZones func(childComplexity int, input *TimeZoneSearchOptions) int - User func(childComplexity int, id *string) int - UserCalendarSubscription func(childComplexity int, id string) int - UserContactMethod func(childComplexity int, id string) int - UserOverride func(childComplexity int, id string) int - UserOverrides func(childComplexity int, input *UserOverrideSearchOptions) int - Users func(childComplexity int, input *UserSearchOptions, first *int, after *string, search *string) int + Alert func(childComplexity int, id int) int + Alerts func(childComplexity int, input *AlertSearchOptions) int + AuthSubjectsForProvider func(childComplexity int, first *int, after *string, providerID string) int + CalcRotationHandoffTimes func(childComplexity int, input *CalcRotationHandoffTimesInput) int + Config func(childComplexity int, all *bool) int + ConfigHints func(childComplexity int) int + DebugMessageStatus func(childComplexity int, input DebugMessageStatusInput) int + DebugMessages func(childComplexity int, input *DebugMessagesInput) int + DestinationDisplayInfo func(childComplexity int, input DestinationInput) int + DestinationFieldSearch func(childComplexity int, input DestinationFieldSearchInput) int + DestinationFieldValidate func(childComplexity int, input DestinationFieldValidateInput) int + DestinationFieldValueName func(childComplexity int, input DestinationFieldValidateInput) int + DestinationTypes func(childComplexity int) int + EscalationPolicies func(childComplexity int, input *EscalationPolicySearchOptions) int + EscalationPolicy func(childComplexity int, id string) int + ExperimentalFlags func(childComplexity int) int + GenerateSlackAppManifest func(childComplexity int) int + GqlAPIKeys func(childComplexity int) int + HeartbeatMonitor func(childComplexity int, id string) int + IntegrationKey func(childComplexity int, id string) int + IntegrationKeyTypes func(childComplexity int) int + IntegrationKeys func(childComplexity int, input *IntegrationKeySearchOptions) int + LabelKeys func(childComplexity int, input *LabelKeySearchOptions) int + LabelValues func(childComplexity int, input *LabelValueSearchOptions) int + Labels func(childComplexity int, input *LabelSearchOptions) int + LinkAccountInfo func(childComplexity int, token string) int + MessageLogs func(childComplexity int, input *MessageLogSearchOptions) int + PhoneNumberInfo func(childComplexity int, number string) int + Rotation func(childComplexity int, id string) int + Rotations func(childComplexity int, input *RotationSearchOptions) int + Schedule func(childComplexity int, id string) int + Schedules func(childComplexity int, input *ScheduleSearchOptions) int + Service func(childComplexity int, id string) int + Services func(childComplexity int, input *ServiceSearchOptions) int + SlackChannel func(childComplexity int, id string) int + SlackChannels func(childComplexity int, input *SlackChannelSearchOptions) int + SlackUserGroup func(childComplexity int, id string) int + SlackUserGroups func(childComplexity int, input *SlackUserGroupSearchOptions) int + SwoStatus func(childComplexity int) int + SystemLimits func(childComplexity int) int + TimeZones func(childComplexity int, input *TimeZoneSearchOptions) int + User func(childComplexity int, id *string) int + UserCalendarSubscription func(childComplexity int, id string) int + UserContactMethod func(childComplexity int, id string) int + UserOverride func(childComplexity int, id string) int + UserOverrides func(childComplexity int, input *UserOverrideSearchOptions) int + Users func(childComplexity int, input *UserSearchOptions, first *int, after *string, search *string) int } Rotation struct { @@ -779,6 +789,9 @@ type EscalationPolicyStepResolver interface { Targets(ctx context.Context, obj *escalation.Step) ([]assignment.RawTarget, error) EscalationPolicy(ctx context.Context, obj *escalation.Step) (*escalation.Policy, error) } +type FieldValuePairResolver interface { + Label(ctx context.Context, obj *FieldValuePair) (string, error) +} type GQLAPIKeyResolver interface { CreatedBy(ctx context.Context, obj *GQLAPIKey) (*user.User, error) @@ -901,6 +914,8 @@ type QueryResolver interface { SwoStatus(ctx context.Context) (*SWOStatus, error) DestinationTypes(ctx context.Context) ([]DestinationTypeInfo, error) DestinationFieldValidate(ctx context.Context, input DestinationFieldValidateInput) (bool, error) + DestinationFieldSearch(ctx context.Context, input DestinationFieldSearchInput) (*FieldValueConnection, error) + DestinationFieldValueName(ctx context.Context, input DestinationFieldValidateInput) (string, error) DestinationDisplayInfo(ctx context.Context, input DestinationInput) (*DestinationDisplayInfo, error) GqlAPIKeys(ctx context.Context) ([]GQLAPIKey, error) } @@ -1796,6 +1811,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EscalationPolicyStep.Targets(childComplexity), true + case "FieldValueConnection.nodes": + if e.complexity.FieldValueConnection.Nodes == nil { + break + } + + return e.complexity.FieldValueConnection.Nodes(childComplexity), true + + case "FieldValueConnection.pageInfo": + if e.complexity.FieldValueConnection.PageInfo == nil { + break + } + + return e.complexity.FieldValueConnection.PageInfo(childComplexity), true + case "FieldValuePair.fieldID": if e.complexity.FieldValuePair.FieldID == nil { break @@ -1803,6 +1832,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.FieldValuePair.FieldID(childComplexity), true + case "FieldValuePair.isFavorite": + if e.complexity.FieldValuePair.IsFavorite == nil { + break + } + + return e.complexity.FieldValuePair.IsFavorite(childComplexity), true + + case "FieldValuePair.label": + if e.complexity.FieldValuePair.Label == nil { + break + } + + return e.complexity.FieldValuePair.Label(childComplexity), true + case "FieldValuePair.value": if e.complexity.FieldValuePair.Value == nil { break @@ -2999,6 +3042,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.DestinationDisplayInfo(childComplexity, args["input"].(DestinationInput)), true + case "Query.destinationFieldSearch": + if e.complexity.Query.DestinationFieldSearch == nil { + break + } + + args, err := ec.field_Query_destinationFieldSearch_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.DestinationFieldSearch(childComplexity, args["input"].(DestinationFieldSearchInput)), true + case "Query.destinationFieldValidate": if e.complexity.Query.DestinationFieldValidate == nil { break @@ -3011,6 +3066,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.DestinationFieldValidate(childComplexity, args["input"].(DestinationFieldValidateInput)), true + case "Query.destinationFieldValueName": + if e.complexity.Query.DestinationFieldValueName == nil { + break + } + + args, err := ec.field_Query_destinationFieldValueName_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.DestinationFieldValueName(childComplexity, args["input"].(DestinationFieldValidateInput)), true + case "Query.destinationTypes": if e.complexity.Query.DestinationTypes == nil { break @@ -4503,6 +4570,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputDebugMessageStatusInput, ec.unmarshalInputDebugMessagesInput, ec.unmarshalInputDebugSendSMSInput, + ec.unmarshalInputDestinationFieldSearchInput, ec.unmarshalInputDestinationFieldValidateInput, ec.unmarshalInputDestinationInput, ec.unmarshalInputEscalationPolicySearchOptions, @@ -5632,6 +5700,21 @@ func (ec *executionContext) field_Query_destinationDisplayInfo_args(ctx context. return args, nil } +func (ec *executionContext) field_Query_destinationFieldSearch_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 DestinationFieldSearchInput + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNDestinationFieldSearchInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldSearchInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_destinationFieldValidate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -5647,6 +5730,21 @@ func (ec *executionContext) field_Query_destinationFieldValidate_args(ctx contex return args, nil } +func (ec *executionContext) field_Query_destinationFieldValueName_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 DestinationFieldValidateInput + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNDestinationFieldValidateInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldValidateInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_escalationPolicies_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -9424,6 +9522,10 @@ func (ec *executionContext) fieldContext_Destination_values(ctx context.Context, return ec.fieldContext_FieldValuePair_fieldID(ctx, field) case "value": return ec.fieldContext_FieldValuePair_value(ctx, field) + case "label": + return ec.fieldContext_FieldValuePair_label(ctx, field) + case "isFavorite": + return ec.fieldContext_FieldValuePair_isFavorite(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type FieldValuePair", field.Name) }, @@ -11410,6 +11512,110 @@ func (ec *executionContext) fieldContext_EscalationPolicyStep_escalationPolicy(c return fc, nil } +func (ec *executionContext) _FieldValueConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *FieldValueConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FieldValueConnection_nodes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Nodes, 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.([]FieldValuePair) + fc.Result = res + return ec.marshalNFieldValuePair2ᚕgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValuePairᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FieldValueConnection_nodes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FieldValueConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "fieldID": + return ec.fieldContext_FieldValuePair_fieldID(ctx, field) + case "value": + return ec.fieldContext_FieldValuePair_value(ctx, field) + case "label": + return ec.fieldContext_FieldValuePair_label(ctx, field) + case "isFavorite": + return ec.fieldContext_FieldValuePair_isFavorite(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type FieldValuePair", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _FieldValueConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *FieldValueConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FieldValueConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, 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.(*PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FieldValueConnection_pageInfo(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FieldValueConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _FieldValuePair_fieldID(ctx context.Context, field graphql.CollectedField, obj *FieldValuePair) (ret graphql.Marshaler) { fc, err := ec.fieldContext_FieldValuePair_fieldID(ctx, field) if err != nil { @@ -11498,8 +11704,8 @@ func (ec *executionContext) fieldContext_FieldValuePair_value(ctx context.Contex return fc, nil } -func (ec *executionContext) _GQLAPIKey_id(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_id(ctx, field) +func (ec *executionContext) _FieldValuePair_label(ctx context.Context, field graphql.CollectedField, obj *FieldValuePair) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FieldValuePair_label(ctx, field) if err != nil { return graphql.Null } @@ -11512,7 +11718,7 @@ func (ec *executionContext) _GQLAPIKey_id(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.ID, nil + return ec.resolvers.FieldValuePair().Label(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -11526,24 +11732,24 @@ func (ec *executionContext) _GQLAPIKey_id(ctx context.Context, field graphql.Col } res := resTmp.(string) fc.Result = res - return ec.marshalNID2string(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_FieldValuePair_label(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GQLAPIKey", + Object: "FieldValuePair", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, 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 nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _GQLAPIKey_name(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_name(ctx, field) +func (ec *executionContext) _FieldValuePair_isFavorite(ctx context.Context, field graphql.CollectedField, obj *FieldValuePair) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FieldValuePair_isFavorite(ctx, field) if err != nil { return graphql.Null } @@ -11556,7 +11762,7 @@ func (ec *executionContext) _GQLAPIKey_name(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.IsFavorite, nil }) if err != nil { ec.Error(ctx, err) @@ -11568,26 +11774,26 @@ func (ec *executionContext) _GQLAPIKey_name(ctx context.Context, field graphql.C } return graphql.Null } - res := resTmp.(string) + res := resTmp.(bool) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_FieldValuePair_isFavorite(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "GQLAPIKey", + Object: "FieldValuePair", 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 nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil } -func (ec *executionContext) _GQLAPIKey_description(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_description(ctx, field) +func (ec *executionContext) _GQLAPIKey_id(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_id(ctx, field) if err != nil { return graphql.Null } @@ -11600,7 +11806,7 @@ func (ec *executionContext) _GQLAPIKey_description(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Description, nil + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -11614,24 +11820,24 @@ func (ec *executionContext) _GQLAPIKey_description(ctx context.Context, field gr } res := resTmp.(string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GQLAPIKey_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "GQLAPIKey", 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 nil, errors.New("field of type ID does not have child fields") }, } return fc, nil } -func (ec *executionContext) _GQLAPIKey_createdAt(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_createdAt(ctx, field) +func (ec *executionContext) _GQLAPIKey_name(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_name(ctx, field) if err != nil { return graphql.Null } @@ -11644,7 +11850,7 @@ func (ec *executionContext) _GQLAPIKey_createdAt(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.CreatedAt, nil + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) @@ -11656,26 +11862,26 @@ func (ec *executionContext) _GQLAPIKey_createdAt(ctx context.Context, field grap } return graphql.Null } - res := resTmp.(time.Time) + res := resTmp.(string) fc.Result = res - return ec.marshalNISOTimestamp2timeᚐTime(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_createdAt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GQLAPIKey_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "GQLAPIKey", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type ISOTimestamp does not have child fields") + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _GQLAPIKey_createdBy(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_createdBy(ctx, field) +func (ec *executionContext) _GQLAPIKey_description(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_description(ctx, field) if err != nil { return graphql.Null } @@ -11688,63 +11894,38 @@ func (ec *executionContext) _GQLAPIKey_createdBy(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.GQLAPIKey().CreatedBy(rctx, obj) + return obj.Description, 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.(*user.User) + res := resTmp.(string) fc.Result = res - return ec.marshalOUser2ᚖgithubᚗcomᚋtargetᚋgoalertᚋuserᚐUser(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_createdBy(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GQLAPIKey_description(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "GQLAPIKey", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_User_id(ctx, field) - case "role": - return ec.fieldContext_User_role(ctx, field) - case "name": - return ec.fieldContext_User_name(ctx, field) - case "email": - return ec.fieldContext_User_email(ctx, field) - case "contactMethods": - return ec.fieldContext_User_contactMethods(ctx, field) - case "notificationRules": - return ec.fieldContext_User_notificationRules(ctx, field) - case "calendarSubscriptions": - return ec.fieldContext_User_calendarSubscriptions(ctx, field) - case "statusUpdateContactMethodID": - return ec.fieldContext_User_statusUpdateContactMethodID(ctx, field) - case "authSubjects": - return ec.fieldContext_User_authSubjects(ctx, field) - case "sessions": - return ec.fieldContext_User_sessions(ctx, field) - case "onCallSteps": - return ec.fieldContext_User_onCallSteps(ctx, field) - case "isFavorite": - return ec.fieldContext_User_isFavorite(ctx, field) - case "assignedSchedules": - return ec.fieldContext_User_assignedSchedules(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } return fc, nil } -func (ec *executionContext) _GQLAPIKey_updatedAt(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_updatedAt(ctx, field) +func (ec *executionContext) _GQLAPIKey_createdAt(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_createdAt(ctx, field) if err != nil { return graphql.Null } @@ -11757,7 +11938,7 @@ func (ec *executionContext) _GQLAPIKey_updatedAt(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.UpdatedAt, nil + return obj.CreatedAt, nil }) if err != nil { ec.Error(ctx, err) @@ -11774,7 +11955,7 @@ func (ec *executionContext) _GQLAPIKey_updatedAt(ctx context.Context, field grap return ec.marshalNISOTimestamp2timeᚐTime(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_updatedAt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GQLAPIKey_createdAt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "GQLAPIKey", Field: field, @@ -11787,8 +11968,8 @@ func (ec *executionContext) fieldContext_GQLAPIKey_updatedAt(ctx context.Context return fc, nil } -func (ec *executionContext) _GQLAPIKey_updatedBy(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_GQLAPIKey_updatedBy(ctx, field) +func (ec *executionContext) _GQLAPIKey_createdBy(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_createdBy(ctx, field) if err != nil { return graphql.Null } @@ -11801,7 +11982,7 @@ func (ec *executionContext) _GQLAPIKey_updatedBy(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.GQLAPIKey().UpdatedBy(rctx, obj) + return ec.resolvers.GQLAPIKey().CreatedBy(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -11815,7 +11996,120 @@ func (ec *executionContext) _GQLAPIKey_updatedBy(ctx context.Context, field grap return ec.marshalOUser2ᚖgithubᚗcomᚋtargetᚋgoalertᚋuserᚐUser(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_GQLAPIKey_updatedBy(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_GQLAPIKey_createdBy(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GQLAPIKey", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "role": + return ec.fieldContext_User_role(ctx, field) + case "name": + return ec.fieldContext_User_name(ctx, field) + case "email": + return ec.fieldContext_User_email(ctx, field) + case "contactMethods": + return ec.fieldContext_User_contactMethods(ctx, field) + case "notificationRules": + return ec.fieldContext_User_notificationRules(ctx, field) + case "calendarSubscriptions": + return ec.fieldContext_User_calendarSubscriptions(ctx, field) + case "statusUpdateContactMethodID": + return ec.fieldContext_User_statusUpdateContactMethodID(ctx, field) + case "authSubjects": + return ec.fieldContext_User_authSubjects(ctx, field) + case "sessions": + return ec.fieldContext_User_sessions(ctx, field) + case "onCallSteps": + return ec.fieldContext_User_onCallSteps(ctx, field) + case "isFavorite": + return ec.fieldContext_User_isFavorite(ctx, field) + case "assignedSchedules": + return ec.fieldContext_User_assignedSchedules(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GQLAPIKey_updatedAt(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_updatedAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, 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.marshalNISOTimestamp2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GQLAPIKey_updatedAt(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GQLAPIKey", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ISOTimestamp does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _GQLAPIKey_updatedBy(ctx context.Context, field graphql.CollectedField, obj *GQLAPIKey) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GQLAPIKey_updatedBy(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.GQLAPIKey().UpdatedBy(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*user.User) + fc.Result = res + return ec.marshalOUser2ᚖgithubᚗcomᚋtargetᚋgoalertᚋuserᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GQLAPIKey_updatedBy(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "GQLAPIKey", Field: field, @@ -20485,6 +20779,122 @@ func (ec *executionContext) fieldContext_Query_destinationFieldValidate(ctx cont return fc, nil } +func (ec *executionContext) _Query_destinationFieldSearch(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_destinationFieldSearch(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().DestinationFieldSearch(rctx, fc.Args["input"].(DestinationFieldSearchInput)) + }) + 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.(*FieldValueConnection) + fc.Result = res + return ec.marshalNFieldValueConnection2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_destinationFieldSearch(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "nodes": + return ec.fieldContext_FieldValueConnection_nodes(ctx, field) + case "pageInfo": + return ec.fieldContext_FieldValueConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type FieldValueConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_destinationFieldSearch_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_destinationFieldValueName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_destinationFieldValueName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().DestinationFieldValueName(rctx, fc.Args["input"].(DestinationFieldValidateInput)) + }) + 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_Query_destinationFieldValueName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_destinationFieldValueName_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query_destinationDisplayInfo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_destinationDisplayInfo(ctx, field) if err != nil { @@ -31242,6 +31652,72 @@ func (ec *executionContext) unmarshalInputDebugSendSMSInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputDestinationFieldSearchInput(ctx context.Context, obj interface{}) (DestinationFieldSearchInput, error) { + var it DestinationFieldSearchInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + if _, present := asMap["first"]; !present { + asMap["first"] = 15 + } + + fieldsInOrder := [...]string{"destType", "fieldID", "search", "omit", "after", "first"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "destType": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("destType")) + data, err := ec.unmarshalNDestinationType2string(ctx, v) + if err != nil { + return it, err + } + it.DestType = data + case "fieldID": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fieldID")) + data, err := ec.unmarshalNID2string(ctx, v) + if err != nil { + return it, err + } + it.FieldID = data + case "search": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("search")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Search = data + case "omit": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("omit")) + data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.Omit = data + case "after": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.After = data + case "first": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + data, err := ec.unmarshalOInt2ᚖint(ctx, v) + if err != nil { + return it, err + } + it.First = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputDestinationFieldValidateInput(ctx context.Context, obj interface{}) (DestinationFieldValidateInput, error) { var it DestinationFieldValidateInput asMap := map[string]interface{}{} @@ -35340,212 +35816,297 @@ func (ec *executionContext) _EscalationPolicy(ctx context.Context, sel ast.Selec } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - 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 escalationPolicyConnectionImplementors = []string{"EscalationPolicyConnection"} - -func (ec *executionContext) _EscalationPolicyConnection(ctx context.Context, sel ast.SelectionSet, obj *EscalationPolicyConnection) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, escalationPolicyConnectionImplementors) - - 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("EscalationPolicyConnection") - case "nodes": - out.Values[i] = ec._EscalationPolicyConnection_nodes(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "pageInfo": - out.Values[i] = ec._EscalationPolicyConnection_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 escalationPolicyStepImplementors = []string{"EscalationPolicyStep"} - -func (ec *executionContext) _EscalationPolicyStep(ctx context.Context, sel ast.SelectionSet, obj *escalation.Step) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, escalationPolicyStepImplementors) - - 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("EscalationPolicyStep") - case "id": - out.Values[i] = ec._EscalationPolicyStep_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "stepNumber": - out.Values[i] = ec._EscalationPolicyStep_stepNumber(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } - case "delayMinutes": - out.Values[i] = ec._EscalationPolicyStep_delayMinutes(ctx, field, obj) + 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 escalationPolicyConnectionImplementors = []string{"EscalationPolicyConnection"} + +func (ec *executionContext) _EscalationPolicyConnection(ctx context.Context, sel ast.SelectionSet, obj *EscalationPolicyConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, escalationPolicyConnectionImplementors) + + 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("EscalationPolicyConnection") + case "nodes": + out.Values[i] = ec._EscalationPolicyConnection_nodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._EscalationPolicyConnection_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 escalationPolicyStepImplementors = []string{"EscalationPolicyStep"} + +func (ec *executionContext) _EscalationPolicyStep(ctx context.Context, sel ast.SelectionSet, obj *escalation.Step) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, escalationPolicyStepImplementors) + + 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("EscalationPolicyStep") + case "id": + out.Values[i] = ec._EscalationPolicyStep_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "stepNumber": + out.Values[i] = ec._EscalationPolicyStep_stepNumber(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "delayMinutes": + out.Values[i] = ec._EscalationPolicyStep_delayMinutes(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "targets": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._EscalationPolicyStep_targets(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 "escalationPolicy": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._EscalationPolicyStep_escalationPolicy(ctx, field, obj) + 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) }) + 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 fieldValueConnectionImplementors = []string{"FieldValueConnection"} + +func (ec *executionContext) _FieldValueConnection(ctx context.Context, sel ast.SelectionSet, obj *FieldValueConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, fieldValueConnectionImplementors) + + 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("FieldValueConnection") + case "nodes": + out.Values[i] = ec._FieldValueConnection_nodes(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._FieldValueConnection_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 fieldValuePairImplementors = []string{"FieldValuePair"} + +func (ec *executionContext) _FieldValuePair(ctx context.Context, sel ast.SelectionSet, obj *FieldValuePair) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, fieldValuePairImplementors) + + 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("FieldValuePair") + case "fieldID": + out.Values[i] = ec._FieldValuePair_fieldID(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "value": + out.Values[i] = ec._FieldValuePair_value(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "label": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._FieldValuePair_label(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 "isFavorite": + out.Values[i] = ec._FieldValuePair_isFavorite(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "targets": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._EscalationPolicyStep_targets(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 "escalationPolicy": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._EscalationPolicyStep_escalationPolicy(ctx, field, obj) - 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) }) - 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 fieldValuePairImplementors = []string{"FieldValuePair"} - -func (ec *executionContext) _FieldValuePair(ctx context.Context, sel ast.SelectionSet, obj *FieldValuePair) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, fieldValuePairImplementors) - - 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("FieldValuePair") - case "fieldID": - out.Values[i] = ec._FieldValuePair_fieldID(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "value": - out.Values[i] = ec._FieldValuePair_value(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -38003,6 +38564,50 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "destinationFieldSearch": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_destinationFieldSearch(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "destinationFieldValueName": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_destinationFieldValueName(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "destinationDisplayInfo": field := field @@ -42253,6 +42858,11 @@ func (ec *executionContext) marshalNDestinationFieldConfig2ᚕgithubᚗcomᚋtar return ret } +func (ec *executionContext) unmarshalNDestinationFieldSearchInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldSearchInput(ctx context.Context, v interface{}) (DestinationFieldSearchInput, error) { + res, err := ec.unmarshalInputDestinationFieldSearchInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNDestinationFieldValidateInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐDestinationFieldValidateInput(ctx context.Context, v interface{}) (DestinationFieldValidateInput, error) { res, err := ec.unmarshalInputDestinationFieldValidateInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -42446,6 +43056,20 @@ func (ec *executionContext) marshalNEscalationPolicyStep2ᚕgithubᚗcomᚋtarge return ret } +func (ec *executionContext) marshalNFieldValueConnection2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueConnection(ctx context.Context, sel ast.SelectionSet, v FieldValueConnection) graphql.Marshaler { + return ec._FieldValueConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNFieldValueConnection2ᚖgithubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueConnection(ctx context.Context, sel ast.SelectionSet, v *FieldValueConnection) 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._FieldValueConnection(ctx, sel, v) +} + func (ec *executionContext) unmarshalNFieldValueInput2githubᚗcomᚋtargetᚋgoalertᚋgraphql2ᚐFieldValueInput(ctx context.Context, v interface{}) (FieldValueInput, error) { res, err := ec.unmarshalInputFieldValueInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/graphql2/graph/destinations.graphqls b/graphql2/graph/destinations.graphqls index 5e845b28eb..782c5a291b 100644 --- a/graphql2/graph/destinations.graphqls +++ b/graphql2/graph/destinations.graphqls @@ -1,20 +1,25 @@ extend type Query { - # destinationTypes returns a list of destination types that can be used for # notifications. destinationTypes: [DestinationTypeInfo!]! @experimental(flagName: "dest-types") - # destinationFieldValidate validates a destination field value as valid or invalid. # # It does not guarantee that the value is valid for the destination type, only # that it is valid for the field (i.e., syntax/formatting). destinationFieldValidate(input: DestinationFieldValidateInput!): Boolean! @experimental(flagName: "dest-types") - + destinationFieldSearch(input: DestinationFieldSearchInput!): FieldValueConnection! + destinationFieldValueName(input: DestinationFieldValidateInput!): String! # destinationDisplayInfo returns the display information for a destination. destinationDisplayInfo(input: DestinationInput!): DestinationDisplayInfo! @experimental(flagName: "dest-types") } +# FieldValueConnection is a connection to a list of FieldValuePairs. +type FieldValueConnection { + nodes: [FieldValuePair!]! + pageInfo: PageInfo! +} + input DestinationFieldValidateInput { destType: DestinationType! # the type of destination to validate fieldID: ID! # the ID of the input field to validate @@ -25,6 +30,15 @@ input DestinationFieldValidateInput { # notifications. scalar DestinationType +input DestinationFieldSearchInput { + destType: DestinationType! # the type of destination to search for + fieldID: ID! # the ID of the input field to search for + search: String # search string to match against + omit: [String!] # values/ids to omit from results + after: String # cursor to start search from + first: Int = 15 # number of results to return +} + # Destination represents a destination that can be used for notifications. type Destination { type: DestinationType! @@ -45,6 +59,9 @@ type FieldValuePair { fieldID: ID! # The ID of the input field that this value is for. value: String! # The value of the input field. + label: String! @goField(forceResolver: true) # The user-friendly text for this value of the input field (e.g., if the value is a user ID, label would be the user's name). + + isFavorite: Boolean! # if true, this value is a favorite for the user, only set for search results } input DestinationInput { diff --git a/graphql2/graphqlapp/destinationtypes.go b/graphql2/graphqlapp/destinationtypes.go index d06d30b0df..89c52f581c 100644 --- a/graphql2/graphqlapp/destinationtypes.go +++ b/graphql2/graphqlapp/destinationtypes.go @@ -35,6 +35,130 @@ const ( fieldScheduleID = "schedule-id" ) +type FieldValuePair App +type DestinationDisplayInfo App + +func (a *App) FieldValuePair() graphql2.FieldValuePairResolver { return (*FieldValuePair)(a) } + +func (a *FieldValuePair) Label(ctx context.Context, fvp *graphql2.FieldValuePair) (string, error) { + if fvp.Label != "" { + return fvp.Label, nil + } + + app := (*App)(a) + switch fvp.FieldID { + case fieldSlackChanID: + ch, err := app.SlackStore.Channel(ctx, fvp.Value) + if err != nil { + return "", err + } + return ch.Name, nil + case fieldSlackUGID: + ug, err := app.SlackStore.UserGroup(ctx, fvp.Value) + if err != nil { + return "", err + } + + return ug.Handle, nil + case fieldUserID: + u, err := app.FindOneUser(ctx, fvp.Value) + if err != nil { + return "", err + } + return u.Name, nil + case fieldRotationID: + r, err := app.FindOneRotation(ctx, fvp.Value) + if err != nil { + return "", err + } + return r.Name, nil + case fieldScheduleID: + s, err := app.FindOneSchedule(ctx, fvp.Value) + if err != nil { + return "", err + } + return s.Name, nil + } + + return "", validation.NewGenericError("unsupported fieldID") +} + +func (q *Query) DestinationFieldValueName(ctx context.Context, input graphql2.DestinationFieldValidateInput) (string, error) { + switch input.FieldID { + case fieldSlackChanID: + ch, err := q.SlackChannel(ctx, input.Value) + if err != nil { + return "", err + } + + return ch.Name, nil + case fieldSlackUGID: + ug, err := q.SlackUserGroup(ctx, input.Value) + if err != nil { + return "", err + } + + return ug.Handle, nil + } + + return "", validation.NewGenericError("unsupported fieldID") +} + +func (q *Query) DestinationFieldSearch(ctx context.Context, input graphql2.DestinationFieldSearchInput) (*graphql2.FieldValueConnection, error) { + switch input.FieldID { + case fieldSlackChanID: + res, err := q.SlackChannels(ctx, &graphql2.SlackChannelSearchOptions{ + Omit: input.Omit, + First: input.First, + Search: input.Search, + After: input.After, + }) + if err != nil { + return nil, err + } + + var nodes []graphql2.FieldValuePair + for _, c := range res.Nodes { + nodes = append(nodes, graphql2.FieldValuePair{ + FieldID: input.FieldID, + Value: c.ID, + Label: c.Name, + }) + } + + return &graphql2.FieldValueConnection{ + Nodes: nodes, + PageInfo: res.PageInfo, + }, nil + case fieldSlackUGID: + res, err := q.SlackUserGroups(ctx, &graphql2.SlackUserGroupSearchOptions{ + Omit: input.Omit, + First: input.First, + Search: input.Search, + After: input.After, + }) + if err != nil { + return nil, err + } + + var nodes []graphql2.FieldValuePair + for _, ug := range res.Nodes { + nodes = append(nodes, graphql2.FieldValuePair{ + FieldID: input.FieldID, + Value: ug.ID, + Label: ug.Handle, + }) + } + + return &graphql2.FieldValueConnection{ + Nodes: nodes, + PageInfo: res.PageInfo, + }, nil + } + + return nil, validation.NewGenericError("unsupported fieldID") +} + func (q *Query) DestinationFieldValidate(ctx context.Context, input graphql2.DestinationFieldValidateInput) (bool, error) { switch input.DestType { case destTwilioSMS, destTwilioVoice: diff --git a/graphql2/models_gen.go b/graphql2/models_gen.go index 383d7ee47f..d2c798ae7a 100644 --- a/graphql2/models_gen.go +++ b/graphql2/models_gen.go @@ -318,6 +318,15 @@ type DestinationFieldConfig struct { SupportsValidation bool `json:"supportsValidation"` } +type DestinationFieldSearchInput struct { + DestType string `json:"destType"` + FieldID string `json:"fieldID"` + Search *string `json:"search,omitempty"` + Omit []string `json:"omit,omitempty"` + After *string `json:"after,omitempty"` + First *int `json:"first,omitempty"` +} + type DestinationFieldValidateInput struct { DestType string `json:"destType"` FieldID string `json:"fieldID"` @@ -357,14 +366,21 @@ type EscalationPolicySearchOptions struct { FavoritesFirst *bool `json:"favoritesFirst,omitempty"` } +type FieldValueConnection struct { + Nodes []FieldValuePair `json:"nodes"` + PageInfo *PageInfo `json:"pageInfo"` +} + type FieldValueInput struct { FieldID string `json:"fieldID"` Value string `json:"value"` } type FieldValuePair struct { - FieldID string `json:"fieldID"` - Value string `json:"value"` + FieldID string `json:"fieldID"` + Value string `json:"value"` + Label string `json:"label"` + IsFavorite bool `json:"isFavorite"` } type GQLAPIKey struct { diff --git a/web/src/schema.d.ts b/web/src/schema.d.ts index 3519098771..4269ff2e40 100644 --- a/web/src/schema.d.ts +++ b/web/src/schema.d.ts @@ -366,6 +366,15 @@ export interface DestinationFieldConfig { supportsValidation: boolean } +export interface DestinationFieldSearchInput { + after?: null | string + destType: DestinationType + fieldID: string + first?: null | number + omit?: null | string[] + search?: null | string +} + export interface DestinationFieldValidateInput { destType: DestinationType fieldID: string @@ -426,6 +435,11 @@ export interface EscalationPolicyStep { targets: Target[] } +export interface FieldValueConnection { + nodes: FieldValuePair[] + pageInfo: PageInfo +} + export interface FieldValueInput { fieldID: string value: string @@ -433,6 +447,8 @@ export interface FieldValueInput { export interface FieldValuePair { fieldID: string + isFavorite: boolean + label: string value: string } @@ -691,7 +707,9 @@ export interface Query { debugMessageStatus: DebugMessageStatusInfo debugMessages: DebugMessage[] destinationDisplayInfo: DestinationDisplayInfo + destinationFieldSearch: FieldValueConnection destinationFieldValidate: boolean + destinationFieldValueName: string destinationTypes: DestinationTypeInfo[] escalationPolicies: EscalationPolicyConnection escalationPolicy?: null | EscalationPolicy From c70d2dc0d9db3830029922d34b3d85a7d0677c72 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 15:33:13 -0600 Subject: [PATCH 2/8] add search select components --- .../app/selection/DestinationSearchSelect.tsx | 128 ++++++++++++++++++ web/src/app/selection/MaterialSelect.tsx | 2 +- 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 web/src/app/selection/DestinationSearchSelect.tsx diff --git a/web/src/app/selection/DestinationSearchSelect.tsx b/web/src/app/selection/DestinationSearchSelect.tsx new file mode 100644 index 0000000000..cb0f10f1cb --- /dev/null +++ b/web/src/app/selection/DestinationSearchSelect.tsx @@ -0,0 +1,128 @@ +import React, { useState } from 'react' +import { useQuery, gql } from 'urql' +import { + DestinationFieldConfig, + DestinationType, + FieldValueConnection, +} from '../../schema' +import MaterialSelect from './MaterialSelect' +import { FavoriteIcon } from '../util/SetFavoriteButton' + +const searchOptionsQuery = gql` + query DestinationSearchSelect($input: DestinationFieldSearchInput!) { + destinationFieldSearch(input: $input) { + nodes { + value + label + isFavorite + } + } + } +` + +const selectedLabelQuery = gql` + query DestinationFieldValueName($input: DestinationFieldValidateInput!) { + destinationFieldValueName(input: $input) + } +` + +const noSuspense = { suspense: false } + +export type DestinationSearchSelectProps = { + value: string + onChange?: (newValue: string) => void + config: DestinationFieldConfig + destType: DestinationType + + disabled?: boolean +} + +const cacheByJSON = {} + +function cachify(val: T): T { + const json = JSON.stringify(val) + if (cacheByJSON[json]) return cacheByJSON[json] + cacheByJSON[json] = val + + return val +} + +export default function DestinationSearchSelect( + props: DestinationSearchSelectProps, +): JSX.Element { + const [inputValue, setInputValue] = useState('') + + // check validation of the input phoneNumber through graphql + const [{ data, fetching, error }] = useQuery<{ + destinationFieldSearch: FieldValueConnection + }>({ + query: searchOptionsQuery, + variables: { + input: { + destType: props.destType, + search: inputValue, + fieldID: props.config.fieldID, + }, + }, + requestPolicy: 'cache-first', + pause: props.disabled, + context: noSuspense, + }) + const options = data?.destinationFieldSearch.nodes || [] + + const [{ data: selectedLabelData }] = useQuery<{ + destinationFieldValueName: string + }>({ + query: selectedLabelQuery, + variables: { + input: { + destType: props.destType, + value: props.value, + fieldID: props.config.fieldID, + }, + }, + requestPolicy: 'cache-first', + pause: props.disabled || !props.value, + context: noSuspense, + }) + const selectedLabel = selectedLabelData?.destinationFieldValueName || '' + + interface SelectOption { + value: string + label: string + } + + function handleChange(val: SelectOption | SelectOption[]): void { + if (!props.onChange) return + + // should not be possible since multiple is false + if (Array.isArray(val)) throw new Error('Multiple values not supported') + + props.onChange(val.value) + } + + const value = props.value + ? { label: selectedLabel, value: props.value } + : null + + return ( + setInputValue(val)} + value={value as unknown as SelectOption} + label={props.config.labelSingular} + options={options + .map((opt) => ({ + label: opt.label, + value: opt.value, + icon: opt.isFavorite ? : undefined, + })) + .map(cachify)} + placeholder='Start typing...' + onChange={handleChange} + /> + ) +} diff --git a/web/src/app/selection/MaterialSelect.tsx b/web/src/app/selection/MaterialSelect.tsx index 738b8f4e77..7e3a06802f 100644 --- a/web/src/app/selection/MaterialSelect.tsx +++ b/web/src/app/selection/MaterialSelect.tsx @@ -82,7 +82,7 @@ interface CommonSelectProps { interface SingleSelectProps { multiple: false - value: SelectOption + value: SelectOption | null onChange: (value: SelectOption | null) => void } From 67d3430d5486cc4dbb920341b0dd01166fffc5d3 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 15:33:24 -0600 Subject: [PATCH 3/8] add initial stories --- .../DestinationSearchSelect.stories.tsx | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 web/src/app/selection/DestinationSearchSelect.stories.tsx diff --git a/web/src/app/selection/DestinationSearchSelect.stories.tsx b/web/src/app/selection/DestinationSearchSelect.stories.tsx new file mode 100644 index 0000000000..cf34444984 --- /dev/null +++ b/web/src/app/selection/DestinationSearchSelect.stories.tsx @@ -0,0 +1,79 @@ +import React from 'react' +import type { Meta, StoryObj } from '@storybook/react' +import DestinationSearchSelect from './DestinationSearchSelect' +import { expect } from '@storybook/jest' +import { within } from '@storybook/testing-library' +import { handleDefaultConfig } from '../storybook/graphql' +import { HttpResponse, graphql } from 'msw' + +const meta = { + title: 'util/DestinationSearchSelect', + component: DestinationSearchSelect, + render: function Component(args) { + return + }, + tags: ['autodocs'], + parameters: { + msw: { + handlers: [ + handleDefaultConfig, + graphql.query('DestinationSearchSelect', () => { + return HttpResponse.json({ + data: { + destinationFieldSearch: { + nodes: [ + { + value: 'C03SJES5FA7', + label: '#general', + isFavorite: false, + __typename: 'FieldValuePair', + }, + ], + __typename: 'FieldValueConnection', + }, + }, + }) + }), + graphql.query('DestinationFieldValueName', ({ variables: vars }) => { + return HttpResponse.json({ + data: { + destinationFieldValueName: + vars.input.value === 'C03SJES5FA7' ? '#general' : '', + }, + }) + }), + ], + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Render: Story = { + args: { + value: '', + config: { + fieldID: 'slack-channel-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Slack Channels', + labelSingular: 'Slack Channel', + placeholderText: '', + prefix: '', + supportsValidation: false, + }, + destType: 'builtin-slack-channel', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + // should see #general channel as option + await userEvent.type( + canvas.getByPlaceholderText('Start typing...'), + '#general', + ) + }, +} From c130a002372a2a1e9daca82efbb8e0f743d7c4d4 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 15:49:15 -0600 Subject: [PATCH 4/8] fix types --- .../app/selection/DestinationSearchSelect.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/web/src/app/selection/DestinationSearchSelect.tsx b/web/src/app/selection/DestinationSearchSelect.tsx index cb0f10f1cb..290c2fad08 100644 --- a/web/src/app/selection/DestinationSearchSelect.tsx +++ b/web/src/app/selection/DestinationSearchSelect.tsx @@ -9,7 +9,7 @@ import MaterialSelect from './MaterialSelect' import { FavoriteIcon } from '../util/SetFavoriteButton' const searchOptionsQuery = gql` - query DestinationSearchSelect($input: DestinationFieldSearchInput!) { + query DestinationFieldSearch($input: DestinationFieldSearchInput!) { destinationFieldSearch(input: $input) { nodes { value @@ -28,20 +28,19 @@ const selectedLabelQuery = gql` const noSuspense = { suspense: false } -export type DestinationSearchSelectProps = { +export type DestinationSearchSelectProps = DestinationFieldConfig & { value: string onChange?: (newValue: string) => void - config: DestinationFieldConfig destType: DestinationType disabled?: boolean } -const cacheByJSON = {} +const cacheByJSON: Record = {} function cachify(val: T): T { const json = JSON.stringify(val) - if (cacheByJSON[json]) return cacheByJSON[json] + if (cacheByJSON[json]) return cacheByJSON[json] as T cacheByJSON[json] = val return val @@ -61,7 +60,7 @@ export default function DestinationSearchSelect( input: { destType: props.destType, search: inputValue, - fieldID: props.config.fieldID, + fieldID: props.fieldID, }, }, requestPolicy: 'cache-first', @@ -78,7 +77,7 @@ export default function DestinationSearchSelect( input: { destType: props.destType, value: props.value, - fieldID: props.config.fieldID, + fieldID: props.fieldID, }, }, requestPolicy: 'cache-first', @@ -92,13 +91,13 @@ export default function DestinationSearchSelect( label: string } - function handleChange(val: SelectOption | SelectOption[]): void { + function handleChange(val: SelectOption | null): void { if (!props.onChange) return // should not be possible since multiple is false if (Array.isArray(val)) throw new Error('Multiple values not supported') - props.onChange(val.value) + props.onChange(val?.value || '') } const value = props.value @@ -113,7 +112,7 @@ export default function DestinationSearchSelect( noOptionsError={error} onInputChange={(val) => setInputValue(val)} value={value as unknown as SelectOption} - label={props.config.labelSingular} + label={props.labelSingular} options={options .map((opt) => ({ label: opt.label, From df8467bdd0bd52c1771db4c0af7b9338e93f377e Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 17:44:16 -0600 Subject: [PATCH 5/8] add additional stories --- .../DestinationSearchSelect.stories.tsx | 126 ++++++++++++++---- 1 file changed, 99 insertions(+), 27 deletions(-) diff --git a/web/src/app/selection/DestinationSearchSelect.stories.tsx b/web/src/app/selection/DestinationSearchSelect.stories.tsx index cf34444984..fb36507d30 100644 --- a/web/src/app/selection/DestinationSearchSelect.stories.tsx +++ b/web/src/app/selection/DestinationSearchSelect.stories.tsx @@ -5,26 +5,38 @@ import { expect } from '@storybook/jest' import { within } from '@storybook/testing-library' import { handleDefaultConfig } from '../storybook/graphql' import { HttpResponse, graphql } from 'msw' +import { useArgs } from '@storybook/preview-api' const meta = { title: 'util/DestinationSearchSelect', component: DestinationSearchSelect, render: function Component(args) { - return + const [, setArgs] = useArgs() + const onChange = (value: string): void => { + if (args.onChange) args.onChange(value) + setArgs({ value }) + } + return }, tags: ['autodocs'], parameters: { msw: { handlers: [ handleDefaultConfig, - graphql.query('DestinationSearchSelect', () => { + graphql.query('DestinationFieldSearch', () => { return HttpResponse.json({ data: { destinationFieldSearch: { nodes: [ { - value: 'C03SJES5FA7', - label: '#general', + value: 'value-id-1', + label: '#value-one', + isFavorite: false, + __typename: 'FieldValuePair', + }, + { + value: 'value-id-2', + label: '#value-two', isFavorite: false, __typename: 'FieldValuePair', }, @@ -35,10 +47,13 @@ const meta = { }) }), graphql.query('DestinationFieldValueName', ({ variables: vars }) => { + const names: Record = { + 'value-id-1': '#value-one', + 'value-id-2': '#value-two', + } return HttpResponse.json({ data: { - destinationFieldValueName: - vars.input.value === 'C03SJES5FA7' ? '#general' : '', + destinationFieldValueName: names[vars.input.value] || '', }, }) }), @@ -50,30 +65,87 @@ const meta = { export default meta type Story = StoryObj -export const Render: Story = { +export const EmptyValue: Story = { args: { value: '', - config: { - fieldID: 'slack-channel-id', - hint: '', - hintURL: '', - inputType: 'text', - isSearchSelectable: true, - labelPlural: 'Slack Channels', - labelSingular: 'Slack Channel', - placeholderText: '', - prefix: '', - supportsValidation: false, - }, - destType: 'builtin-slack-channel', + + fieldID: 'field-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Select Values', + labelSingular: 'Select Value', + placeholderText: 'asdf', + prefix: '', + supportsValidation: false, + + destType: 'test-type', + }, + // play: async ({ canvasElement }) => { + // const canvas = within(canvasElement) + + // // should see #general channel as option + // await userEvent.type( + // canvas.getByPlaceholderText('Start typing...'), + // '#general', + // ) + // }, +} + +export const SelectedValue: Story = { + args: { + value: 'value-id-1', + + fieldID: 'field-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Select Values', + labelSingular: 'Select Value', + placeholderText: '', + prefix: '', + supportsValidation: false, + + destType: 'test-type', }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement) + // play: async ({ canvasElement }) => { + // const canvas = within(canvasElement) + + // // should see #general channel as option + // await userEvent.type( + // canvas.getByPlaceholderText('Start typing...'), + // '#general', + // ) + // }, +} + +export const Disabled: Story = { + args: { + value: 'value-id-1', - // should see #general channel as option - await userEvent.type( - canvas.getByPlaceholderText('Start typing...'), - '#general', - ) + fieldID: 'field-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Select Values', + labelSingular: 'Select Value', + placeholderText: '', + prefix: '', + supportsValidation: false, + + destType: 'test-type', + disabled: true, }, + // play: async ({ canvasElement }) => { + // const canvas = within(canvasElement) + + // // should see #general channel as option + // await userEvent.type( + // canvas.getByPlaceholderText('Start typing...'), + // '#general', + // ) + // }, } From 014e478d985b20565659d1a878c381fe3eb4e26c Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 17:44:26 -0600 Subject: [PATCH 6/8] fix disable behavior --- web/src/app/selection/DestinationSearchSelect.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/app/selection/DestinationSearchSelect.tsx b/web/src/app/selection/DestinationSearchSelect.tsx index 290c2fad08..8f1eed0bde 100644 --- a/web/src/app/selection/DestinationSearchSelect.tsx +++ b/web/src/app/selection/DestinationSearchSelect.tsx @@ -81,7 +81,7 @@ export default function DestinationSearchSelect( }, }, requestPolicy: 'cache-first', - pause: props.disabled || !props.value, + pause: !props.value, context: noSuspense, }) const selectedLabel = selectedLabelData?.destinationFieldValueName || '' @@ -109,6 +109,7 @@ export default function DestinationSearchSelect( isLoading={fetching} multiple={false} noOptionsText='No options' + disabled={props.disabled} noOptionsError={error} onInputChange={(val) => setInputValue(val)} value={value as unknown as SelectOption} From 0bcfd2dfcf1e82c474a1bb1a2108bf46686647cf Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 18:40:43 -0600 Subject: [PATCH 7/8] handle errors --- .../DestinationSearchSelect.stories.tsx | 98 +++++++++++++------ .../app/selection/DestinationSearchSelect.tsx | 8 +- 2 files changed, 73 insertions(+), 33 deletions(-) diff --git a/web/src/app/selection/DestinationSearchSelect.stories.tsx b/web/src/app/selection/DestinationSearchSelect.stories.tsx index fb36507d30..1b9c387981 100644 --- a/web/src/app/selection/DestinationSearchSelect.stories.tsx +++ b/web/src/app/selection/DestinationSearchSelect.stories.tsx @@ -2,7 +2,7 @@ import React from 'react' import type { Meta, StoryObj } from '@storybook/react' import DestinationSearchSelect from './DestinationSearchSelect' import { expect } from '@storybook/jest' -import { within } from '@storybook/testing-library' +import { userEvent, within } from '@storybook/testing-library' import { handleDefaultConfig } from '../storybook/graphql' import { HttpResponse, graphql } from 'msw' import { useArgs } from '@storybook/preview-api' @@ -23,7 +23,13 @@ const meta = { msw: { handlers: [ handleDefaultConfig, - graphql.query('DestinationFieldSearch', () => { + graphql.query('DestinationFieldSearch', ({ variables: vars }) => { + if (vars.input.search === 'query-error') { + return HttpResponse.json({ + errors: [{ message: 'some_backend_error_message' }], + }) + } + return HttpResponse.json({ data: { destinationFieldSearch: { @@ -47,6 +53,12 @@ const meta = { }) }), graphql.query('DestinationFieldValueName', ({ variables: vars }) => { + if (vars.input.value === 'invalid-value') { + return HttpResponse.json({ + errors: [{ message: 'some_backend_error_message' }], + }) + } + const names: Record = { 'value-id-1': '#value-one', 'value-id-2': '#value-two', @@ -65,7 +77,7 @@ const meta = { export default meta type Story = StoryObj -export const EmptyValue: Story = { +export const Default: Story = { args: { value: '', @@ -82,18 +94,9 @@ export const EmptyValue: Story = { destType: 'test-type', }, - // play: async ({ canvasElement }) => { - // const canvas = within(canvasElement) - - // // should see #general channel as option - // await userEvent.type( - // canvas.getByPlaceholderText('Start typing...'), - // '#general', - // ) - // }, } -export const SelectedValue: Story = { +export const OptionSelected: Story = { args: { value: 'value-id-1', @@ -110,15 +113,6 @@ export const SelectedValue: Story = { destType: 'test-type', }, - // play: async ({ canvasElement }) => { - // const canvas = within(canvasElement) - - // // should see #general channel as option - // await userEvent.type( - // canvas.getByPlaceholderText('Start typing...'), - // '#general', - // ) - // }, } export const Disabled: Story = { @@ -139,13 +133,55 @@ export const Disabled: Story = { destType: 'test-type', disabled: true, }, - // play: async ({ canvasElement }) => { - // const canvas = within(canvasElement) - - // // should see #general channel as option - // await userEvent.type( - // canvas.getByPlaceholderText('Start typing...'), - // '#general', - // ) - // }, +} + +export const InvalidOptionSelected: Story = { + args: { + value: 'invalid-value', + + fieldID: 'field-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Select Values', + labelSingular: 'Select Value', + placeholderText: '', + prefix: '', + supportsValidation: false, + + destType: 'test-type', + }, +} + +export const QueryError: Story = { + args: { + value: '', + + fieldID: 'field-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Select Values', + labelSingular: 'Select Value', + placeholderText: '', + prefix: '', + supportsValidation: false, + + destType: 'test-type', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + const box = await canvas.findByRole('combobox') + await userEvent.click(box) + await userEvent.type(box, 'query-error', { delay: null }) + + expect( + await within(document.body).findByText( + '[GraphQL] some_backend_error_message', + ), + ).toBeVisible() + }, } diff --git a/web/src/app/selection/DestinationSearchSelect.tsx b/web/src/app/selection/DestinationSearchSelect.tsx index 8f1eed0bde..1f43f0c529 100644 --- a/web/src/app/selection/DestinationSearchSelect.tsx +++ b/web/src/app/selection/DestinationSearchSelect.tsx @@ -69,7 +69,7 @@ export default function DestinationSearchSelect( }) const options = data?.destinationFieldSearch.nodes || [] - const [{ data: selectedLabelData }] = useQuery<{ + const [{ data: selectedLabelData, error: selectedErr }] = useQuery<{ destinationFieldValueName: string }>({ query: selectedLabelQuery, @@ -84,7 +84,11 @@ export default function DestinationSearchSelect( pause: !props.value, context: noSuspense, }) - const selectedLabel = selectedLabelData?.destinationFieldValueName || '' + + let selectedLabel = selectedLabelData?.destinationFieldValueName || '' + if (selectedErr) { + selectedLabel = `ERROR: ${selectedErr.message}` + } interface SelectOption { value: string From e9d4d6b8119c0ab2a7d3c3fad910e3ebc07a06d7 Mon Sep 17 00:00:00 2001 From: Nathaniel Caza Date: Thu, 11 Jan 2024 18:58:47 -0600 Subject: [PATCH 8/8] fix hint and error --- .../DestinationSearchSelect.stories.tsx | 53 +++++++++++++++++-- .../app/selection/DestinationSearchSelect.tsx | 20 +++++++ web/src/app/selection/MaterialSelect.tsx | 2 + 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/web/src/app/selection/DestinationSearchSelect.stories.tsx b/web/src/app/selection/DestinationSearchSelect.stories.tsx index 1b9c387981..43f4b81254 100644 --- a/web/src/app/selection/DestinationSearchSelect.stories.tsx +++ b/web/src/app/selection/DestinationSearchSelect.stories.tsx @@ -6,10 +6,18 @@ import { userEvent, within } from '@storybook/testing-library' import { handleDefaultConfig } from '../storybook/graphql' import { HttpResponse, graphql } from 'msw' import { useArgs } from '@storybook/preview-api' +import { FieldValueConnection } from '../../schema' const meta = { title: 'util/DestinationSearchSelect', component: DestinationSearchSelect, + argTypes: { + inputType: { table: { disable: true } }, + placeholderText: { table: { disable: true } }, + isSearchSelectable: { table: { disable: true } }, + supportsValidation: { table: { disable: true } }, + prefix: { table: { disable: true } }, + }, render: function Component(args) { const [, setArgs] = useArgs() const onChange = (value: string): void => { @@ -29,27 +37,38 @@ const meta = { errors: [{ message: 'some_backend_error_message' }], }) } + if (vars.input.search === 'empty') { + return HttpResponse.json({ + data: { + destinationFieldSearch: { + nodes: [], + __typename: 'FieldValueConnection', + }, + }, + }) + } return HttpResponse.json({ data: { destinationFieldSearch: { nodes: [ { + fieldID: 'field-id', value: 'value-id-1', label: '#value-one', isFavorite: false, - __typename: 'FieldValuePair', }, { + fieldID: 'field-id', value: 'value-id-2', label: '#value-two', isFavorite: false, - __typename: 'FieldValuePair', }, ], - __typename: 'FieldValueConnection', }, }, + } satisfies { + data: { destinationFieldSearch: Partial } }) }), graphql.query('DestinationFieldValueName', ({ variables: vars }) => { @@ -154,6 +173,34 @@ export const InvalidOptionSelected: Story = { }, } +export const NoOptions: Story = { + args: { + value: '', + + fieldID: 'field-id', + hint: '', + hintURL: '', + inputType: 'text', + isSearchSelectable: true, + labelPlural: 'Select Values', + labelSingular: 'Select Value', + placeholderText: '', + prefix: '', + supportsValidation: false, + + destType: 'test-type', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement) + + const box = await canvas.findByRole('combobox') + await userEvent.click(box) + await userEvent.type(box, 'empty', { delay: null }) + + expect(await within(document.body).findByText('No options')).toBeVisible() + }, +} + export const QueryError: Story = { args: { value: '', diff --git a/web/src/app/selection/DestinationSearchSelect.tsx b/web/src/app/selection/DestinationSearchSelect.tsx index 1f43f0c529..5eec03bbe0 100644 --- a/web/src/app/selection/DestinationSearchSelect.tsx +++ b/web/src/app/selection/DestinationSearchSelect.tsx @@ -7,6 +7,7 @@ import { } from '../../schema' import MaterialSelect from './MaterialSelect' import { FavoriteIcon } from '../util/SetFavoriteButton' +import AppLink from '../util/AppLink' const searchOptionsQuery = gql` query DestinationFieldSearch($input: DestinationFieldSearchInput!) { @@ -34,6 +35,7 @@ export type DestinationSearchSelectProps = DestinationFieldConfig & { destType: DestinationType disabled?: boolean + error?: boolean } const cacheByJSON: Record = {} @@ -46,6 +48,14 @@ function cachify(val: T): T { return val } +/** + * DestinationSearchSelect is a select field that allows the user to select a + * destination from a list of options. + * + * You should almost never use this component directly. Instead, use + * DestinationField, which will select the correct component based on the + * destination type. + */ export default function DestinationSearchSelect( props: DestinationSearchSelectProps, ): JSX.Element { @@ -115,9 +125,19 @@ export default function DestinationSearchSelect( noOptionsText='No options' disabled={props.disabled} noOptionsError={error} + error={props.error} onInputChange={(val) => setInputValue(val)} value={value as unknown as SelectOption} label={props.labelSingular} + helperText={ + props.hintURL ? ( + + {props.hint} + + ) : ( + props.hint + ) + } options={options .map((opt) => ({ label: opt.label, diff --git a/web/src/app/selection/MaterialSelect.tsx b/web/src/app/selection/MaterialSelect.tsx index 7e3a06802f..1355442fdf 100644 --- a/web/src/app/selection/MaterialSelect.tsx +++ b/web/src/app/selection/MaterialSelect.tsx @@ -78,6 +78,7 @@ interface CommonSelectProps { clientSideFilter?: boolean disableCloseOnSelect?: boolean optionsLimit?: number + helperText?: ReactNode } interface SingleSelectProps { @@ -251,6 +252,7 @@ export default function MaterialSelect( const newInputVal: string = target.value setInputValue(newInputVal) }} + helperText={props.helperText} error={error} /> )