Skip to content

Commit bf80326

Browse files
committed
cleanup
On-behalf-of: @SAP [email protected] Signed-off-by: Artem Shcherbatiuk <[email protected]>
1 parent a7fa5fb commit bf80326

File tree

3 files changed

+6
-283
lines changed

3 files changed

+6
-283
lines changed

gateway/resolver/relationships.go

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

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

@@ -11,24 +10,14 @@ import (
1110
"go.opentelemetry.io/otel/trace"
1211
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1312
"k8s.io/apimachinery/pkg/runtime/schema"
14-
"sigs.k8s.io/controller-runtime/pkg/client"
1513

1614
"github.com/openmfp/golang-commons/logger"
1715
)
1816

1917
// RelationshipResolver handles resolution of relationships between Kubernetes resources
2018
type RelationshipResolver struct {
21-
log *logger.Logger
22-
runtimeClient client.WithWatch
23-
groupNames map[string]string
24-
}
25-
26-
// RelationshipRef represents a reference to another Kubernetes resource
27-
type RelationshipRef struct {
28-
Kind string `json:"kind,omitempty"`
29-
GroupVersion string `json:"groupVersion,omitempty"`
30-
Name string `json:"name"`
31-
Namespace string `json:"namespace,omitempty"`
19+
log *logger.Logger
20+
groupNames map[string]string
3221
}
3322

3423
// EnhancedRef represents our custom relationship reference structure
@@ -40,11 +29,10 @@ type EnhancedRef struct {
4029
}
4130

