Skip to content

Commit 503151f

Browse files
Fix/no diff request transformer plugin (#358)
* fix: sort nested sets in plugin config, but not arrays * fix: unused elements * tests: add integration tests * tests: refactor and add new case * style: format test file * tests: run for kong v3 and above * tests: run for all gateway versions * refactor: address feedback * refactor: move constants back to function scope
1 parent 3bea2c9 commit 503151f

File tree

9 files changed

+617
-43
lines changed

9 files changed

+617
-43
lines changed
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
{
2+
"fields": [
3+
{
4+
"config": {
5+
"fields": [
6+
{
7+
"primitive_str": {
8+
"description": "The primitive string field.",
9+
"type": "string"
10+
}
11+
},
12+
{
13+
"record_of_array_of_str": {
14+
"fields": [
15+
{
16+
"headers": {
17+
"default": [],
18+
"elements": {
19+
"type": "string"
20+
},
21+
"required": true,
22+
"type": "array"
23+
}
24+
}
25+
],
26+
"required": true,
27+
"type": "record"
28+
}
29+
},
30+
{
31+
"map_type": {
32+
"description": "The custom query params to be added in the callout HTTP request. Values can contain Lua expressions in the form `$(some_lua_expression)`. The syntax is based on `request-transformer-advanced` templates.",
33+
"keys": {
34+
"type": "string"
35+
},
36+
"required": false,
37+
"type": "map",
38+
"values": {
39+
"referenceable": true,
40+
"required": false,
41+
"type": "string"
42+
}
43+
}
44+
},
45+
{
46+
"array_of_record": {
47+
"description": "Sentinel node addresses to use for Redis connections when the `redis` strategy is defined. Defining this field implies using a Redis Sentinel. The minimum length of the array is 1 element.",
48+
"elements": {
49+
"fields": [
50+
{
51+
"host": {
52+
"default": "127.0.0.1",
53+
"description": "A string representing a host name, such as example.com.",
54+
"required": true,
55+
"type": "string"
56+
}
57+
},
58+
{
59+
"port": {
60+
"default": 6379,
61+
"description": "An integer representing a port number between 0 and 65535, inclusive.",
62+
"type": "integer"
63+
}
64+
}
65+
],
66+
"type": "record"
67+
},
68+
"len_min": 1,
69+
"required": false,
70+
"type": "array"
71+
}
72+
},
73+
{
74+
"nested_array_of_array_of_str": {
75+
"type": "array",
76+
"required": false,
77+
"description": "List of list of strings.",
78+
"elements": {
79+
"type": "array",
80+
"elements": {
81+
"type": "string"
82+
}
83+
}
84+
}
85+
},
86+
{
87+
"nested_array_of_set_of_str": {
88+
"type": "array",
89+
"required": false,
90+
"description": "List of list of strings.",
91+
"elements": {
92+
"type": "set",
93+
"elements": {
94+
"type": "string"
95+
}
96+
}
97+
}
98+
},
99+
{
100+
"nested_set_of_array_of_str": {
101+
"type": "set",
102+
"required": false,
103+
"description": "List of list of strings.",
104+
"elements": {
105+
"type": "array",
106+
"elements": {
107+
"type": "string"
108+
}
109+
}
110+
}
111+
},
112+
{
113+
"nested_set_of_set_of_str": {
114+
"type": "set",
115+
"required": false,
116+
"description": "List of list of strings.",
117+
"elements": {
118+
"type": "set",
119+
"elements": {
120+
"type": "string"
121+
}
122+
}
123+
}
124+
},
125+
{
126+
"nested_array_of_record_of_array": {
127+
"type": "array",
128+
"required": false,
129+
"description": "List of list of strings.",
130+
"elements": {
131+
"type": "array",
132+
"elements": {
133+
"fields": [
134+
{
135+
"ports": {
136+
"elements": {
137+
"type": "number"
138+
},
139+
"type": "array"
140+
}
141+
}
142+
],
143+
"type": "record"
144+
}
145+
}
146+
}
147+
},
148+
{
149+
"nested_array_of_record_of_set": {
150+
"type": "array",
151+
"required": false,
152+
"description": "List of list of strings.",
153+
"elements": {
154+
"type": "array",
155+
"elements": {
156+
"fields": [
157+
{
158+
"ports": {
159+
"elements": {
160+
"type": "number"
161+
},
162+
"type": "set"
163+
}
164+
}
165+
],
166+
"type": "record"
167+
}
168+
}
169+
}
170+
},
171+
{
172+
"nested_set_of_record_of_set": {
173+
"type": "array",
174+
"required": false,
175+
"description": "List of list of strings.",
176+
"elements": {
177+
"type": "set",
178+
"elements": {
179+
"fields": [
180+
{
181+
"ports": {
182+
"elements": {
183+
"type": "number"
184+
},
185+
"type": "set"
186+
}
187+
}
188+
],
189+
"type": "record"
190+
}
191+
}
192+
}
193+
}
194+
],
195+
"required": true,
196+
"type": "record",
197+
"shorthand_fields": [
198+
{
199+
"shorthand_record_of_set_array": {
200+
"fields": [
201+
{
202+
"set_hosts": {
203+
"elements": {
204+
"type": "string"
205+
},
206+
"type": "set"
207+
}
208+
},
209+
{
210+
"array_hosts": {
211+
"elements": {
212+
"type": "string"
213+
},
214+
"type": "array"
215+
}
216+
}
217+
],
218+
"type": "record"
219+
}
220+
}
221+
]
222+
}
223+
}
224+
]
225+
}

pkg/state/types.go

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/kong/go-kong/kong"
12+
"github.com/tidwall/gjson"
1213
)
1314

