8
8
"strconv"
9
9
"strings"
10
10
11
+ "github.com/gohugoio/hashstructure"
12
+
11
13
"goa.design/goa/v3/codegen"
12
14
"goa.design/goa/v3/expr"
13
15
"goa.design/goa/v3/http/codegen/openapi"
@@ -376,7 +378,7 @@ func toString(val any) string {
376
378
// identifiers. Structurally identical means same primitive types, arrays with
377
379
// structurally equivalent element types, maps with structurally equivalent key
378
380
// and value types or object with identical attribute names and structurally
379
- // equivalent types and identical set of required attributes .
381
+ // equivalent types and identical set of validation rules .
380
382
func (* schemafier ) hashAttribute (att * expr.AttributeExpr , h hash.Hash64 ) uint64 {
381
383
return * hashAttribute (att , h , make (map [string ]* uint64 ))
382
384
}
@@ -393,6 +395,7 @@ func hashAttribute(att *expr.AttributeExpr, h hash.Hash64, seen map[string]*uint
393
395
}
394
396
seen [t .Hash ()] = res
395
397
398
+ hv := hashValidation (att .Validation , h )
396
399
switch t .Kind () {
397
400
case expr .ObjectKind :
398
401
o := expr .AsObject (t )
@@ -404,25 +407,26 @@ func hashAttribute(att *expr.AttributeExpr, h hash.Hash64, seen map[string]*uint
404
407
vh := hashAttribute (m .Attribute , h , seen )
405
408
* res = * res ^ orderedHash (kh , * vh , h )
406
409
}
407
- // Objects with a different set of required attributes should produce
408
- // different hashes.
409
- if att .Validation != nil {
410
- for _ , req := range att .Validation .Required {
411
- rh := hashString (req , h )
412
- * res = * res ^ rh
413
- }
410
+ if hv != 0 {
411
+ * res = orderedHash (* res , hv , h )
414
412
}
415
413
416
414
case expr .ArrayKind :
417
415
kh := hashString ("[]" , h )
418
416
vh := hashAttribute (expr .AsArray (t ).ElemType , h , seen )
419
417
* res = orderedHash (kh , * vh , h )
418
+ if hv != 0 {
419
+ * res = orderedHash (* res , hv , h )
420
+ }
420
421
421
422
case expr .MapKind :
422
423
m := expr .AsMap (t )
423
424
kh := hashAttribute (m .KeyType , h , seen )
424
425
vh := hashAttribute (m .ElemType , h , seen )
425
426
* res = orderedHash (* kh , * vh , h )
427
+ if hv != 0 {
428
+ * res = orderedHash (* res , hv , h )
429
+ }
426
430
427
431
case expr .UserTypeKind :
428
432
* res = * hashAttribute (t .(expr.UserType ).Attribute (), h , seen )
@@ -438,8 +442,31 @@ func hashAttribute(att *expr.AttributeExpr, h hash.Hash64, seen map[string]*uint
438
442
439
443
default : // Primitives or Any
440
444
* res = hashString (t .Name (), h )
445
+ if hv != 0 {
446
+ * res = orderedHash (* res , hv , h )
447
+ }
448
+ }
449
+
450
+ return res
451
+ }
452
+
453
+ func hashValidation (val * expr.ValidationExpr , h hash.Hash64 ) uint64 {
454
+ // Note: we can't use hashstructure for attributes because it doesn't
455
+ // handle recursive structures.
456
+ if val == nil {
457
+ return 0
441
458
}
442
459
460
+ res , err := hashstructure .Hash (val , & hashstructure.HashOptions {
461
+ Hasher : h ,
462
+ ZeroNil : false ,
463
+ IgnoreZeroValue : true ,
464
+ SlicesAsSets : true ,
465
+ })
466
+ if err != nil {
467
+ // should really never happen (OOM maybe)
468
+ return 0
469
+ }
443
470
return res
444
471
}
445
472
0 commit comments