Skip to content

Commit 52b3349

Browse files
vertex451Artem Shcherbatiuk
andauthored
tests(gateway): subscription.go (#86)
* resolver tests * subscription tests * introduced builder patter to field config args * better validation --------- Co-authored-by: Artem Shcherbatiuk <[email protected]>
1 parent d3527f6 commit 52b3349

File tree

9 files changed

+299
-349
lines changed

9 files changed

+299
-349
lines changed

gateway/resolver/arguments.go

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package resolver
22

3-
import "github.com/graphql-go/graphql"
3+
import (
4+
"errors"
5+
"github.com/graphql-go/graphql"
6+
"github.com/rs/zerolog/log"
7+
)
48

59
const (
610
LabelSelectorArg = "labelselector"
@@ -67,3 +71,67 @@ func (b *FieldConfigArgumentsBuilder) WithSubscribeToAllArg() *FieldConfigArgume
6771
func (b *FieldConfigArgumentsBuilder) Complete() graphql.FieldConfigArgument {
6872
return b.arguments
6973
}
74+
75+
func getRequiredNameAndNamespaceArgs(args map[string]interface{}) (string, string, error) {
76+
name, err := getStringArg(args, NameArg, true)
77+
if err != nil {
78+
return "", "", err
79+
}
80+
81+
namespace, err := getStringArg(args, NamespaceArg, true)
82+
if err != nil {
83+
return "", "", err
84+
}
85+
86+
return name, namespace, nil
87+
}
88+
89+
func getStringArg(args map[string]interface{}, key string, required bool) (string, error) {
90+
val, exists := args[key]
91+
if !exists {
92+
if required {
93+
err := errors.New("missing required argument: " + key)
94+
log.Error().Err(err).Msg(key + " argument is required")
95+
return "", err
96+
}
97+
98+
return "", nil
99+
}
100+
101+
str, ok := val.(string)
102+
if !ok {
103+
err := errors.New("invalid type for argument: " + key)
104+
log.Error().Err(err).Msg(key + " argument must be a string")
105+
return "", err
106+
}
107+
108+
if str == "" {
109+
err := errors.New("empty value for argument: " + key)
110+
log.Error().Err(err).Msg(key + " argument cannot be empty")
111+
return "", err
112+
}
113+
114+
return str, nil
115+
}
116+
117+
func getBoolArg(args map[string]interface{}, key string, required bool) (bool, error) {
118+
val, exists := args[key]
119+
if !exists {
120+
if required {
121+
err := errors.New("missing required argument: " + key)
122+
log.Error().Err(err).Msg(key + " argument is required")
123+
return false, err
124+
}
125+
126+
return false, nil
127+
}
128+
129+
res, ok := val.(bool)
130+
if !ok {
131+
err := errors.New("invalid type for argument: " + key)
132+
log.Error().Err(err).Msg(key + " argument must be a bool")
133+
return false, err
134+
}
135+
136+
return res, nil
137+
}

gateway/resolver/resolver.go

Lines changed: 21 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8-
"regexp"
9-
10-
"github.com/rs/zerolog/log"
118
"gopkg.in/yaml.v3"
9+
"regexp"
1210

1311
"github.com/graphql-go/graphql"
1412
"go.opentelemetry.io/otel"
@@ -25,7 +23,8 @@ import (
2523

2624
type Provider interface {
2725
CrudProvider
28-
FieldResolverProvider
26+
CommonResolver() graphql.FieldResolveFn
27+
SanitizeGroupName(string) string
2928
}
3029

3130
type CrudProvider interface {
@@ -39,15 +38,10 @@ type CrudProvider interface {
3938
SubscribeItems(gvk schema.GroupVersionKind) graphql.FieldResolveFn
4039
}
4140

42-
type FieldResolverProvider interface {
43-
CommonResolver() graphql.FieldResolveFn
44-
SanitizeGroupName(string) string
45-
GetOriginalGroupName(string) string
46-
}
47-
4841
type Service struct {
49-
log *logger.Logger
50-
groupNames map[string]string
42+
log *logger.Logger
43+
// groupNames stores relation between sanitized group names and original group names that are used in the Kubernetes API
44+
groupNames map[string]string // map[sanitizedGroupName]originalGroupName
5145
runtimeClient client.WithWatch
5246
}
5347

@@ -65,7 +59,7 @@ func (r *Service) ListItems(gvk schema.GroupVersionKind) graphql.FieldResolveFn
6559
ctx, span := otel.Tracer("").Start(p.Context, "ListItems", trace.WithAttributes(attribute.String("kind", gvk.Kind)))
6660
defer span.End()
6761

68-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
62+
gvk.Group = r.getOriginalGroupName(gvk.Group)
6963

7064
log, err := r.log.ChildLoggerWithAttributes(
7165
"operation", "list",
@@ -119,7 +113,7 @@ func (r *Service) GetItem(gvk schema.GroupVersionKind) graphql.FieldResolveFn {
119113
ctx, span := otel.Tracer("").Start(p.Context, "GetItem", trace.WithAttributes(attribute.String("kind", gvk.Kind)))
120114
defer span.End()
121115

122-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
116+
gvk.Group = r.getOriginalGroupName(gvk.Group)
123117

124118
log, err := r.log.ChildLoggerWithAttributes(
125119
"operation", "get",
@@ -134,7 +128,7 @@ func (r *Service) GetItem(gvk schema.GroupVersionKind) graphql.FieldResolveFn {
134128
}
135129

136130
// Retrieve required arguments
137-
name, namespace, err := getNameAndNamespace(p.Args)
131+
name, namespace, err := getRequiredNameAndNamespaceArgs(p.Args)
138132
if err != nil {
139133
return nil, err
140134
}
@@ -181,7 +175,7 @@ func (r *Service) CreateItem(gvk schema.GroupVersionKind) graphql.FieldResolveFn
181175
ctx, span := otel.Tracer("").Start(p.Context, "CreateItem", trace.WithAttributes(attribute.String("kind", gvk.Kind)))
182176
defer span.End()
183177

184-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
178+
gvk.Group = r.getOriginalGroupName(gvk.Group)
185179

186180
log := r.log.With().Str("operation", "create").Str("kind", gvk.Kind).Logger()
187181

@@ -212,11 +206,11 @@ func (r *Service) UpdateItem(gvk schema.GroupVersionKind) graphql.FieldResolveFn
212206
ctx, span := otel.Tracer("").Start(p.Context, "UpdateItem", trace.WithAttributes(attribute.String("kind", gvk.Kind)))
213207
defer span.End()
214208

215-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
209+
gvk.Group = r.getOriginalGroupName(gvk.Group)
216210

217211
log := r.log.With().Str("operation", "update").Str("kind", gvk.Kind).Logger()
218212

219-
name, namespace, err := getNameAndNamespace(p.Args)
213+
name, namespace, err := getRequiredNameAndNamespaceArgs(p.Args)
220214
if err != nil {
221215
return nil, err
222216
}
@@ -256,11 +250,11 @@ func (r *Service) DeleteItem(gvk schema.GroupVersionKind) graphql.FieldResolveFn
256250
ctx, span := otel.Tracer("").Start(p.Context, "DeleteItem", trace.WithAttributes(attribute.String("kind", gvk.Kind)))
257251
defer span.End()
258252

259-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
253+
gvk.Group = r.getOriginalGroupName(gvk.Group)
260254

261255
log := r.log.With().Str("operation", "delete").Str("kind", gvk.Kind).Logger()
262256

263-
name, namespace, err := getNameAndNamespace(p.Args)
257+
name, namespace, err := getRequiredNameAndNamespaceArgs(p.Args)
264258
if err != nil {
265259
return nil, err
266260
}
@@ -286,7 +280,7 @@ func (r *Service) CommonResolver() graphql.FieldResolveFn {
286280
}
287281

288282
func (r *Service) SanitizeGroupName(groupName string) string {
289-
oldGroupName := groupName
283+
originalGroupName := groupName
290284

291285
if groupName == "" {
292286
groupName = "core"
@@ -298,53 +292,19 @@ func (r *Service) SanitizeGroupName(groupName string) string {
298292
}
299293
}
300294

301-
r.groupNames[groupName] = oldGroupName
295+
r.storeOriginalGroupName(groupName, originalGroupName)
302296

303297
return groupName
304298
}
305299

306-
func (r *Service) GetOriginalGroupName(groupName string) string {
300+
func (r *Service) storeOriginalGroupName(groupName, originalName string) {
301+
r.groupNames[groupName] = originalName
302+
}
303+
304+
func (r *Service) getOriginalGroupName(groupName string) string {
307305
if originalName, ok := r.groupNames[groupName]; ok {
308306
return originalName
309307
}
310308

311309
return groupName
312310
}
313-
314-
func getNameAndNamespace(args map[string]interface{}) (string, string, error) {
315-
name, err := getStringArg(args, NameArg)
316-
if err != nil {
317-
return "", "", err
318-
}
319-
320-
namespace, err := getStringArg(args, NamespaceArg)
321-
if err != nil {
322-
return "", "", err
323-
}
324-
325-
return name, namespace, nil
326-
}
327-
328-
func getStringArg(args map[string]interface{}, key string) (string, error) {
329-
val, exists := args[key]
330-
if !exists {
331-
err := errors.New("missing required argument: " + key)
332-
log.Error().Err(err).Msg(key + " argument is required")
333-
return "", err
334-
}
335-
336-
str, ok := val.(string)
337-
if !ok {
338-
err := errors.New("invalid type for argument: " + key)
339-
log.Error().Err(err).Msg(key + " argument must be a string")
340-
return "", err
341-
}
342-
343-
if str == "" {
344-
err := errors.New("empty value for argument: " + key)
345-
log.Error().Err(err).Msg(key + " argument cannot be empty")
346-
return "", err
347-
}
348-
349-
return str, nil
350-
}

gateway/resolver/resolver_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ func TestGetOriginalGroupName(t *testing.T) {
528528

529529
for _, tt := range tests {
530530
t.Run(tt.name, func(t *testing.T) {
531-
result := r.GetOriginalGroupName(tt.input)
531+
result := r.getOriginalGroupName(tt.input)
532532
assert.Equal(t, tt.expected, result)
533533
})
534534
}

gateway/resolver/subscription.go

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package resolver
22

33
import (
4-
"context"
54
"reflect"
65
"strings"
76

@@ -17,50 +16,67 @@ import (
1716

1817
func (r *Service) SubscribeItem(gvk schema.GroupVersionKind) graphql.FieldResolveFn {
1918
return func(p graphql.ResolveParams) (interface{}, error) {
20-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
21-
22-
ctx := p.Context
23-
namespace, _ := p.Args[NamespaceArg].(string)
24-
name, _ := p.Args["name"].(string)
25-
labelSelector, _ := p.Args[LabelSelectorArg].(string)
26-
subscribeToAll, _ := p.Args["subscribeToAll"].(bool)
27-
fieldsToWatch := extractRequestedFields(p.Info)
2819

2920
resultChannel := make(chan interface{})
30-
go r.runWatch(ctx, gvk, namespace, name, labelSelector, subscribeToAll, fieldsToWatch, resultChannel, true)
21+
22+
go r.runWatch(p, gvk, resultChannel, true)
3123

3224
return resultChannel, nil
3325
}
3426
}
3527

3628
func (r *Service) SubscribeItems(gvk schema.GroupVersionKind) graphql.FieldResolveFn {
3729
return func(p graphql.ResolveParams) (interface{}, error) {
38-
gvk.Group = r.GetOriginalGroupName(gvk.Group)
39-
40-
ctx := p.Context
41-
namespace, _ := p.Args[NamespaceArg].(string)
42-
labelSelector, _ := p.Args[LabelSelectorArg].(string)
43-
subscribeToAll, _ := p.Args[SubscribeToAllArg].(bool)
44-
fieldsToWatch := extractRequestedFields(p.Info)
4530

4631
resultChannel := make(chan interface{})
47-
go r.runWatch(ctx, gvk, namespace, "", labelSelector, subscribeToAll, fieldsToWatch, resultChannel, false)
32+
33+
go r.runWatch(p, gvk, resultChannel, false)
4834

4935
return resultChannel, nil
5036
}
5137
}
5238

5339
func (r *Service) runWatch(
54-
ctx context.Context,
40+
p graphql.ResolveParams,
5541
gvk schema.GroupVersionKind,
56-
namespace, name, labelSelector string,
57-
subscribeToAll bool,
58-
fieldsToWatch []string,
5942
resultChannel chan interface{},
6043
singleItem bool,
6144
) {
6245
defer close(resultChannel)
6346

47+
ctx := p.Context
48+
49+
gvk.Group = r.getOriginalGroupName(gvk.Group)
50+
51+
namespace, err := getStringArg(p.Args, NamespaceArg, true)
52+
if err != nil {
53+
r.log.Error().Err(err).Msg("Failed to get namespace argument")
54+
return
55+
}
56+
57+
var name string
58+
if singleItem {
59+
name, err = getStringArg(p.Args, NameArg, true)
60+
if err != nil {
61+
r.log.Error().Err(err).Msg("Failed to get name argument")
62+
return
63+
}
64+
}
65+
66+
labelSelector, err := getStringArg(p.Args, LabelSelectorArg, false)
67+
if err != nil {
68+
r.log.Error().Err(err).Msg("Failed to get label selector argument")
69+
return
70+
}
71+
72+
subscribeToAll, err := getBoolArg(p.Args, SubscribeToAllArg, false)
73+
if err != nil {
74+
r.log.Error().Err(err).Msg("Failed to get subscribeToAll argument")
75+
return
76+
}
77+
78+
fieldsToWatch := extractRequestedFields(p.Info)
79+
6480
list := &unstructured.UnstructuredList{}
6581
list.SetGroupVersionKind(schema.GroupVersionKind{
6682
Group: gvk.Group, Version: gvk.Version, Kind: gvk.Kind + "List",

gateway/schema/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ func (g *Gateway) generateGraphqlSchema() error {
172172
Type: graphql.NewList(resourceType),
173173
Args: resolver.NewFieldConfigArguments().
174174
WithNamespaceArg().
175+
WithLabelSelectorArg().
175176
WithSubscribeToAllArg().
176177
Complete(),
177178
Resolve: g.resolver.CommonResolver(),

0 commit comments

Comments
 (0)