diff --git a/examples/oauth2/main.go b/examples/oauth2/main.go new file mode 100644 index 0000000..9a98c03 --- /dev/null +++ b/examples/oauth2/main.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "html/template" + "io/ioutil" + "log" + "net/http" + "os" + "path" + + jira "github.com/andygrunwald/go-jira" + jwt "golang.org/x/oauth2/jira" +) + +// IssueEvent holds all issue change data +// This isn't even close to the full struct that +// JIRA returns. This is a sample only. +type IssueEvent struct { + Timestamp int64 `json:"timestamp"` + WebhookEvent string `json:"webhookEvent"` + IssueEventTypeName string `json:"issue_event_type_name"` + Issue struct { + ID string `json:"id"` + Self string `json:"self"` + Key string `json:"key"` + } `json:"issue"` +} + +// SecurityContext holds the information from the installation handshake +type SecurityContext struct { + Key string `json:"key"` + ClientKey string `json:"clientKey"` + OauthClientID string `json:"oauthClientId"` + PublicKey string `json:"publicKey"` + SharedSecret string `json:"sharedSecret"` + ServerVersion string `json:"serverVersion"` + PluginsVersion string `json:"pluginsVersion"` + BaseURL string `json:"baseUrl"` + ProductType string `json:"productType"` + Description string `json:"description"` + EventType string `json:"eventType"` +} + +func main() { + var ( + port = flag.String("port", "8080", "web server port") + baseURL = flag.String("baseurl", os.Getenv("BASE_URL"), "local base url") + ) + flag.Parse() + + log.Printf("Example server - running on port:%v\nYou should spin up ngrok to expose this to your Jira dev instance", *port) + + // This is the JWT config. I just pass it around everywhere here but I'd + // probably use a context or something if this was a real app + config := &jwt.Config{} + + http.HandleFunc("/atlassian-connect.json", atlassianConnect(*baseURL, "config")) + + http.HandleFunc("/installed", installed(config)) + http.HandleFunc("/enabled", enabled(config)) + + http.HandleFunc("/issue_event", handleIssueEvent(config)) + + http.HandleFunc("/uninstalled", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + http.ListenAndServe(":"+*port, nil) +} + +func atlassianConnect(baseURL string, templateName string) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + lp := path.Join("./templates", "atlassian-connect.json") + vals := map[string]string{ + "BaseURL": baseURL, + } + tmpl, err := template.ParseFiles(lp) + if err != nil { + log.Fatalf("%v", err) + http.Error(w, err.Error(), 500) + return + } + tmpl.ExecuteTemplate(w, templateName, vals) + } +} + +func installed(c *jwt.Config) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Fatalf("Can't read request:%v\n", err) + http.Error(w, err.Error(), 500) + return + } + + var sc SecurityContext + json.Unmarshal(body, &sc) + + // Set during install + // This is only for the example. If this was a real app you + // should probably save the security context somewhere + // and create the config from that. This app only works if you install + // it and never shut it down. + c.BaseURL = sc.BaseURL + c.Subject = "admin" + c.Config.ClientID = sc.OauthClientID + c.Config.ClientSecret = sc.SharedSecret + c.Config.Endpoint.AuthURL = "https://auth.atlassian.io" + c.Config.Endpoint.TokenURL = "https://auth.atlassian.io/oauth2/token" + + json.NewEncoder(w).Encode([]string{"OK"}) + } +} + +func enabled(c *jwt.Config) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Fatalf("Can't read request:%v\n", err) + http.Error(w, err.Error(), 500) + return + } + + fmt.Printf("%v\n", string(body)) + + json.NewEncoder(w).Encode([]string{"OK"}) + } +} + +func handleIssueEvent(c *jwt.Config) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Fatalf("Can't read request:%v\n", err) + http.Error(w, err.Error(), 500) + return + } + + var ie IssueEvent + json.Unmarshal(body, &ie) + + ctx := context.Background() + jiraClient, _ := jira.NewClient(c.Client(ctx), c.BaseURL) + issue, _, err := jiraClient.Issue.Get(ie.Issue.Key, nil) + if err != nil { + log.Fatalf("failed to get issue: %v\n", err) + http.Error(w, err.Error(), 500) + return + } + fmt.Print("ISSUE INFO:\n") + issueJSON, err := json.MarshalIndent(issue, "", " ") + if err != nil { + log.Fatalf("failed to marshal issue JSON: %v\n", err) + http.Error(w, err.Error(), 500) + return + } + fmt.Print(string(issueJSON)) + + json.NewEncoder(w).Encode([]string{"OK"}) + } +} diff --git a/examples/oauth2/templates/atlassian-connect.json b/examples/oauth2/templates/atlassian-connect.json new file mode 100644 index 0000000..42e7698 --- /dev/null +++ b/examples/oauth2/templates/atlassian-connect.json @@ -0,0 +1,52 @@ +{{define "config"}}{ + "key": "jira-golang-oauth2-example", + "name": "Golang ouath2 example add-on", + "description": "Some add-on examples in Go", + "vendor": { + "name": "Bob Briski", + "url": "https://github.com/rbriski" + }, + "baseUrl": "{{ .BaseURL }}", + "links": { + "self": "{{ .BaseURL }}/atlassian-connect.json", + "homepage": "{{ .BaseURL }}/atlassian-connect.json" + }, + "authentication": { + "type": "jwt" + }, + "lifecycle": { + "installed": "/installed", + "uninstalled": "/uninstalled", + "enabled": "/enabled" + }, + "scopes": [ + "ACT_AS_USER", + "ADMIN", + "READ", + "WRITE", + "PROJECT_ADMIN" + ], + "modules": { + "webhooks": [ + { + "event": "jira:issue_created", + "url": "/issue_event" + }, + { + "event": "jira:issue_deleted", + "url": "/issue_event" + }, + { + "event": "jira:issue_updated", + "url": "/issue_event" + } + ] + } + } + {{ end }} + + + + + +