github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/core/section/github/github.go (about)

     1  // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
     2  //
     3  // This software (Documize Community Edition) is licensed under
     4  // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
     5  //
     6  // You can operate outside the AGPL restrictions by purchasing
     7  // Documize Enterprise Edition and obtaining a commercial license
     8  // by contacting <sales@documize.com>.
     9  //
    10  // https://documize.com
    11  
    12  package github
    13  
    14  import (
    15  	"bytes"
    16  	"encoding/json"
    17  	"errors"
    18  	//"fmt"
    19  	"html/template"
    20  	"io/ioutil"
    21  	"net/http"
    22  	"strings"
    23  
    24  	"github.com/documize/community/core/log"
    25  	"github.com/documize/community/core/section/provider"
    26  
    27  	gogithub "github.com/google/go-github/github"
    28  )
    29  
    30  // TODO find a smaller image than the one below
    31  const githubGravatar = "https://i2.wp.com/assets-cdn.github.com/images/gravatars/gravatar-user-420.png"
    32  
    33  var meta provider.TypeMeta
    34  
    35  func init() {
    36  	meta = provider.TypeMeta{}
    37  
    38  	meta.ID = "38c0e4c5-291c-415e-8a4d-262ee80ba5df"
    39  	meta.Title = "GitHub"
    40  	meta.Description = "Link code commits and issues"
    41  	meta.ContentType = "github"
    42  	meta.PageType = "tab"
    43  	meta.Callback = Callback
    44  }
    45  
    46  // Provider represents GitHub
    47  type Provider struct {
    48  }
    49  
    50  // Meta describes us.
    51  func (*Provider) Meta() provider.TypeMeta {
    52  	return meta
    53  }
    54  
    55  // Command to run the various functions required...
    56  func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
    57  	query := r.URL.Query()
    58  	method := query.Get("method")
    59  
    60  	if len(method) == 0 {
    61  		msg := "missing method name"
    62  		log.ErrorString("github: " + msg)
    63  		provider.WriteMessage(w, "gitub", msg)
    64  		return
    65  	}
    66  
    67  	if method == "config" {
    68  		var ret struct {
    69  			CID string `json:"clientID"`
    70  			URL string `json:"authorizationCallbackURL"`
    71  		}
    72  		ret.CID = clientID()
    73  		ret.URL = authorizationCallbackURL()
    74  		provider.WriteJSON(w, ret)
    75  		return
    76  	}
    77  
    78  	defer r.Body.Close() // ignore error
    79  
    80  	body, err := ioutil.ReadAll(r.Body)
    81  
    82  	if err != nil {
    83  		msg := "Bad body"
    84  		log.ErrorString("github: " + msg)
    85  		provider.WriteMessage(w, "github", msg)
    86  		return
    87  	}
    88  
    89  	if method == "saveSecret" { // secret Token update code
    90  
    91  		// write the new one, direct from JS
    92  		if err = ctx.SaveSecrets(string(body)); err != nil {
    93  			log.Error("github settoken configuration", err)
    94  			provider.WriteError(w, "github", err)
    95  			return
    96  		}
    97  		provider.WriteEmpty(w)
    98  		return
    99  
   100  	}
   101  
   102  	// load the config from the client-side
   103  	config := githubConfig{}
   104  	err = json.Unmarshal(body, &config)
   105  
   106  	if err != nil {
   107  		log.Error("github Command Unmarshal", err)
   108  		provider.WriteError(w, "github", err)
   109  		return
   110  	}
   111  
   112  	config.Clean()
   113  	// always use DB version of the token
   114  	config.Token = ctx.GetSecrets("token") // get the secret token in the database
   115  
   116  	client := p.githubClient(&config)
   117  
   118  	switch method {
   119  
   120  	case "checkAuth":
   121  
   122  		if len(config.Token) == 0 {
   123  			err = errors.New("empty github token")
   124  		} else {
   125  			err = validateToken(config.Token)
   126  		}
   127  		if err != nil {
   128  			// token now invalid, so wipe it
   129  			ctx.SaveSecrets("") // ignore error, already in an error state
   130  			log.Error("github check token validation", err)
   131  			provider.WriteError(w, "github", err)
   132  			return
   133  		}
   134  		provider.WriteEmpty(w)
   135  
   136  	default:
   137  
   138  		if listFailed(method, config, client, w) {
   139  
   140  			gr := githubRender{}
   141  			for _, rep := range reports {
   142  				log.IfErr(rep.refresh(&gr, &config, client))
   143  			}
   144  			provider.WriteJSON(w, &gr)
   145  
   146  		}
   147  
   148  	}
   149  }
   150  
   151  // Refresh ... gets the latest version
   152  func (p *Provider) Refresh(ctx *provider.Context, configJSON, data string) string {
   153  	var c = githubConfig{}
   154  
   155  	err := json.Unmarshal([]byte(configJSON), &c)
   156  
   157  	if err != nil {
   158  		log.Error("unable to unmarshall github config", err)
   159  		return "internal configuration error '" + err.Error() + "'"
   160  	}
   161  
   162  	c.Clean()
   163  	c.Token = ctx.GetSecrets("token")
   164  
   165  	client := p.githubClient(&c)
   166  
   167  	byts, err := json.Marshal(refreshReportData(&c, client))
   168  	if err != nil {
   169  		log.Error("unable to marshall github data", err)
   170  		return "internal configuration error '" + err.Error() + "'"
   171  	}
   172  
   173  	return string(byts)
   174  
   175  }
   176  
   177  func refreshReportData(c *githubConfig, client *gogithub.Client) *githubRender {
   178  	var gr = githubRender{}
   179  	for _, rep := range reports {
   180  		log.IfErr(rep.refresh(&gr, c, client))
   181  	}
   182  	return &gr
   183  }
   184  
   185  // Render ... just returns the data given, suitably formatted
   186  func (p *Provider) Render(ctx *provider.Context, config, data string) string {
   187  	var err error
   188  
   189  	payload := githubRender{}
   190  	var c = githubConfig{}
   191  
   192  	err = json.Unmarshal([]byte(config), &c)
   193  
   194  	if err != nil {
   195  		log.Error("unable to unmarshall github config", err)
   196  		return "Please delete and recreate this Github section."
   197  	}
   198  
   199  	c.Clean()
   200  	c.Token = ctx.GetSecrets("token")
   201  
   202  	data = strings.TrimSpace(data)
   203  	if len(data) == 0 {
   204  		// TODO review why this error occurs & if it should be reported - seems to occur for new sections
   205  		// log.ErrorString(fmt.Sprintf("Rendered empty github JSON payload as '' for owner %s repos %#v", c.Owner, c.Lists))
   206  		return ""
   207  	}
   208  
   209  	err = json.Unmarshal([]byte(data), &payload)
   210  	if err != nil {
   211  		log.Error("unable to unmarshall github data", err)
   212  		return "Please delete and recreate this Github section."
   213  	}
   214  
   215  	payload.Config = c
   216  	payload.Limit = c.BranchLines
   217  	payload.List = c.Lists
   218  
   219  	ret := ""
   220  	for _, repID := range c.ReportOrder {
   221  
   222  		rep, ok := reports[repID]
   223  		if !ok {
   224  			msg := "github report not found for: " + repID
   225  			log.ErrorString(msg)
   226  			return "Documize internal error: " + msg
   227  		}
   228  
   229  		if err = rep.render(&payload, &c); err != nil {
   230  			log.Error("unable to render "+repID, err)
   231  			return "Documize internal github render " + repID + " error: " + err.Error() + "<BR>" + data
   232  		}
   233  
   234  		t := template.New("github")
   235  
   236  		t, err = t.Parse(rep.template)
   237  
   238  		if err != nil {
   239  			log.Error("github render template.Parse error:", err)
   240  			//for k, v := range strings.Split(rep.template, "\n") {
   241  			//	fmt.Println("DEBUG", k+1, v)
   242  			//}
   243  			return "Documize internal github template.Parse error: " + err.Error()
   244  		}
   245  
   246  		buffer := new(bytes.Buffer)
   247  		err = t.Execute(buffer, payload)
   248  		if err != nil {
   249  			log.Error("github render template.Execute error:", err)
   250  			return "Documize internal github template.Execute error: " + err.Error()
   251  		}
   252  
   253  		ret += buffer.String()
   254  
   255  	}
   256  	return ret
   257  }