c31fa1d82e025a89cc7d6738e3d96959fb9b90db
[nonrtric/rapp/ransliceassurance.git] / smoversion / internal / restclient / client.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2021: Nordix Foundation
6 //   %%
7 //   Licensed under the Apache License, Version 2.0 (the "License");
8 //   you may not use this file except in compliance with the License.
9 //   You may obtain a copy of the License at
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
13 //   Unless required by applicable law or agreed to in writing, software
14 //   distributed under the License is distributed on an "AS IS" BASIS,
15 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 //   See the License for the specific language governing permissions and
17 //   limitations under the License.
18 //   ========================LICENSE_END===================================
19 //
20
21 package restclient
22
23 import (
24         "bytes"
25         "encoding/json"
26         "fmt"
27         "io"
28         "net/http"
29         "net/http/httputil"
30
31         log "github.com/sirupsen/logrus"
32 )
33
34 type RequestError struct {
35         StatusCode int
36         Body       []byte
37 }
38
39 func (e RequestError) Error() string {
40         return fmt.Sprintf("error response with status: %v and body: %v", e.StatusCode, string(e.Body))
41 }
42
43 type Client struct {
44         httpClient *http.Client
45         verbose    bool
46 }
47
48 func New(httpClient *http.Client, verbose bool) *Client {
49         return &Client{
50                 httpClient: httpClient,
51                 verbose:    verbose,
52         }
53 }
54
55 func (c *Client) Get(path string, v interface{}, userInfo ...string) error {
56         var req *http.Request
57         var err error
58
59         if len(userInfo) > 1 {
60                 req, err = c.newRequest(http.MethodGet, path, nil, userInfo[0], userInfo[1])
61         } else {
62                 req, err = c.newRequest(http.MethodGet, path, nil)
63         }
64
65         if err != nil {
66                 return fmt.Errorf("failed to create GET request: %w", err)
67         }
68
69         if err := c.doRequest(req, v); err != nil {
70                 return err
71         }
72
73         return nil
74 }
75
76 func (c *Client) Post(path string, payload interface{}, v interface{}, userInfo ...string) error {
77         var req *http.Request
78         var err error
79
80         if len(userInfo) > 1 {
81                 req, err = c.newRequest(http.MethodPost, path, payload, userInfo[0], userInfo[1])
82         } else {
83                 req, err = c.newRequest(http.MethodPost, path, payload)
84         }
85
86         if err != nil {
87                 return fmt.Errorf("failed to create POST request: %w", err)
88         }
89
90         if err := c.doRequest(req, v); err != nil {
91                 return err
92         }
93
94         return nil
95 }
96
97 func (c *Client) Put(path string, payload interface{}, v interface{}, userName string, password string) error {
98         req, err := c.newRequest(http.MethodPut, path, payload, userName, password)
99         if err != nil {
100                 return fmt.Errorf("failed to create PUT request: %w", err)
101         }
102
103         if err := c.doRequest(req, v); err != nil {
104                 return err
105         }
106
107         return nil
108 }
109
110 func (c *Client) newRequest(method, path string, payload interface{}, userInfo ...string) (*http.Request, error) {
111         var reqBody io.Reader
112
113         if payload != nil {
114                 bodyBytes, err := json.Marshal(payload)
115                 if err != nil {
116                         return nil, fmt.Errorf("failed to marshal request body: %w", err)
117                 }
118                 reqBody = bytes.NewReader(bodyBytes)
119         }
120
121         req, err := http.NewRequest(method, path, reqBody)
122
123         if err != nil {
124                 return nil, fmt.Errorf("failed to create HTTP request: %w", err)
125         }
126
127         if len(userInfo) > 0 {
128                 req.SetBasicAuth(userInfo[0], userInfo[1])
129         }
130
131         if reqBody != nil {
132                 req.Header.Set("Content-Type", "application/json")
133         }
134
135         if c.verbose {
136                 if reqDump, error := httputil.DumpRequest(req, true); error != nil {
137                         fmt.Println(err)
138                 } else {
139                         fmt.Println(string(reqDump))
140                 }
141         }
142
143         return req, nil
144 }
145
146 func (c *Client) doRequest(r *http.Request, v interface{}) error {
147         resp, err := c.do(r)
148         if err != nil {
149                 return err
150         }
151
152         if resp == nil {
153                 return nil
154         }
155         defer resp.Body.Close()
156
157         if v == nil {
158                 return nil
159         }
160
161         dec := json.NewDecoder(resp.Body)
162         if err := dec.Decode(&v); err != nil {
163                 return fmt.Errorf("could not parse response body: %w [%s:%s]", err, r.Method, r.URL.String())
164         }
165         log.Debugf("Http Client Response: %v\n", v)
166         return nil
167 }
168
169 func (c *Client) do(r *http.Request) (*http.Response, error) {
170         resp, err := c.httpClient.Do(r)
171         if err != nil {
172                 return nil, fmt.Errorf("failed to make request [%s:%s]: %w", r.Method, r.URL.String(), err)
173         }
174
175         if c.verbose {
176                 if responseDump, error := httputil.DumpResponse(resp, true); error != nil {
177                         fmt.Println(err)
178                 } else {
179                         fmt.Println(string(responseDump))
180                 }
181         }
182
183         if resp.StatusCode >= http.StatusOK && resp.StatusCode <= 299 {
184                 return resp, nil
185         }
186
187         defer resp.Body.Close()
188         responseData, _ := io.ReadAll(resp.Body)
189
190         putError := RequestError{
191                 StatusCode: resp.StatusCode,
192                 Body:       responseData,
193         }
194
195         return resp, putError
196 }