diff --git a/README.md b/README.md index 3fb43de..db3d438 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,20 @@ if pinterestError, ok := err.(*models.PinterestError); ok { } ``` +## OAuth Endpoints + +### Generate Access Token + +`[POST] /v1/oauth/token` + +```go +accessToken, err := client.OAuth.Token.Create( + "client-id", + "client-secret", + "access-code" +) +``` + ## Boards Endpoints ### Create a Board diff --git a/controllers/oauth_controller.go b/controllers/oauth_controller.go new file mode 100644 index 0000000..ec3c972 --- /dev/null +++ b/controllers/oauth_controller.go @@ -0,0 +1,20 @@ +package controllers + +import ( + "github.com/BrandonRomano/wrecker" +) + +// OAuthController is the controller that is responsible +// for all /v1/oauth endpoints in the Pinterest API. +type OAuthController struct { + wreckerClient *wrecker.Wrecker + Token *OAuthTokenController +} + +// NewOAuthController instantiates a new OAuthController +func NewOAuthController(wc *wrecker.Wrecker) *OAuthController { + return &OAuthController{ + wreckerClient: wc, + Token: newOAuthTokenController(wc), + } +} diff --git a/controllers/oauth_token_controller.go b/controllers/oauth_token_controller.go new file mode 100644 index 0000000..80817ad --- /dev/null +++ b/controllers/oauth_token_controller.go @@ -0,0 +1,47 @@ +package controllers + +import ( + "github.com/BrandonRomano/wrecker" + "github.com/carrot/go-pinterest/models" +) + +// OAuthTokenController is the controller that is responsible +// for all /v1/oauth/token endpoints in the Pinterest API. +type OAuthTokenController struct { + wreckerClient *wrecker.Wrecker +} + +// newOAuthTokenController instantiates a new OAuthTokenController +func newOAuthTokenController(wc *wrecker.Wrecker) *OAuthTokenController { + return &OAuthTokenController{ + wreckerClient: wc, + } +} + +// Create generates an access token +// Endpoint: [POST] /v1/oauth/token +func (otc *OAuthTokenController) Create(clientId, clientSecret, accessCode string) (*models.AccessToken, error) { + // Build + execute request + accessToken := new(models.AccessToken) + httpResp, err := otc.wreckerClient.Post("/oauth/token"). + URLParam("grant_type", "authorization_code"). + URLParam("client_id", clientId). + URLParam("client_secret", clientSecret). + URLParam("code", accessCode). + Into(accessToken). + Execute() + + if err != nil { + return nil, err + } + + if !(httpResp.StatusCode >= 200 && httpResp.StatusCode < 300) { + return nil, &models.PinterestError{ + StatusCode: httpResp.StatusCode, + Message: accessToken.Error, + } + } + + // OK + return accessToken, nil +} diff --git a/models/access_token.go b/models/access_token.go new file mode 100644 index 0000000..048e73a --- /dev/null +++ b/models/access_token.go @@ -0,0 +1,11 @@ +package models + +// AccessToken is a struct that represents a Access Token +// response from the Pinterest API. +type AccessToken struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + Scope []string `json:"scope"` + ErrorDescription string `json:"error_description"` + Error string `json:"error"` +} diff --git a/pinterest.go b/pinterest.go index 8d14412..d4de055 100644 --- a/pinterest.go +++ b/pinterest.go @@ -17,6 +17,7 @@ import ( // For more information about the Pinterest API, // check out https://developers.pinterest.com/ type Client struct { + OAuth *controllers.OAuthController Users *controllers.UsersController Boards *controllers.BoardsController Pins *controllers.PinsController @@ -40,6 +41,7 @@ func NewClient() *Client { // Build Pinterest client return &Client{ wreckerClient: wc, + OAuth: controllers.NewOAuthController(wc), Users: controllers.NewUsersController(wc), Boards: controllers.NewBoardsController(wc), Pins: controllers.NewPinsController(wc), diff --git a/pinterest_test.go b/pinterest_test.go index 4ba1614..721ed0d 100644 --- a/pinterest_test.go +++ b/pinterest_test.go @@ -357,18 +357,18 @@ func (suite *ClientTestSuite) TestSuccessfulBoardCUD() { // Updating the Board board, err = suite.client.Boards.Update("brandonrromano/go-pinterest-test", &controllers.BoardUpdateOptionals{ - Name: "Go Pinterest Test2", - Description: "Go Pinterest Test2!", + Name: "Go Pinterest Test3", + Description: "Go Pinterest Test3!", }, ) // Assume there is no error / test result assert.Equal(suite.T(), nil, err) - assert.Equal(suite.T(), board.Name, "Go Pinterest Test2") - assert.Equal(suite.T(), board.Description, "Go Pinterest Test2!") + assert.Equal(suite.T(), board.Name, "Go Pinterest Test3") + assert.Equal(suite.T(), board.Description, "Go Pinterest Test3!") // Deleting the board - err = suite.client.Boards.Delete("brandonrromano/go-pinterest-test2") + err = suite.client.Boards.Delete("brandonrromano/go-pinterest-test3") assert.Equal(suite.T(), nil, err) } @@ -1432,3 +1432,32 @@ func (suite *ClientTestSuite) TestUnauthorizedMeSearchPinsFetch() { assert.Equal(suite.T(), true, false) } } + +// ======================================== +// ========== OAuth.Token.Create ========== +// ======================================== + +// TestTimeoutOAuthTokenCreate tests that an error is appropriately thrown +// when a network timeout occurs +func (suite *ClientTestSuite) TestTimeoutOAuthTokenCreate() { + _, err := suite.timeoutClient.OAuth.Token.Create("", "", "") + assert.NotEqual(suite.T(), nil, err) +} + +// TestUnauthorizedOAuthTokenCreate tests that an error is appropriately thrown +// when the user makes an unauthorized request +func (suite *ClientTestSuite) TestUnauthorizedOAuthTokenCreate() { + _, err := suite.client.OAuth.Token.Create("", "", "") + + // There should be an error + assert.NotEqual(suite.T(), nil, err) + + // Check error type + if pinterestError, ok := err.(*models.PinterestError); ok { + // Should be a 401 + assert.Equal(suite.T(), http.StatusUnauthorized, pinterestError.StatusCode) + } else { + // Make this error out, should always be a PinterestError + assert.Equal(suite.T(), true, false) + } +}