Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

at master 3.2 kB View raw
1package xrpc 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "math" 8 "net/http" 9 "runtime/pprof" 10 "time" 11 12 "github.com/bluesky-social/indigo/atproto/atclient" 13 "github.com/bluesky-social/indigo/atproto/syntax" 14 "tangled.org/core/api/tangled" 15 "tangled.org/core/knotserver/git" 16) 17 18const ( 19 RepoLanguagesByDid = "git_list_languages:repo:%s:%s" 20 RepoLanguagesTTL = 24 * time.Hour 21) 22 23func (x *Xrpc) ListLanguages(w http.ResponseWriter, r *http.Request) { 24 var ( 25 repoQuery = r.URL.Query().Get("repo") 26 ref = r.URL.Query().Get("ref") 27 ) 28 l := x.logger.With("method", "git.listLanguages", "repo", repoQuery, "ref", ref) 29 l.Debug("request") 30 31 repo, err := syntax.ParseATURI(repoQuery) 32 if err != nil || repo.RecordKey() == "" { 33 l.Error("invalid repo at-uri", "err", err) 34 writeJson(w, http.StatusBadRequest, atclient.ErrorBody{Name: "BadRequest", Message: fmt.Sprintf("repo parameter invalid: %s", repoQuery)}) 35 return 36 } 37 38 if val, err := x.rdb.Get(r.Context(), fmt.Sprintf(RepoLanguagesByDid, repo, ref)).Result(); err == nil { 39 l.Debug("served from cache") 40 var langs []*tangled.GitTempListLanguages_Language 41 err = json.Unmarshal([]byte(val), &langs) 42 if err == nil { 43 writeJson(w, http.StatusOK, &tangled.GitTempListLanguages_Output{ 44 Ref: ref, 45 Languages: langs, 46 }) 47 return 48 } 49 } 50 51 var out *tangled.GitTempListLanguages_Output 52 pprof.Do(r.Context(), pprof.Labels("repo", repo.String()), func(ctx context.Context) { 53 out, err = x.listLanguages(ctx, repo, ref) 54 }) 55 if err != nil { 56 l.Warn("local mirror failed, trying proxy", "err", err) 57 if x.proxyToKnot(w, r, repo) { 58 return 59 } 60 writeErr(w, err) 61 return 62 } 63 64 writeJson(w, http.StatusOK, out) 65} 66 67func (x *Xrpc) listLanguages(ctx context.Context, repo syntax.ATURI, ref string) (*tangled.GitTempListLanguages_Output, error) { 68 repoPath, err := x.makeRepoPath(ctx, repo) 69 if err != nil { 70 return nil, fmt.Errorf("resolving repo at-uri: %w", err) 71 } 72 73 gr, err := git.Open(repoPath, ref) 74 if err != nil { 75 return nil, &atclient.APIError{StatusCode: http.StatusNotFound, Name: "RepoNotFound", Message: "failed to find git repo"} 76 } 77 78 ctx, cancel := context.WithTimeout(ctx, 1*time.Second) 79 defer cancel() 80 81 sizes, err := gr.AnalyzeLanguages(ctx) 82 if err != nil { 83 return nil, fmt.Errorf("analyzing languages: %w", err) 84 } 85 86 langs := sizesToLanguages(sizes) 87 88 go func() { 89 ctx := context.Background() 90 encoded, err := json.Marshal(langs) 91 if err != nil { 92 return 93 } 94 x.rdb.Set(ctx, fmt.Sprintf(RepoLanguagesByDid, repo, ref), encoded, RepoLanguagesTTL) 95 }() 96 97 return &tangled.GitTempListLanguages_Output{ 98 Ref: ref, 99 Languages: sizesToLanguages(sizes), 100 }, nil 101} 102 103func sizesToLanguages(sizes git.LangBreakdown) []*tangled.GitTempListLanguages_Language { 104 var apiLanguages []*tangled.GitTempListLanguages_Language 105 var totalSize int64 106 for _, size := range sizes { 107 totalSize += size 108 } 109 110 for name, size := range sizes { 111 percentagef64 := float64(size) / float64(totalSize) * 100 112 percentage := math.Round(percentagef64) 113 114 lang := &tangled.GitTempListLanguages_Language{ 115 Name: name, 116 Size: size, 117 Percentage: int64(percentage), 118 } 119 120 apiLanguages = append(apiLanguages, lang) 121 } 122 123 return apiLanguages 124}