github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/edgeworkers/edgekv_items.go (about) 1 package edgeworkers 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 12 validation "github.com/go-ozzo/ozzo-validation/v4" 13 ) 14 15 type ( 16 // EdgeKVItems is EdgeKV Item API interface 17 EdgeKVItems interface { 18 // ListItems lists items in EdgeKV group 19 // 20 // See: https://techdocs.akamai.com/edgekv/reference/get-group-1 21 ListItems(context.Context, ListItemsRequest) (*ListItemsResponse, error) 22 23 // GetItem reads an item from EdgeKV group 24 // 25 // See: https://techdocs.akamai.com/edgekv/reference/get-item 26 GetItem(context.Context, GetItemRequest) (*Item, error) 27 28 // UpsertItem creates or updates an item in EdgeKV group 29 // 30 // See: https://techdocs.akamai.com/edgekv/reference/put-item 31 UpsertItem(context.Context, UpsertItemRequest) (*string, error) 32 33 // DeleteItem deletes an item from EdgeKV group 34 // 35 // See: https://techdocs.akamai.com/edgekv/reference/delete-item 36 DeleteItem(context.Context, DeleteItemRequest) (*string, error) 37 } 38 39 // ListItemsRequest represents the request params used to list items 40 ListItemsRequest struct { 41 ItemsRequestParams 42 } 43 44 // GetItemRequest represents the request params used to get a single item 45 GetItemRequest struct { 46 ItemID string 47 ItemsRequestParams 48 } 49 50 // UpsertItemRequest represents the request params and body used to create or update an item 51 UpsertItemRequest struct { 52 ItemID string 53 ItemData Item 54 ItemsRequestParams 55 } 56 57 // DeleteItemRequest represents the request params used to delete an item 58 DeleteItemRequest struct { 59 ItemID string 60 ItemsRequestParams 61 } 62 63 // ItemsRequestParams represents the params used to list items 64 ItemsRequestParams struct { 65 Network ItemNetwork 66 NamespaceID string 67 GroupID string 68 } 69 70 // Item represents a single item 71 Item string 72 73 // ItemNetwork represents available item network types 74 ItemNetwork string 75 76 // ListItemsResponse represents the response from list items 77 ListItemsResponse []string 78 ) 79 80 const ( 81 // ItemStagingNetwork is the staging network 82 ItemStagingNetwork ItemNetwork = "staging" 83 84 // ItemProductionNetwork is the staging network 85 ItemProductionNetwork ItemNetwork = "production" 86 ) 87 88 // Validate validates ItemsRequestParams 89 func (r ItemsRequestParams) Validate() error { 90 return validation.Errors{ 91 "Network": validation.Validate(r.Network, validation.Required, validation.In(ItemStagingNetwork, ItemProductionNetwork).Error( 92 fmt.Sprintf("value '%s' is invalid. Must be one of: '%s' or '%s'", r.Network, ItemStagingNetwork, ItemProductionNetwork))), 93 "NamespaceID": validation.Validate(r.NamespaceID, validation.Required), 94 "GroupID": validation.Validate(r.GroupID, validation.Required), 95 }.Filter() 96 } 97 98 // Validate validates ListItemsRequest 99 func (r ListItemsRequest) Validate() error { 100 return validation.Errors{ 101 "ItemsRequestParams": validation.Validate(r.ItemsRequestParams, validation.Required), 102 }.Filter() 103 } 104 105 // Validate validates GetItemRequest 106 func (r GetItemRequest) Validate() error { 107 return validation.Errors{ 108 "ItemID": validation.Validate(r.ItemID, validation.Required), 109 "ItemsRequestParams": validation.Validate(r.ItemsRequestParams, validation.Required), 110 }.Filter() 111 } 112 113 // Validate validates UpsertItemRequest 114 func (r UpsertItemRequest) Validate() error { 115 return validation.Errors{ 116 "ItemID": validation.Validate(r.ItemID, validation.Required), 117 "ItemData": validation.Validate(r.ItemData, validation.Required), 118 "ItemsRequestParams": validation.Validate(r.ItemsRequestParams, validation.Required), 119 }.Filter() 120 } 121 122 // Validate validates DeleteItemRequest 123 func (r DeleteItemRequest) Validate() error { 124 return validation.Errors{ 125 "ItemID": validation.Validate(r.ItemID, validation.Required), 126 "ItemsRequestParams": validation.Validate(r.ItemsRequestParams, validation.Required), 127 }.Filter() 128 } 129 130 // IsJSON returns true if the given string is a valid json 131 func IsJSON(str Item) bool { 132 var js json.RawMessage 133 return json.Unmarshal([]byte(str), &js) == nil 134 } 135 136 var ( 137 // ErrListItems is returned in case an error occurs on ListItems operation 138 ErrListItems = errors.New("list items") 139 // ErrGetItem is returned in case an error occurs on GetItem operation 140 ErrGetItem = errors.New("get item") 141 // ErrUpsertItem is returned in case an error occurs on UpsertItem operation 142 ErrUpsertItem = errors.New("create or update item") 143 // ErrDeleteItem is returned in case an error occurs on DeleteItem operation 144 ErrDeleteItem = errors.New("delete item") 145 ) 146 147 func (e *edgeworkers) ListItems(ctx context.Context, params ListItemsRequest) (*ListItemsResponse, error) { 148 logger := e.Log(ctx) 149 logger.Debug("ListItems") 150 151 if err := params.Validate(); err != nil { 152 return nil, fmt.Errorf("%s: %w: %s", ErrListItems, ErrStructValidation, err) 153 } 154 155 uri := fmt.Sprintf("/edgekv/v1/networks/%s/namespaces/%s/groups/%s", params.Network, params.NamespaceID, params.GroupID) 156 157 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) 158 if err != nil { 159 return nil, fmt.Errorf("%w: failed to create request: %s", ErrListItems, err) 160 } 161 162 var result ListItemsResponse 163 resp, err := e.Exec(req, &result) 164 if err != nil { 165 return nil, fmt.Errorf("%w: request failed: %s", ErrListItems, err) 166 } 167 168 if resp.StatusCode != http.StatusOK { 169 return nil, fmt.Errorf("%s: %w", ErrListItems, e.Error(resp)) 170 } 171 172 return &result, nil 173 } 174 175 func (e *edgeworkers) GetItem(ctx context.Context, params GetItemRequest) (*Item, error) { 176 logger := e.Log(ctx) 177 logger.Debug("GetItem") 178 179 if err := params.Validate(); err != nil { 180 return nil, fmt.Errorf("%s: %w: %s", ErrGetItem, ErrStructValidation, err) 181 } 182 183 uri := fmt.Sprintf("/edgekv/v1/networks/%s/namespaces/%s/groups/%s/items/%s", params.Network, 184 params.NamespaceID, params.GroupID, params.ItemID) 185 186 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil) 187 if err != nil { 188 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetItem, err) 189 } 190 191 resp, err := e.Exec(req, nil) 192 if err != nil { 193 return nil, fmt.Errorf("%w: request failed: %s", ErrGetItem, err) 194 } 195 196 if resp.StatusCode != http.StatusOK { 197 return nil, fmt.Errorf("%s: %w", ErrGetItem, e.Error(resp)) 198 } 199 200 data, err := ioutil.ReadAll(resp.Body) 201 if err != nil { 202 return nil, fmt.Errorf("%w: failed to fetch data: %s", ErrGetItem, err) 203 } 204 205 result := Item(data) 206 return &result, nil 207 } 208 209 func (e *edgeworkers) UpsertItem(ctx context.Context, params UpsertItemRequest) (*string, error) { 210 logger := e.Log(ctx) 211 logger.Debug("UpsertItem") 212 213 if err := params.Validate(); err != nil { 214 return nil, fmt.Errorf("%s: %w: %s", ErrUpsertItem, ErrStructValidation, err) 215 } 216 217 uri := fmt.Sprintf("/edgekv/v1/networks/%s/namespaces/%s/groups/%s/items/%s", params.Network, 218 params.NamespaceID, params.GroupID, params.ItemID) 219 220 data := []byte(params.ItemData) 221 req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri, ioutil.NopCloser(bytes.NewBuffer(data))) 222 if err != nil { 223 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpsertItem, err) 224 } 225 req.ContentLength = int64(len(data)) 226 227 if !IsJSON(params.ItemData) { 228 req.Header.Set("Content-Type", "text/plain") 229 } 230 231 resp, err := e.Exec(req, nil) 232 if err != nil { 233 return nil, fmt.Errorf("%w: request failed: %s", ErrUpsertItem, err) 234 } 235 236 if resp.StatusCode != http.StatusOK { 237 return nil, fmt.Errorf("%s: %w", ErrUpsertItem, e.Error(resp)) 238 } 239 240 data, err = ioutil.ReadAll(resp.Body) 241 if err != nil { 242 return nil, fmt.Errorf("%w: failed to fetch data: %s", ErrUpsertItem, err) 243 } 244 245 result := string(data) 246 return &result, nil 247 } 248 249 func (e *edgeworkers) DeleteItem(ctx context.Context, params DeleteItemRequest) (*string, error) { 250 logger := e.Log(ctx) 251 logger.Debug("DeleteItem") 252 253 if err := params.Validate(); err != nil { 254 return nil, fmt.Errorf("%s: %w: %s", ErrDeleteItem, ErrStructValidation, err) 255 } 256 257 uri := fmt.Sprintf("/edgekv/v1/networks/%s/namespaces/%s/groups/%s/items/%s", params.Network, 258 params.NamespaceID, params.GroupID, params.ItemID) 259 260 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, uri, nil) 261 if err != nil { 262 return nil, fmt.Errorf("%w: failed to create request: %s", ErrDeleteItem, err) 263 } 264 265 resp, err := e.Exec(req, nil) 266 if err != nil { 267 return nil, fmt.Errorf("%w: request failed: %s", ErrDeleteItem, err) 268 } 269 270 if resp.StatusCode != http.StatusOK { 271 return nil, fmt.Errorf("%s: %w", ErrDeleteItem, e.Error(resp)) 272 } 273 274 data, err := ioutil.ReadAll(resp.Body) 275 if err != nil { 276 return nil, fmt.Errorf("%w: failed to fetch data: %s", ErrDeleteItem, err) 277 } 278 279 var result = string(data) 280 return &result, nil 281 }