github.com/andrewhsu/cli/v2@v2.0.1-0.20210910131313-d4b4061f5b89/pkg/cmd/gist/shared/shared.go (about)

     1  package shared
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/andrewhsu/graphql"
    13  	"github.com/andrewhsu/cli/v2/api"
    14  	"github.com/andrewhsu/cli/v2/internal/ghinstance"
    15  	"github.com/gabriel-vasile/mimetype"
    16  	"github.com/shurcooL/githubv4"
    17  )
    18  
    19  type GistFile struct {
    20  	Filename string `json:"filename,omitempty"`
    21  	Type     string `json:"type,omitempty"`
    22  	Language string `json:"language,omitempty"`
    23  	Content  string `json:"content"`
    24  }
    25  
    26  type GistOwner struct {
    27  	Login string `json:"login,omitempty"`
    28  }
    29  
    30  type Gist struct {
    31  	ID          string               `json:"id,omitempty"`
    32  	Description string               `json:"description"`
    33  	Files       map[string]*GistFile `json:"files"`
    34  	UpdatedAt   time.Time            `json:"updated_at"`
    35  	Public      bool                 `json:"public"`
    36  	HTMLURL     string               `json:"html_url,omitempty"`
    37  	Owner       *GistOwner           `json:"owner,omitempty"`
    38  }
    39  
    40  var NotFoundErr = errors.New("not found")
    41  
    42  func GetGist(client *http.Client, hostname, gistID string) (*Gist, error) {
    43  	gist := Gist{}
    44  	path := fmt.Sprintf("gists/%s", gistID)
    45  
    46  	apiClient := api.NewClientFromHTTP(client)
    47  	err := apiClient.REST(hostname, "GET", path, nil, &gist)
    48  	if err != nil {
    49  		var httpErr api.HTTPError
    50  		if errors.As(err, &httpErr) && httpErr.StatusCode == 404 {
    51  			return nil, NotFoundErr
    52  		}
    53  		return nil, err
    54  	}
    55  
    56  	return &gist, nil
    57  }
    58  
    59  func GistIDFromURL(gistURL string) (string, error) {
    60  	u, err := url.Parse(gistURL)
    61  	if err == nil && strings.HasPrefix(u.Path, "/") {
    62  		split := strings.Split(u.Path, "/")
    63  
    64  		if len(split) > 2 {
    65  			return split[2], nil
    66  		}
    67  
    68  		if len(split) == 2 && split[1] != "" {
    69  			return split[1], nil
    70  		}
    71  	}
    72  
    73  	return "", fmt.Errorf("Invalid gist URL %s", u)
    74  }
    75  
    76  func ListGists(client *http.Client, hostname string, limit int, visibility string) ([]Gist, error) {
    77  	type response struct {
    78  		Viewer struct {
    79  			Gists struct {
    80  				Nodes []struct {
    81  					Description string
    82  					Files       []struct {
    83  						Name string
    84  					}
    85  					IsPublic  bool
    86  					Name      string
    87  					UpdatedAt time.Time
    88  				}
    89  				PageInfo struct {
    90  					HasNextPage bool
    91  					EndCursor   string
    92  				}
    93  			} `graphql:"gists(first: $per_page, after: $endCursor, privacy: $visibility, orderBy: {field: CREATED_AT, direction: DESC})"`
    94  		}
    95  	}
    96  
    97  	perPage := limit
    98  	if perPage > 100 {
    99  		perPage = 100
   100  	}
   101  
   102  	variables := map[string]interface{}{
   103  		"per_page":   githubv4.Int(perPage),
   104  		"endCursor":  (*githubv4.String)(nil),
   105  		"visibility": githubv4.GistPrivacy(strings.ToUpper(visibility)),
   106  	}
   107  
   108  	gql := graphql.NewClient(ghinstance.GraphQLEndpoint(hostname), client)
   109  
   110  	gists := []Gist{}
   111  pagination:
   112  	for {
   113  		var result response
   114  		err := gql.QueryNamed(context.Background(), "GistList", &result, variables)
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  
   119  		for _, gist := range result.Viewer.Gists.Nodes {
   120  			files := map[string]*GistFile{}
   121  			for _, file := range gist.Files {
   122  				files[file.Name] = &GistFile{
   123  					Filename: file.Name,
   124  				}
   125  			}
   126  
   127  			gists = append(
   128  				gists,
   129  				Gist{
   130  					ID:          gist.Name,
   131  					Description: gist.Description,
   132  					Files:       files,
   133  					UpdatedAt:   gist.UpdatedAt,
   134  					Public:      gist.IsPublic,
   135  				},
   136  			)
   137  			if len(gists) == limit {
   138  				break pagination
   139  			}
   140  		}
   141  
   142  		if !result.Viewer.Gists.PageInfo.HasNextPage {
   143  			break
   144  		}
   145  		variables["endCursor"] = githubv4.String(result.Viewer.Gists.PageInfo.EndCursor)
   146  	}
   147  
   148  	return gists, nil
   149  }
   150  
   151  func IsBinaryFile(file string) (bool, error) {
   152  	detectedMime, err := mimetype.DetectFile(file)
   153  	if err != nil {
   154  		return false, err
   155  	}
   156  
   157  	isBinary := true
   158  	for mime := detectedMime; mime != nil; mime = mime.Parent() {
   159  		if mime.Is("text/plain") {
   160  			isBinary = false
   161  			break
   162  		}
   163  	}
   164  	return isBinary, nil
   165  }
   166  
   167  func IsBinaryContents(contents []byte) bool {
   168  	isBinary := true
   169  	for mime := mimetype.Detect(contents); mime != nil; mime = mime.Parent() {
   170  		if mime.Is("text/plain") {
   171  			isBinary = false
   172  			break
   173  		}
   174  	}
   175  	return isBinary
   176  }