4231
// NewRelationshipResolver creates a new relationship resolver
43-
func NewRelationshipResolver(log *logger.Logger, runtimeClient client.WithWatch, groupNames map[string]string) *RelationshipResolver {
32+
func NewRelationshipResolver(log *logger.Logger, groupNames map[string]string) *RelationshipResolver {
4433
return &RelationshipResolver{
45-
log: log,
46-
runtimeClient: runtimeClient,
47-
groupNames: groupNames,
34+
log: log,
35+
groupNames: groupNames,
4836
}
4937
}
5038

@@ -69,110 +57,6 @@ func ExtractTargetKind(fieldName string) string {
6957
return strings.ToUpper(kindName[:1]) + kindName[1:]
7058
}
7159

72-
// CreateRelationshipResolver creates a GraphQL field resolver for relationship fields
73-
func (r *RelationshipResolver) CreateRelationshipResolver(sourceGVK schema.GroupVersionKind, fieldName string, targetKind string) graphql.FieldResolveFn {
74-
return func(p graphql.ResolveParams) (interface{}, error) {
75-
ctx, span := otel.Tracer("").Start(p.Context, "ResolveRelationship",
76-
trace.WithAttributes(
77-
attribute.String("sourceKind", sourceGVK.Kind),
78-
attribute.String("fieldName", fieldName),
79-
attribute.String("targetKind", targetKind),
80-
))
81-
defer span.End()
82-
83-
// Get the source object from the parent resolver
84-
sourceObj, ok := p.Source.(map[string]interface{})
85-
if !ok {
86-
return nil, fmt.Errorf("expected source to be map[string]interface{}, got %T", p.Source)
87-
}
88-
89-
// Extract the relationship reference from the source object
90-
refValue, found, err := unstructured.NestedFieldNoCopy(sourceObj, fieldName)
91-
if err != nil {
92-
return nil, fmt.Errorf("error accessing field %s: %v", fieldName, err)
93-
}
94-
if !found {
95-
return nil, nil // Field not present
96-
}
97-
98-
refMap, ok := refValue.(map[string]interface{})
99-
if !ok {
100-
return nil, fmt.Errorf("expected %s to be map[string]interface{}, got %T", fieldName, refValue)
101-
}
102-
103-
// Parse the relationship reference
104-
ref, err := r.parseRelationshipRef(refMap, sourceObj, sourceGVK, fieldName)
105-
if err != nil {
106-
return nil, fmt.Errorf("error parsing relationship ref: %v", err)
107-
}
108-
109-
// If no kind specified in ref, use the target kind from field name
110-
if ref.Kind == "" {
111-
ref.Kind = targetKind
112-
}
113-
114-
// Resolve the target GVK
115-
targetGVK, err := r.resolveTargetGVK(ref, sourceGVK)
116-
if err != nil {
117-
return nil, fmt.Errorf("error resolving target GVK: %v", err)
118-
}
119-
120-
// Fetch the referenced resource
121-
return r.fetchReferencedResource(ctx, ref, targetGVK, sourceObj, sourceGVK)
122-
}
123-
}
124-
125-
// CreateRelationsResolver creates a GraphQL field resolver for the relations field
126-
func (r *RelationshipResolver) CreateRelationsResolver(sourceGVK schema.GroupVersionKind, relationshipFields []string) graphql.FieldResolveFn {
127-
return func(p graphql.ResolveParams) (interface{}, error) {
128-
_, span := otel.Tracer("").Start(p.Context, "ResolveRelations",
129-
trace.WithAttributes(
130-
attribute.String("sourceKind", sourceGVK.Kind),
131-
attribute.Int("relationshipCount", len(relationshipFields)),
132-
))
133-
defer span.End()
134-
135-
// Get the source object from the parent resolver
136-
sourceObj, ok := p.Source.(map[string]interface{})
137-
if !ok {
138-
return nil, fmt.Errorf("expected source to be map[string]interface{}, got %T", p.Source)
139-
}
140-
141-
relations := make(map[string]interface{})
142-
143-
for _, fieldName := range relationshipFields {
144-
// Extract the relationship reference from the source object
145-
refValue, found, err := unstructured.NestedFieldNoCopy(sourceObj, fieldName)
146-
if err != nil {
147-
r.log.Debug().Err(err).Str("field", fieldName).Msg("Error accessing relationship field")
148-
continue
149-
}
150-
if !found {
151-
continue // Field not present, skip
152-
}
153-
154-
refMap, ok := refValue.(map[string]interface{})
155-
if !ok {
156-
r.log.Debug().Str("field", fieldName).Msg("Relationship field is not a map")
157-
continue
158-
}
159-
160-
// Create enhanced reference
161-
enhancedRef, err := r.createEnhancedRef(refMap, sourceObj, sourceGVK, fieldName)
162-
if err != nil {
163-
r.log.Debug().Err(err).Str("field", fieldName).Msg("Error creating enhanced reference")
164-
continue
165-
}
166-
167-
// Add to relations without "Ref" suffix
168-
relationName := strings.TrimSuffix(fieldName, "Ref")
169-
relations[relationName] = enhancedRef
170-
}
171-
172-
return relations, nil
173-
}
174-
}
175-
17660
// CreateSingleRelationResolver creates a GraphQL field resolver for a single relation field
17761
func (r *RelationshipResolver) CreateSingleRelationResolver(sourceGVK schema.GroupVersionKind, fieldName string) graphql.FieldResolveFn {
17862
return func(p graphql.ResolveParams) (interface{}, error) {
@@ -216,48 +100,6 @@ func (r *RelationshipResolver) CreateSingleRelationResolver(sourceGVK schema.Gro
216100
}
217101
}
218102

219-
// parseRelationshipRef parses a relationship reference from a map
220-
func (r *RelationshipResolver) parseRelationshipRef(refMap map[string]interface{}, sourceObj map[string]interface{}, sourceGVK schema.GroupVersionKind, fieldName string) (*RelationshipRef, error) {
221-
ref := &RelationshipRef{}
222-
223-
if kind, ok := refMap["kind"].(string); ok {
224-
ref.Kind = kind
225-
}
226-
227-
if groupVersion, ok := refMap["groupVersion"].(string); ok {
228-
ref.GroupVersion = groupVersion
229-
}
230-
231-
// Handle native Kubernetes apiGroup field (used in roleRef, clusterRoleRef, etc.)
232-
if apiGroup, ok := refMap["apiGroup"].(string); ok {
233-
// For RBAC resources, apiGroup + kind determines the groupVersion
234-
if apiGroup == "rbac.authorization.k8s.io" {
235-
ref.GroupVersion = "rbac.authorization.k8s.io/v1"
236-
} else if apiGroup == "" {
237-
// Empty apiGroup means core/v1
238-
ref.GroupVersion = "v1"
239-
} else {
240-
// Default to v1 for other groups
241-
ref.GroupVersion = apiGroup + "/v1"
242-
}
243-
}
244-
245-
name, ok := refMap["name"].(string)
246-
if !ok {
247-
return nil, fmt.Errorf("name is required in relationship reference")
248-
}
249-
ref.Name = name
250-
251-
if namespace, ok := refMap["namespace"].(string); ok {
252-
ref.Namespace = namespace
253-
} else {
254-
// For native Kubernetes references like roleRef, infer namespace from source
255-
ref.Namespace = r.inferNamespaceForReference(sourceObj, sourceGVK, fieldName, ref.Kind)
256-
}
257-
258-
return ref, nil
259-
}
260-
261103
// createEnhancedRef transforms a native Kubernetes reference to our enhanced structure
262104
func (r *RelationshipResolver) createEnhancedRef(nativeRefMap map[string]interface{}, sourceObj map[string]interface{}, sourceGVK schema.GroupVersionKind, fieldName string) (map[string]interface{}, error) {
263105
enhancedRef := make(map[string]interface{})
@@ -345,72 +187,4 @@ func (r *RelationshipResolver) inferNamespaceForReference(sourceObj map[string]i
345187
return ""
346188
}
347189

348-
// resolveTargetGVK resolves the target GroupVersionKind from the relationship reference
349-
func (r *RelationshipResolver) resolveTargetGVK(ref *RelationshipRef, sourceGVK schema.GroupVersionKind) (schema.GroupVersionKind, error) {
350-
targetGVK := schema.GroupVersionKind{
351-
Kind: ref.Kind,
352-
}
353-
354-
if ref.GroupVersion != "" {
355-
// Parse group and version from groupVersion
356-
parts := strings.Split(ref.GroupVersion, "/")
357-
if len(parts) == 2 {
358-
targetGVK.Group = parts[0]
359-
targetGVK.Version = parts[1]
360-
} else if len(parts) == 1 {
361-
// Could be just version (for core resources) or just group
362-
if ref.GroupVersion == "v1" || strings.HasPrefix(ref.GroupVersion, "v") {
363-
targetGVK.Group = ""
364-
targetGVK.Version = ref.GroupVersion
365-
} else {
366-
targetGVK.Group = ref.GroupVersion
367-
targetGVK.Version = "v1" // Default version
368-
}
369-
} else {
370-
return targetGVK, fmt.Errorf("invalid groupVersion format: %s", ref.GroupVersion)
371-
}
372-
} else {
373-
// If no groupVersion specified, inherit from source or use defaults
374-
targetGVK.Group = sourceGVK.Group
375-
targetGVK.Version = sourceGVK.Version
376-
}
377-
378-
return targetGVK, nil
379-
}
380-
381-
// fetchReferencedResource fetches the referenced Kubernetes resource
382-
func (r *RelationshipResolver) fetchReferencedResource(ctx context.Context, ref *RelationshipRef, targetGVK schema.GroupVersionKind, sourceObj map[string]interface{}, sourceGVK schema.GroupVersionKind) (interface{}, error) {
383-
// Create an unstructured object to hold the result
384-
obj := &unstructured.Unstructured{}
385-
obj.SetGroupVersionKind(targetGVK)
386-
387-
key := client.ObjectKey{
388-
Name: ref.Name,
389-
}
390-
391-
// Set namespace if specified and target is namespaced
392-
if ref.Namespace != "" {
393-
key.Namespace = ref.Namespace
394-
}
395-
396-
r.log.Debug().
397-
Str("refName", ref.Name).
398-
Str("refNamespace", ref.Namespace).
399-
Str("targetGVK", targetGVK.String()).
400-
Str("sourceKind", sourceGVK.Kind).
401-
Msg("Fetching referenced resource")
402-
403-
// Get the object using the runtime client
404-
if err := r.runtimeClient.Get(ctx, key, obj); err != nil {
405-
r.log.Error().Err(err).
406-
Str("name", ref.Name).
407-
Str("namespace", ref.Namespace).
408-
Str("gvk", targetGVK.String()).
409-
Msg("Unable to get referenced object")
410-
return nil, err
411-
}
412-
413-
return obj.Object, nil
414-
}
415-
416190
// getOriginalGroupName converts sanitized group name back to original

gateway/resolver/resolver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func New(log *logger.Logger, runtimeClient client.WithWatch) *Service {
6464
}
6565

6666
// Initialize relationship resolver
67-
service.relationshipResolver = NewRelationshipResolver(log, runtimeClient, service.groupNames)
67+
service.relationshipResolver = NewRelationshipResolver(log, service.groupNames)
6868

6969
return service
7070
}

gateway/schema/schema.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ func (g *Gateway) generateGraphqlSchema() error {
8080
g.AddTypeByCategoryQuery(rootQueryFields)
8181

8282
// Add relations fields to existing types that have relationship fields
83-
g.addRelationsToExistingTypes()
8483

8584
newSchema, err := graphql.NewSchema(graphql.SchemaConfig{
8685
Query: graphql.NewObject(graphql.ObjectConfig{
@@ -727,56 +726,6 @@ func (g *Gateway) createEnhancedRefType(targetKind string) graphql.Output {
727726
return refType
728727
}
729728

730-
// addRelationsToExistingTypes adds relations fields to existing types that have relationship fields
731-
func (g *Gateway) addRelationsToExistingTypes() {
732-
// List of types that should have relations fields added
733-
typesToEnhance := []string{"RoleBinding", "ClusterRoleBinding"}
734-
735-
for _, typeName := range typesToEnhance {
736-
if existingType, exists := g.typesCache[typeName]; exists && existingType != nil {
737-
g.log.Info().
738-
Str("typeName", typeName).
739-
Msg("Checking existing type for relationship fields")
740-
741-
// Check if this type has relationship fields by examining its existing fields
742-
relationshipFields := g.findRelationshipFieldsInType(existingType)
743-
744-
if len(relationshipFields) > 0 {
745-
g.log.Info().
746-
Str("typeName", typeName).
747-
Strs("relationshipFields", relationshipFields).
748-
Msg("Found relationship fields, but cannot modify existing GraphQL object")
749-
750-
// Note: GraphQL objects are immutable after creation
751-
// We need to recreate the type with the relations field
752-
// This is a limitation we need to address differently
753-
} else {
754-
g.log.Info().
755-
Str("typeName", typeName).
756-
Msg("No relationship fields found in existing type")
757-
}
758-
} else {
759-
g.log.Info().
760-
Str("typeName", typeName).
761-
Msg("Type not found in cache")
762-
}
763-
}
764-
}
765-
766-
// findRelationshipFieldsInType finds relationship fields in an existing GraphQL type
767-
func (g *Gateway) findRelationshipFieldsInType(objType *graphql.Object) []string {
768-
var relationshipFields []string
769-
770-
fields := objType.Fields()
771-
for fieldName := range fields {
772-
if resolver.IsRelationshipField(fieldName) {
773-
relationshipFields = append(relationshipFields, fieldName)
774-
}
775-
}
776-
777-
return relationshipFields
778-
}
779-
780729
// getGVKForTypeName gets the GroupVersionKind for a type name
781730
func (g *Gateway) getGVKForTypeName(typeName string) *schema.GroupVersionKind {
782731
switch typeName {

0 commit comments

Comments
 (0)