internal: Add RemainingKeys function (#351)
* internal: Add RemainingKeys function
This commit adds the RemainingKeys function which can be used to
detect fields returned in response bodies but are not defined in
the resource's result struct.
* Refactor RemainingKeys to not alter original map
Related-PROD: PROD-28126
Change-Id: I0fac3ae32dbce5be4f66945e68f4166f244ba613
diff --git a/internal/testing/util_test.go b/internal/testing/util_test.go
new file mode 100644
index 0000000..a4f03ef
--- /dev/null
+++ b/internal/testing/util_test.go
@@ -0,0 +1,36 @@
+package testing
+
+import (
+ "testing"
+
+ "gerrit.mcp.mirantis.net/debian/gophercloud.git/internal"
+ th "gerrit.mcp.mirantis.net/debian/gophercloud.git/testhelper"
+)
+
+func TestRemainingKeys(t *testing.T) {
+ type User struct {
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"`
+ City string
+ }
+
+ userStruct := User{
+ FirstName: "John",
+ LastName: "Doe",
+ }
+
+ userMap := map[string]interface{}{
+ "first_name": "John",
+ "last_name": "Doe",
+ "city": "Honolulu",
+ "state": "Hawaii",
+ }
+
+ expected := map[string]interface{}{
+ "city": "Honolulu",
+ "state": "Hawaii",
+ }
+
+ actual := internal.RemainingKeys(userStruct, userMap)
+ th.AssertDeepEquals(t, expected, actual)
+}
diff --git a/internal/util.go b/internal/util.go
new file mode 100644
index 0000000..f7e10d2
--- /dev/null
+++ b/internal/util.go
@@ -0,0 +1,26 @@
+package internal
+
+import (
+ "reflect"
+)
+
+// RemainingKeys will inspect a struct and compare it to a map. Any key that
+// is not defined in a JSON tag of the struct will be added to the extras map
+// and returned.
+//
+// This is useful for determining the extra fields returned in response bodies
+// for resources that can contain an arbitrary or dynamic number of fields.
+func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) {
+ extras = make(map[string]interface{})
+ valueOf := reflect.ValueOf(s)
+ typeOf := reflect.TypeOf(s)
+ for i := 0; i < valueOf.NumField(); i++ {
+ field := typeOf.Field(i)
+ tagValue := field.Tag.Get("json")
+ if _, ok := m[tagValue]; !ok {
+ extras[tagValue] = m[tagValue]
+ }
+ }
+
+ return
+}