forked from stmcginnis/gofish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
entity.go
139 lines (122 loc) · 3.6 KB
/
entity.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//
// SPDX-License-Identifier: BSD-3-Clause
//
package common
import (
"encoding/json"
"fmt"
"reflect"
)
// Entity provides the common basis for all Redfish and Swordfish objects.
type Entity struct {
// ODataID is the location of the resource.
ODataID string `json:"@odata.id"`
// ID uniquely identifies the resource.
ID string `json:"Id"`
// Name is the name of the resource or array element.
Name string `json:"Name"`
// Client is the REST client interface to the system.
Client Client `json:"-"`
// etag contains the etag header when fetching the object. This is used to
// control updates to make sure the object has not been modified my a different
// process between fetching and updating that could cause conflicts.
etag string
}
// SetClient sets the API client connection to use for accessing this
// entity.
func (e *Entity) SetClient(c Client) {
e.Client = c
}
// Update commits changes to an entity.
func (e *Entity) Update(originalEntity, currentEntity reflect.Value, allowedUpdates []string) error {
payload := make(map[string]interface{})
for i := 0; i < originalEntity.NumField(); i++ {
if !originalEntity.Field(i).CanInterface() {
// Private field or something that we can't access
continue
}
fieldType := originalEntity.Type().Field(i).Type.Kind()
if fieldType == reflect.Struct || fieldType == reflect.Ptr || fieldType == reflect.Slice {
// TODO: Handle more complicated data types
continue
}
fieldName := originalEntity.Type().Field(i).Name
jsonName := originalEntity.Type().Field(i).Tag.Get("json")
if jsonName != "" {
fieldName = jsonName
}
originalValue := originalEntity.Field(i).Interface()
currentValue := currentEntity.Field(i).Interface()
if originalValue == nil && currentValue == nil {
continue
} else if originalValue == nil {
payload[fieldName] = currentValue
} else if reflect.TypeOf(originalValue).Kind() != reflect.Map {
if originalValue != currentValue {
payload[fieldName] = currentValue
}
} else if !reflect.DeepEqual(originalValue, currentValue) {
payload[fieldName] = currentValue
}
}
// See if we are attempting to update anything that is not allowed
for field := range payload {
found := false
for _, name := range allowedUpdates {
if name == field {
found = true
break
}
}
if !found {
return fmt.Errorf("%s field is read only", field)
}
}
// If there are any allowed updates, try to send updates to the system and
// return the result.
if len(payload) > 0 {
return e.Patch(e.ODataID, payload)
}
return nil
}
// Get performs a Get request against the Redfish service and save etag
func (e *Entity) Get(c Client, uri string, payload interface{}) error {
resp, err := c.Get(uri)
if err != nil {
return err
}
defer resp.Body.Close()
err = json.NewDecoder(resp.Body).Decode(payload)
if err != nil {
return err
}
if resp.Header["Etag"] != nil {
e.etag = resp.Header["Etag"][0]
}
e.SetClient(c)
return nil
}
// Patch performs a Patch request against the Redfish service with etag
func (e *Entity) Patch(uri string, payload interface{}) error {
header := make(map[string]string)
if e.etag != "" {
header["If-Match"] = e.etag
}
resp, err := e.Client.PatchWithHeaders(uri, payload, header)
if err == nil {
return resp.Body.Close()
}
return err
}
// Post performs a Post request against the Redfish service with etag
func (e *Entity) Post(uri string, payload interface{}) error {
header := make(map[string]string)
if e.etag != "" {
header["If-Match"] = e.etag
}
resp, err := e.Client.PostWithHeaders(uri, payload, header)
if err == nil {
return resp.Body.Close()
}
return err
}