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
+}