@@ -413,70 +413,181 @@ func (g *Gateway) convertSwaggerTypeToGraphQL(schema spec.Schema, typePrefix str
413
413
}
414
414
415
415
func (g * Gateway ) handleObjectFieldSpecType (fieldSpec spec.Schema , typePrefix string , fieldPath []string , processingTypes map [string ]bool ) (graphql.Output , graphql.Input , error ) {
416
+ // Handle object types with nested properties
416
417
if len (fieldSpec .Properties ) > 0 {
417
- typeName := g .generateTypeName (typePrefix , fieldPath )
418
+ return g .handleObjectWithProperties (fieldSpec , typePrefix , fieldPath , processingTypes )
419
+ }
418
420
419
- // Check if type already generated
420
- if existingType , exists := g . typesCache [ typeName ]; exists {
421
- return existingType , g . inputTypesCache [ typeName ], nil
422
- }
421
+ // Handle map types (map[string]string)
422
+ if g . isStringMapType ( fieldSpec ) {
423
+ return g . handleMapType ( fieldPath , typePrefix )
424
+ }
423
425
424
- // Store placeholder to prevent recursion
425
- g . typesCache [ typeName ] = nil
426
- g . inputTypesCache [ typeName ] = nil
426
+ // Fallback: empty object as JSON string
427
+ return jsonStringScalar , jsonStringScalar , nil
428
+ }
427
429
428
- nestedFields , nestedInputFields , err := g .generateGraphQLFields (& fieldSpec , typeName , fieldPath , processingTypes )
429
- if err != nil {
430
- return nil , nil , err
431
- }
430
+ // handleObjectWithProperties creates GraphQL types for objects with nested properties
431
+ func (g * Gateway ) handleObjectWithProperties (fieldSpec spec.Schema , typePrefix string , fieldPath []string , processingTypes map [string ]bool ) (graphql.Output , graphql.Input , error ) {
432
+ typeName := g .generateTypeName (typePrefix , fieldPath )
432
433
433
- newType := graphql . NewObject (graphql. ObjectConfig {
434
- Name : sanitizeFieldName ( typeName ),
435
- Fields : nestedFields ,
436
- })
434
+ // Check if type already generated
435
+ if existingType , exists := g . typesCache [ typeName ]; exists {
436
+ return existingType , g . inputTypesCache [ typeName ], nil
437
+ }
437
438
438
- newInputType := graphql .NewInputObject (graphql.InputObjectConfig {
439
- Name : sanitizeFieldName (typeName ) + "Input" ,
440
- Fields : nestedInputFields ,
441
- })
439
+ // Store placeholder to prevent recursion
440
+ g .typesCache [typeName ] = nil
441
+ g .inputTypesCache [typeName ] = nil
442
442
443
- // Store the generated types
444
- g .typesCache [typeName ] = newType
445
- g .inputTypesCache [typeName ] = newInputType
446
-
447
- return newType , newInputType , nil
448
- } else if fieldSpec .AdditionalProperties != nil && fieldSpec .AdditionalProperties .Schema != nil {
449
- // Handle map types
450
- if len (fieldSpec .AdditionalProperties .Schema .Type ) == 1 && fieldSpec .AdditionalProperties .Schema .Type [0 ] == "string" {
451
- // Check if this is a field that should use Label arrays for dot-notation support
452
- currentFieldName := ""
453
- if len (fieldPath ) > 0 {
454
- currentFieldName = fieldPath [len (fieldPath )- 1 ]
455
- }
443
+ nestedFields , nestedInputFields , err := g .generateGraphQLFields (& fieldSpec , typeName , fieldPath , processingTypes )
444
+ if err != nil {
445
+ return nil , nil , err
446
+ }
456
447
457
- // Check various contexts where Label arrays should be used
458
- isInMetadata := len (fieldPath ) >= 2 && fieldPath [len (fieldPath )- 2 ] == "metadata"
459
- isObjectMetaType := strings .Contains (typePrefix , "ObjectMeta" ) || strings .Contains (typePrefix , "meta_v1" )
460
- isInSelector := len (fieldPath ) >= 2 && fieldPath [len (fieldPath )- 2 ] == "selector"
461
- isInSpec := len (fieldPath ) >= 2 && fieldPath [len (fieldPath )- 2 ] == "spec"
462
-
463
- // Identify fields that need Label array treatment
464
- isLabelsOrAnnotations := (isInMetadata || isObjectMetaType ) && (currentFieldName == "labels" || currentFieldName == "annotations" )
465
- isNodeSelector := isInSpec && currentFieldName == "nodeSelector"
466
- isMatchLabels := isInSelector && currentFieldName == "matchLabels"
467
-
468
- if isLabelsOrAnnotations || isNodeSelector || isMatchLabels {
469
- // Use List(LabelType) for output (allows querying key/value fields)
470
- // Use List(LabelInputType) for input (supports array syntax)
471
- return graphql .NewList (LabelType ), graphql .NewList (LabelInputType ), nil
472
- }
473
- // This is a regular map[string]string
474
- return stringMapScalar , stringMapScalar , nil
475
- }
448
+ newType := graphql .NewObject (graphql.ObjectConfig {
449
+ Name : sanitizeFieldName (typeName ),
450
+ Fields : nestedFields ,
451
+ })
452
+
453
+ newInputType := graphql .NewInputObject (graphql.InputObjectConfig {
454
+ Name : sanitizeFieldName (typeName ) + "Input" ,
455
+ Fields : nestedInputFields ,
456
+ })
457
+
458
+ // Store the generated types
459
+ g .typesCache [typeName ] = newType
460
+ g .inputTypesCache [typeName ] = newInputType
461
+
462
+ return newType , newInputType , nil
463
+ }
464
+
465
+ // handleMapType determines the appropriate GraphQL type for map[string]string fields
466
+ func (g * Gateway ) handleMapType (fieldPath []string , typePrefix string ) (graphql.Output , graphql.Input , error ) {
467
+ if g .shouldUseLabelArrays (fieldPath , typePrefix ) {
468
+ // Use Label arrays for fields that can have dotted keys
469
+ return graphql .NewList (LabelType ), graphql .NewList (LabelInputType ), nil
476
470
}
477
471
478
- // It's an empty object, serialize as JSON string
479
- return jsonStringScalar , jsonStringScalar , nil
472
+ // Use regular string map scalar for normal map[string]string fields
473
+ return stringMapScalar , stringMapScalar , nil
474
+ }
475
+
476
+ // isStringMapType checks if the field spec represents a map[string]string
477
+ func (g * Gateway ) isStringMapType (fieldSpec spec.Schema ) bool {
478
+ if fieldSpec .AdditionalProperties == nil {
479
+ return false
480
+ }
481
+
482
+ if fieldSpec .AdditionalProperties .Schema == nil {
483
+ return false
484
+ }
485
+
486
+ if len (fieldSpec .AdditionalProperties .Schema .Type ) != 1 {
487
+ return false
488
+ }
489
+
490
+ return fieldSpec .AdditionalProperties .Schema .Type [0 ] == "string"
491
+ }
492
+
493
+ // shouldUseLabelArrays determines if a field needs Label array treatment for dotted keys
494
+ func (g * Gateway ) shouldUseLabelArrays (fieldPath []string , typePrefix string ) bool {
495
+ if len (fieldPath ) == 0 {
496
+ return false
497
+ }
498
+
499
+ fieldName := fieldPath [len (fieldPath )- 1 ]
500
+
501
+ if g .isLabelsField (fieldPath , typePrefix , fieldName ) {
502
+ return true
503
+ }
504
+
505
+ if g .isAnnotationsField (fieldPath , typePrefix , fieldName ) {
506
+ return true
507
+ }
508
+
509
+ if g .isNodeSelectorField (fieldPath , fieldName ) {
510
+ return true
511
+ }
512
+
513
+ if g .isMatchLabelsField (fieldPath , fieldName ) {
514
+ return true
515
+ }
516
+
517
+ return false
518
+ }
519
+
520
+ // isLabelsField checks if this is a metadata.labels field
521
+ func (g * Gateway ) isLabelsField (fieldPath []string , typePrefix string , fieldName string ) bool {
522
+ if fieldName != "labels" {
523
+ return false
524
+ }
525
+
526
+ return g .isInMetadataContext (fieldPath , typePrefix )
527
+ }
528
+
529
+ // isAnnotationsField checks if this is a metadata.annotations field
530
+ func (g * Gateway ) isAnnotationsField (fieldPath []string , typePrefix string , fieldName string ) bool {
531
+ if fieldName != "annotations" {
532
+ return false
533
+ }
534
+
535
+ return g .isInMetadataContext (fieldPath , typePrefix )
536
+ }
537
+
538
+ // isNodeSelectorField checks if this is a spec.nodeSelector field
539
+ func (g * Gateway ) isNodeSelectorField (fieldPath []string , fieldName string ) bool {
540
+ if fieldName != "nodeSelector" {
541
+ return false
542
+ }
543
+
544
+ return g .isInSpecContext (fieldPath )
545
+ }
546
+
547
+ // isMatchLabelsField checks if this is a selector.matchLabels field
548
+ func (g * Gateway ) isMatchLabelsField (fieldPath []string , fieldName string ) bool {
549
+ if fieldName != "matchLabels" {
550
+ return false
551
+ }
552
+
553
+ return g .isInSelectorContext (fieldPath )
554
+ }
555
+
556
+ // isInMetadataContext checks if the field is within a metadata context
557
+ func (g * Gateway ) isInMetadataContext (fieldPath []string , typePrefix string ) bool {
558
+ // Check if we're directly in a metadata field
559
+ if len (fieldPath ) >= 2 && fieldPath [len (fieldPath )- 2 ] == "metadata" {
560
+ return true
561
+ }
562
+
563
+ // Check if this is an ObjectMeta type
564
+ if strings .Contains (typePrefix , "ObjectMeta" ) {
565
+ return true
566
+ }
567
+
568
+ if strings .Contains (typePrefix , "meta_v1" ) {
569
+ return true
570
+ }
571
+
572
+ return false
573
+ }
574
+
575
+ // isInSpecContext checks if the field is within a spec context
576
+ func (g * Gateway ) isInSpecContext (fieldPath []string ) bool {
577
+ if len (fieldPath ) < 2 {
578
+ return false
579
+ }
580
+
581
+ return fieldPath [len (fieldPath )- 2 ] == "spec"
582
+ }
583
+
584
+ // isInSelectorContext checks if the field is within a selector context
585
+ func (g * Gateway ) isInSelectorContext (fieldPath []string ) bool {
586
+ if len (fieldPath ) < 2 {
587
+ return false
588
+ }
589
+
590
+ return fieldPath [len (fieldPath )- 2 ] == "selector"
480
591
}
481
592
482
593
func (g * Gateway ) generateTypeName (typePrefix string , fieldPath []string ) string {
0 commit comments