1415
// entity abstracts out common fields in a credentials.
@@ -565,14 +566,14 @@ func (p1 *Plugin) Console() string {
565566
// Equal returns true if r1 and r2 are equal.
566567
// TODO add compare array without position
567568
func (p1 *Plugin) Equal(p2 *Plugin) bool {
568-
return p1.EqualWithOpts(p2, false, false, false)
569+
return p1.EqualWithOpts(p2, false, false, false, gjson.Result{})
569570
}
570571

571572
// EqualWithOpts returns true if p1 and p2 are equal.
572573
// If ignoreID is set to true, IDs will be ignored while comparison.
573574
// If ignoreTS is set to true, timestamp fields will be ignored.
574575
func (p1 *Plugin) EqualWithOpts(p2 *Plugin, ignoreID,
575-
ignoreTS, ignoreForeign bool,
576+
ignoreTS, ignoreForeign bool, schema gjson.Result,
576577
) bool {
577578
p1Copy := p1.Plugin.DeepCopy()
578579
p2Copy := p2.Plugin.DeepCopy()
@@ -590,8 +591,10 @@ func (p1 *Plugin) EqualWithOpts(p2 *Plugin, ignoreID,
590591
sort.Slice(p1Copy.Protocols, func(i, j int) bool { return *(p1Copy.Protocols[i]) < *(p1Copy.Protocols[j]) })
591592
sort.Slice(p2Copy.Protocols, func(i, j int) bool { return *(p2Copy.Protocols[i]) < *(p2Copy.Protocols[j]) })
592593

593-
p1Copy.Config = sortNestedArrays(p1Copy.Config)
594-
p2Copy.Config = sortNestedArrays(p2Copy.Config)
594+
const pluginConfigKey = "fields.#(config).config"
595+
configSchema := schema.Get(pluginConfigKey)
596+
p1Copy.Config = sortNestedArraysBasedOnSchema(p1Copy.Config, configSchema)
597+
p2Copy.Config = sortNestedArraysBasedOnSchema(p2Copy.Config, configSchema)
595598

596599
if ignoreID {
597600
p1Copy.ID = nil
@@ -672,26 +675,61 @@ func (e EmptyInterfaceUsingUnderlyingType) Less(i, j int) bool {
672675
return strings.Compare(objI, objJ) == -1
673676
}
674677

675-
// Helper function to sort nested arrays in a map
676-
func sortNestedArrays(m map[string]interface{}) map[string]interface{} {
677-
sortedMap := make(map[string]interface{})
678+
// Helper function to get schema for a field name
679+
func getSchemaForFieldName(schema gjson.Result, fieldName string) gjson.Result {
680+
const fieldsQueryTemplate = "fields.#(%s).%s"
681+
const shorthandFieldsQueryTemplate = "shorthand_fields.#(%s).%s"
682+
fieldsQuery := fmt.Sprintf(fieldsQueryTemplate, fieldName, fieldName)
683+
result := schema.Get(fieldsQuery)
684+
if !result.Exists() {
685+
// try shorthand fields
686+
shorthandQuery := fmt.Sprintf(shorthandFieldsQueryTemplate, fieldName, fieldName)
687+
result = schema.Get(shorthandQuery)
688+
}
689+
return result
690+
}
691+
692+
// Helper function to determine if we should sort based on schema
693+
func shouldSort(schema gjson.Result) bool {
694+
if !schema.Exists() {
695+
return true
696+
}
697+
698+
typeResult := schema.Get("type")
699+
if !typeResult.Exists() {
700+
return true
701+
}
702+
703+
return typeResult.String() != "array"
704+
}
705+
706+
// Helper function to sort nested arrays in a map referring to schema
707+
func sortNestedArraysBasedOnSchema(m map[string]interface{}, schema gjson.Result) map[string]interface{} {
708+
sortedMap := make(map[string]interface{}, len(m))
678709

679710
for k, v := range m {
680711
switch value := v.(type) {
681712
case []interface{}:
713+
currSchema := getSchemaForFieldName(schema, k)
714+
// For list types like array or set, get the element schema
715+
elementsSchema := currSchema.Get("elements")
682716
// Recursively sort each element if it's a map or array
683717
for i, elem := range value {
684718
switch elemType := elem.(type) {
685719
case map[string]interface{}:
686-
value[i] = sortNestedArrays(elemType)
720+
value[i] = sortNestedArraysBasedOnSchema(elemType, elementsSchema)
687721
case []interface{}:
688-
value[i] = sortArrayElementsRecursively(elemType)
722+
value[i] = sortArrayElementsRecursivelyBasedOnSchema(elemType, elementsSchema)
689723
}
690724
}
691-
sort.Sort(EmptyInterfaceUsingUnderlyingType(value))
725+
726+
if shouldSort(currSchema) {
727+
sort.Sort(EmptyInterfaceUsingUnderlyingType(value))
728+
}
692729
sortedMap[k] = value
693730
case map[string]interface{}:
694-
sortedMap[k] = sortNestedArrays(value)
731+
currSchema := getSchemaForFieldName(schema, k)
732+
sortedMap[k] = sortNestedArraysBasedOnSchema(value, currSchema)
695733
default:
696734
sortedMap[k] = value
697735
}
@@ -701,17 +739,21 @@ func sortNestedArrays(m map[string]interface{}) map[string]interface{} {
701739
}
702740

703741
// Helper function to sort array elements recursively
704-
func sortArrayElementsRecursively(arr []interface{}) []interface{} {
742+
func sortArrayElementsRecursivelyBasedOnSchema(arr []interface{}, parentSchema gjson.Result) []interface{} {
743+
elementsSchema := parentSchema.Get("elements")
744+
705745
for i, elem := range arr {
706746
switch elemType := elem.(type) {
707747
case map[string]interface{}:
708-
arr[i] = sortNestedArrays(elemType)
748+
arr[i] = sortNestedArraysBasedOnSchema(elemType, elementsSchema)
709749
case []interface{}:
710-
arr[i] = sortArrayElementsRecursively(elemType)
750+
arr[i] = sortArrayElementsRecursivelyBasedOnSchema(elemType, elementsSchema)
711751
}
712752
}
713753

714-
sort.Sort(EmptyInterfaceUsingUnderlyingType(arr))
754+
if shouldSort(parentSchema) {
755+
sort.Sort(EmptyInterfaceUsingUnderlyingType(arr))
756+
}
715757
return arr
716758
}
717759

0 commit comments

Comments
 (0)