Skip to content

Commit

Permalink
[MI-1931] Add API to get projects and get tasks (#3)
Browse files Browse the repository at this point in the history
* [MI-1846]: Added nvmrc file

* Modular folder structure

* Modular folder structure

* [MI-1846]: Base setup

* [MI-1846]: Refactored server base setup

* [MI-1854]: Implement OAuth to access Azure DevOps services

* [MI-1931] Add API to get projects and get tasks

* [MI-1931] Self review fixes

* [MI-1931] Review fixes 1

* [MI-1931] Self review fix

* [MI-1846]: Review fixes

* [MI-1931] Review fixes 2

* [MI-1931] Add json error handling

* [MI-1931] Add comment to the code

* [MI-1931] Add error check

* [MI-1846]: Review fixes

* [MI-1854]: Review fixes

* [MI-1854]: Removed unused config

* [MI-1854]: Added logic to verify state

* [MI-1931] Run fmt

* [MI-1854]: Removed unused configs

* [MI-1854]: Review fixes

* [MI-1931] Remove unused code

* [MI-1931] Correct spelling error

* [MI-1931] Correct formatting

Co-authored-by: Abhishek Verma <[email protected]>
Co-authored-by: Abhishek Verma <[email protected]>
  • Loading branch information
3 people authored Aug 9, 2022
1 parent 2a1e24a commit a52039a
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 5 deletions.
4 changes: 4 additions & 0 deletions server/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ const (
CommandTriggerName = "azuredevops"
HelpText = "###### Mattermost Azure Devops Plugin - Slash Command Help\n"
InvalidCommand = "Invalid command parameters. Please use `/azuredevops help` for more information."

// Authorization constants
Bearer = "Bearer"
Authorization = "Authorization"
)
6 changes: 6 additions & 0 deletions server/constants/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ const (
AuthAttemptExpired = "Authentication attempt expired, please try again"
InvalidAuthState = "Invalid oauth state, please try again"
)

const (
// Error messages
Error = "error"
NotAuthorized = "not authorized"
)
15 changes: 14 additions & 1 deletion server/plugin/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/gorilla/mux"

"github.com/Brightscout/mattermost-plugin-azure-devops/server/constants"
"github.com/Brightscout/mattermost-plugin-azure-devops/server/serializers"
)

// Initializes the plugin REST API
Expand All @@ -30,11 +31,23 @@ func (p *Plugin) InitRoutes() {
// OAuth
s.HandleFunc(constants.PathOAuthConnect, p.OAuthConnect).Methods(http.MethodGet)
s.HandleFunc(constants.PathOAuthCallback, p.OAuthComplete).Methods(http.MethodGet)

// TODO: for testing purpose, remove later
s.HandleFunc("/test", p.testAPI).Methods(http.MethodGet)
}

func (p *Plugin) handleError(w http.ResponseWriter, r *http.Request, error *serializers.Error) {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(error.Code)
message := map[string]string{constants.Error: error.Message}
response, err := json.Marshal(message)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
if _, err := w.Write(response); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

func (p *Plugin) WithRecovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
Expand Down
14 changes: 10 additions & 4 deletions server/plugin/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,23 @@ func (c *client) GenerateOAuthToken(encodedFormValues string) (*serializers.OAut
}

// Wrapper to make REST API requests with "application/json" type content
func (c *client) callJSON(url, path, method string, in, out interface{}) (responseData []byte, err error) {
func (c *client) callJSON(url, path, method string, mattermostUserID string, in, out interface{}) (responseData []byte, err error) {
contentType := "application/json"
buf := &bytes.Buffer{}
if err = json.NewEncoder(buf).Encode(in); err != nil {
return nil, err
}
return c.call(url, method, path, contentType, buf, out, "")
return c.call(url, method, path, contentType, mattermostUserID, buf, out, "")
}

// Wrapper to make REST API requests with "application/x-www-form-urlencoded" type content
func (c *client) callFormURLEncoded(url, path, method string, out interface{}, formValues string) (responseData []byte, err error) {
contentType := "application/x-www-form-urlencoded"
return c.call(url, method, path, contentType, nil, out, formValues)
return c.call(url, method, path, contentType, "", nil, out, formValues)
}

// Makes HTTP request to REST APIs
func (c *client) call(basePath, method, path, contentType string, inBody io.Reader, out interface{}, formValues string) (responseData []byte, err error) {
func (c *client) call(basePath, method, path, contentType string, mattermostUserID string, inBody io.Reader, out interface{}, formValues string) (responseData []byte, err error) {
errContext := fmt.Sprintf("Azure Devops: Call failed: method:%s, path:%s", method, path)
pathURL, err := url.Parse(path)
if err != nil {
Expand Down Expand Up @@ -97,6 +97,12 @@ func (c *client) call(basePath, method, path, contentType string, inBody io.Read
req.Header.Add("Content-Type", contentType)
}

if mattermostUserID != "" {
if err = c.plugin.AddAuthorization(req, mattermostUserID); err != nil {
return nil, err
}
}

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
Expand Down
22 changes: 22 additions & 0 deletions server/plugin/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/base64"
"fmt"
"io"
"net/http"
"strings"

"github.com/Brightscout/mattermost-plugin-azure-devops/server/constants"
Expand Down Expand Up @@ -131,3 +132,24 @@ func (p *Plugin) GetPluginURLPath() string {
func (p *Plugin) GetPluginURL() string {
return fmt.Sprintf("%s%s", strings.TrimRight(p.GetSiteURL(), "/"), p.GetPluginURLPath())
}

// AddAuthorization function to add authorization to a request.
func (p *Plugin) AddAuthorization(r *http.Request, mattermostUserID string) error {
user, err := p.Store.LoadUser(mattermostUserID)
if err != nil {
return err
}

decodedAccessToken, err := p.decode(user.AccessToken)
if err != nil {
return err
}

decryptedAccessToken, err := p.decrypt(decodedAccessToken, []byte(p.getConfiguration().EncryptionSecret))
if err != nil {
return err
}

r.Header.Add(constants.Authorization, fmt.Sprintf("%s %s", constants.Bearer, string(decryptedAccessToken)))
return nil
}
7 changes: 7 additions & 0 deletions server/serializers/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package serializers

// Error struct to store error codes and error message.
type Error struct {
Code int
Message string
}

0 comments on commit a52039a

Please sign in to comment.