blob: 9abd321367c6496bc2241b413db20b6d57fb954c [file] [log] [blame]
package github
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/30x/apid"
"io"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"os"
"regexp"
"strings"
)
var log apid.LogService
func Init(services apid.Services) {
log = services.Log()
}
func printResponseInfo(resp *http.Response) {
d, _ := httputil.DumpResponse(resp, false)
s := strings.Replace(string(d), "\n", "\n ", -1)
fmt.Printf("DumpResponse: %s", s)
}
func jsonPrettyPrint(in []byte) {
var out bytes.Buffer
err := json.Indent(&out, in, "", "\t")
if err != nil {
s := string(in)
if len(s) > 512 {
s = s[:512]
}
fmt.Printf("jsonPrettyPrint err: %v (raw snippet): %s\n", err, s)
}
fmt.Printf("jsonPrettyPrint: %s\n", out.String())
}
// SaveContentFile() invokes GetContentFileData() and writes
// to a destination file. Optional 'destFileName' param defaults to
// content file name if empty string
func SaveContentFile(repo string, contentFilePath string, ref string, destDir string, destFileName string, accessToken string) (destFilePath string, err error) {
data, err := GetContentFileData(repo, contentFilePath, ref, accessToken)
if err != nil {
log.Errorf("Failed getting content file data: %v", err)
return
}
if err = os.MkdirAll(destDir, 0700); err != nil {
log.Errorf("Failed dest directory creation: %v", err)
return
}
defer data.Close()
if destFileName == "" {
x := strings.LastIndex(contentFilePath, "/")
destFileName = contentFilePath[x+1:] // exclude leading slash
}
destFilePath = destDir + "/" + destFileName
f, err := os.Create(destFilePath)
if err != nil {
log.Errorf("Failed dest file creation: %v", err)
}
if _, err := io.Copy(f, data); err != nil {
log.Errorf("Failed dest file copy: %v", err)
}
return
}
/* GetUrlData() validates GitHub URL and invokes GetContentFileData()
to return file data as a ReadCloser, which must be closed.
*/
func GetUrlData(u *url.URL, accessToken string) (io.ReadCloser, error) {
if accessToken == "" {
return nil, fmt.Errorf("invalid accessToken: %v", accessToken)
}
regexStr := `^/(.*)/(blob|raw)/([^/]+)/(.*)$`
validPath := regexp.MustCompile(regexStr)
m := validPath.FindStringSubmatch(u.Path)
if len(m) == 0 {
return nil, errors.New("DownloadFileUrl Invalid URL path (" + u.Path + ") does not match regex: " + regexStr)
}
fmt.Printf("DownloadFileUrl: matches: %v\n", m)
repo := m[1]
branch := m[3]
contentFilePath := m[4]
return GetContentFileData(repo, contentFilePath, branch, accessToken)
}
// GetContentFileData() uses GitHub OAuth access token to retrieve
// metadata and returns a ReadCloser with file content data from a
// GitHub public/private repo.
func GetContentFileData(repo string, contentFilePath string, ref string, accessToken string) (data io.ReadCloser, err error) {
log.Debug("repo:", repo)
log.Debug("contentFilePath:", contentFilePath)
log.Debug("ref:", ref)
x := strings.LastIndex(contentFilePath, "/")
parentPath := contentFilePath[:x+1] // include trailing slash
fileName := contentFilePath[x+1:] // exclude leading slash
log.Debug("parentPath:", parentPath)
log.Debug("fileName:", fileName)
//url := "https://api.github.com/repos/apigee-internal/apidx/contents/mockdata/kms/kms.db-shm?ref=master"
url := "https://api.github.com/repos/" + repo + "/contents" + parentPath + "?ref=" + ref
client := &http.Client{
//CheckRedirect: redirectPolicyFunc,
}
req, err := http.NewRequest("GET", url, nil)
if strings.HasPrefix(accessToken, "UPDATE_FROM_GITHUB_API") {
log.Debug("GitHubAccessToken not configured, so omitting Authorization header")
} else {
req.Header.Add("Authorization", "token "+accessToken)
}
req.Header.Add("Accept", "application/vnd.github.v3.json")
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
printResponseInfo(resp)
// pretty-print JSON output
jsonPrettyPrint(body)
// parse JSON response
type GitHubContentFile struct {
Name string `json:"name"`
Path string `json:"path"`
Sha string `json:"sha"`
Size int32 `json:"size"`
Url string `json:"url"`
Type string `json:"type"`
DownloadUrl string `json:"download_url"`
// ...
}
var contents []GitHubContentFile
err = json.Unmarshal(body, &contents)
if err != nil {
log.Errorf("Failed to parse JSON response: %v", err)
}
fmt.Printf("GitHubContentResp: %+v\n", contents)
// find target content file info
var downloadUrl string
var contentPtr *GitHubContentFile
for _, c := range contents {
if c.Name == fileName {
contentPtr = &c
break
}
}
if contentPtr == nil {
log.Errorf("Error: no content found for: %s", contentFilePath)
} else {
fmt.Printf("Found %s with download_url: %s\n", fileName, contentPtr.DownloadUrl)
downloadUrl = contentPtr.DownloadUrl
}
// download target content file
resp2, err := client.Get(downloadUrl)
if err != nil {
log.Errorf("Error on download: %v", err)
return
}
printResponseInfo(resp2)
return resp2.Body, nil
}
//func main() {
//
// log.Debug("GitHubGet start")
// destDir := "./data"
// accessToken := "abc123"
//
// url, err := url.Parse("https://github.com/apigee-internal/apidx/blob/master/NOTICE")
// if (err != nil) {
// log.Errorf("Failed to parse URL: %v", err)
// return
// }
//
// data, err := GetUrlData(url, accessToken)
// if (err != nil) {
// log.Errorf("Failed to get URL data: %v", err)
// return
// }
// defer data.Close()
//
// if newFile, err := SaveContentFile("apigee-internal/apidx", "/README.md", "master", destDir, "", accessToken); err == nil {
// stat, _ := os.Stat(newFile)
// fmt.Printf("SaveContentFile: %+v", stat)
// } else {
// log.Errorf("Failed to save content file: %v", err)
// }
//
// if newFile, err := SaveContentFile("apigee-internal/apidx", "/README.md", "master", destDir, "saved_file_1", accessToken); err == nil {
// stat, _ := os.Stat(newFile)
// fmt.Printf("SaveContentFile: %+v", stat)
// } else {
// log.Errorf("Failed to save content file: %v", err)
// }
//
//}