diff --git a/api_method.go b/api_method.go deleted file mode 100644 index 7553896..0000000 --- a/api_method.go +++ /dev/null @@ -1,23 +0,0 @@ -package zoom - -import "net/http" - -// Method is the HTTP request method types -type Method string - -const ( - // Get is GET HTTP method - Get Method = http.MethodGet - - // Post is POST HTTP method - Post Method = http.MethodPost - - // Put is PUT HTTP method - Put Method = http.MethodPut - - // Patch is PATCH HTTP method - Patch Method = http.MethodPatch - - // Delete is DELETE HTTP method - Delete Method = http.MethodDelete -) diff --git a/api.go b/client.go similarity index 52% rename from api.go rename to client.go index dd59380..10e9195 100644 --- a/api.go +++ b/client.go @@ -3,6 +3,7 @@ package zoom import ( "bytes" "encoding/json" + "errors" "io/ioutil" "log" "net/http" @@ -11,7 +12,6 @@ import ( "time" "github.com/google/go-querystring/query" - "gopkg.in/dgrijalva/jwt-go.v3" ) const ( @@ -33,12 +33,6 @@ var ( defaultClient *Client ) -type apiParams struct { - APIKey string `url:"api_key"` - APISecret string `url:"api_secret"` - DataType string `url:"data_type"` -} - // Client is responsible for making API requests type Client struct { Key string @@ -81,140 +75,108 @@ func normalizeParams(parameters interface{}) interface{} { return parameters } -func requestV2(c *Client, method Method, path string, urlParameters interface{}, dataParameters interface{}, ret interface{}) error { - var buf bytes.Buffer +type requestV2Opts struct { + Client *Client + Method HTTPMethod + Path string + URLParameters interface{} + DataParameters interface{} + Ret interface{} +} - // set client to default if not provided +func initializeDefault(c *Client) *Client { if c == nil { if defaultClient == nil { defaultClient = NewClient(APIKey, APISecret) } - c = defaultClient + return defaultClient } - // allow variadic parameters - urlParams := normalizeParams(urlParameters) - dataParams := normalizeParams(dataParameters) + return c +} - // establish JWT token - claims := &jwt.StandardClaims{ - Issuer: c.Key, - ExpiresAt: jwt.TimeFunc().Local().Add(time.Second * time.Duration(5000)).Unix(), - } - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - token.Header["alg"] = "HS256" - token.Header["typ"] = "JWT" - ss, err := token.SignedString([]byte(c.Secret)) +func (c *Client) executeRequest(opts requestV2Opts) (*http.Response, error) { + client := c.httpClient() + req, err := c.addRequestAuth(c.httpRequest(opts)) if err != nil { - return err + return nil, err } - // set URL parameters - values, err := query.Values(urlParams) - if err != nil { - return err - } - - // set request URL - requestURL := c.endpoint + path + "?" - requestURL += values.Encode() - - if Debug { - log.Printf("Request URL: %s", requestURL) - log.Printf("URL Parameters: %s", values.Encode()) - } - - // create HTTP client and set timeout - client := &http.Client{Transport: c.Transport} - if c.Timeout > 0 { - client.Timeout = c.Timeout - } + return client.Do(req) +} - // encode body parameters if any - if err := json.NewEncoder(&buf).Encode(&dataParams); err != nil { - return err +func (c *Client) addRequestAuth(req *http.Request, err error) (*http.Request, error) { + if err != nil { + return nil, err } - // create HTTP request - req, err := http.NewRequest(string(method), requestURL, &buf) + // establish JWT token + ss, err := jwtToken(c.Key, c.Secret) if err != nil { - return err + return nil, err } // set JWT Authorization header req.Header.Add("Authorization", "Bearer "+ss) - // execute HTTP request - resp, err := client.Do(req) - if err != nil { - return err - } - - // get HTTP response - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - if Debug { - log.Printf("Response Body: %s", string(body)) - } - - if err := checkError(body); err != nil { - return err - } - - return json.Unmarshal(body, &ret) + return req, nil } -func request(c *Client, path string, parameters interface{}, ret interface{}) error { - if c == nil { - if defaultClient == nil { - defaultClient = NewClient(APIKey, APISecret) - } - - c = defaultClient - } +func (c *Client) httpRequest(opts requestV2Opts) (*http.Request, error) { + var buf bytes.Buffer // allow variadic parameters - switch reflect.TypeOf(parameters).Kind() { - case reflect.Slice: - s := reflect.ValueOf(parameters) - if s.Len() == 0 { - parameters = nil - } else { - parameters = s.Index(0).Interface() - } + dataParams := normalizeParams(opts.DataParameters) + + // encode body parameters if any + if err := json.NewEncoder(&buf).Encode(&dataParams); err != nil { + return nil, err } - values, err := query.Values(parameters) + // set URL parameters + values, err := query.Values(normalizeParams(opts.URLParameters)) if err != nil { - return err + return nil, err } - values.Set("api_key", c.Key) - values.Set("api_secret", c.Secret) - values.Set("data_type", "JSON") + // set request URL + requestURL := c.endpoint + opts.Path + "?" + values.Encode() - requestURL := c.endpoint + path if Debug { log.Printf("Request URL: %s", requestURL) - log.Printf("Request Parameters: %s", values.Encode()) + log.Printf("URL Parameters: %s", values.Encode()) } + // create HTTP request + req, err := http.NewRequest(string(opts.Method), requestURL, &buf) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) httpClient() *http.Client { client := &http.Client{Transport: c.Transport} if c.Timeout > 0 { client.Timeout = c.Timeout } - // all v1 API endpoints use POST requests - resp, err := client.PostForm(requestURL, values) + return client +} + +func (c *Client) requestV2(opts requestV2Opts) error { + // make sure the defaultClient is not nil if we are using it + c = initializeDefault(c) + + // execute HTTP request + resp, err := c.executeRequest(opts) if err != nil { return err } + // read HTTP response defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -225,9 +187,15 @@ func request(c *Client, path string, parameters interface{}, ret interface{}) er log.Printf("Response Body: %s", string(body)) } + // check for Zoom errors in the response if err := checkError(body); err != nil { return err } - return json.Unmarshal(body, &ret) + // unmarshal the response body into the return object + return json.Unmarshal(body, &opts.Ret) +} + +func request(c *Client, path string, parameters interface{}, ret interface{}) error { + return errors.New("this method is DEPRECATED! Use requestV2. This method will be removed when v2 implementation is complete") } diff --git a/http_method.go b/http_method.go new file mode 100644 index 0000000..c0f5aed --- /dev/null +++ b/http_method.go @@ -0,0 +1,23 @@ +package zoom + +import "net/http" + +// HTTPMethod is the HTTP request method types +type HTTPMethod string + +const ( + // Get is GET HTTP method + Get HTTPMethod = http.MethodGet + + // Post is POST HTTP method + Post HTTPMethod = http.MethodPost + + // Put is PUT HTTP method + Put HTTPMethod = http.MethodPut + + // Patch is PATCH HTTP method + Patch HTTPMethod = http.MethodPatch + + // Delete is DELETE HTTP method + Delete HTTPMethod = http.MethodDelete +) diff --git a/jwt.go b/jwt.go new file mode 100644 index 0000000..c90e310 --- /dev/null +++ b/jwt.go @@ -0,0 +1,18 @@ +package zoom + +import ( + "time" + + "gopkg.in/dgrijalva/jwt-go.v3" +) + +func jwtToken(key string, secret string) (string, error) { + claims := &jwt.StandardClaims{ + Issuer: key, + ExpiresAt: jwt.TimeFunc().Local().Add(time.Second * time.Duration(5000)).Unix(), + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + token.Header["alg"] = "HS256" + token.Header["typ"] = "JWT" + return token.SignedString([]byte(secret)) +} diff --git a/user.go b/user.go index 5957aaf..6964533 100644 --- a/user.go +++ b/user.go @@ -31,12 +31,17 @@ func ListUsers(opts ...ListUsersOptions) (ListUsersResponse, error) { // ListUsers calls /user/list, listing all users, using client c func (c *Client) ListUsers(opts ...ListUsersOptions) (ListUsersResponse, error) { var ret = ListUsersResponse{} - return ret, requestV2(c, Get, listUsersPath, opts, nil, &ret) + return ret, c.requestV2(requestV2Opts{ + Method: Get, + Path: listUsersPath, + URLParameters: opts, + Ret: &ret, + }) } // GetUserOpts contains options for GetUser type GetUserOpts struct { - EmailOrID string + EmailOrID string `url:"userId"` LoginType *UserLoginType `url:"login_type,omitempty"` // use pointer so it can be null } @@ -48,5 +53,10 @@ func GetUser(opts GetUserOpts) (User, error) { // GetUser calls /users/{userId}, searching for a user by ID or email, using a specific client func (c *Client) GetUser(opts GetUserOpts) (User, error) { var ret = User{} - return ret, requestV2(c, Get, fmt.Sprintf(getUserPath, opts.EmailOrID), opts, nil, &ret) + return ret, c.requestV2(requestV2Opts{ + Method: Get, + Path: fmt.Sprintf(getUserPath, opts.EmailOrID), + URLParameters: opts, + Ret: &ret, + }) }