Files
anonymous_github/backend/internal/server/provider_github_cache.go
T

88 lines
3.3 KiB
Go

package server
import (
"context"
"strings"
"time"
"github.com/tdurieux/anonymous_github/backend/internal/provider"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
func (g *githubProvider) cachedRepositories(ctx context.Context, user M) ([]provider.Repository, error) {
ids := []bson.ObjectID{}
for _, value := range arr(user["repositories"]) {
if id, err := oid(str(value)); err == nil {
ids = append(ids, id)
}
}
// Match TypeScript's find($in) behavior: database order, unique rows, and
// missing references omitted. Other providers never enter GitHub discovery.
rows, err := g.server.Store.list(ctx, "repositories", M{"_id": M{"$in": ids}, "externalId": M{"$regex": "^gh_"}}, 0, nil)
if err != nil {
return nil, err
}
out := []provider.Repository{}
for _, row := range rows {
out = append(out, provider.Repository{ID: strings.TrimPrefix(str(row["externalId"]), "gh_"), Name: str(row["name"]), URL: str(row["url"]), Size: number(row["size"]), DefaultBranch: str(row["defaultBranch"]), HasPage: boolean(row["hasPage"])})
}
return out, nil
}
func (g *githubProvider) cacheRepositories(ctx context.Context, user M, repos []provider.Repository) error {
ids := []bson.ObjectID{}
externalIDs := make([]string, 0, len(repos))
for _, repo := range repos {
externalIDs = append(externalIDs, externalRepositoryID("github", repo.ID))
}
existing, err := g.server.Store.list(ctx, "repositories", M{"externalId": M{"$in": externalIDs}}, 0, nil)
if err != nil {
return err
}
byExternalID := map[string]M{}
for _, row := range existing {
byExternalID[str(row["externalId"])] = row
}
for _, repo := range repos {
external := externalRepositoryID("github", repo.ID)
cached := byExternalID[external]
if cached == nil {
// TypeScript reuses existing rows without overwriting their metadata.
// Atomic insertion also prevents duplicate rows from concurrent discovery.
insert := M{"_id": bson.NewObjectID(), "externalId": external, "name": repo.Name, "url": repo.URL, "size": repo.Size, "defaultBranch": repo.DefaultBranch, "source": "github", "hasPage": false, "status": "ready", "branches": []any{}, "dateOfEntry": time.Now(), "__v": 0}
err := g.server.Store.DB.Collection("repositories").FindOneAndUpdate(ctx, M{"externalId": external}, M{"$setOnInsert": insert}, options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)).Decode(&cached)
if mongo.IsDuplicateKeyError(err) {
cached, err = g.server.Store.one(ctx, "repositories", M{"externalId": external})
}
if err != nil {
return err
}
byExternalID[external] = cached
}
id, err := oid(str(cached["_id"]))
if err != nil {
return err
}
ids = append(ids, id)
}
filter := M{"_id": user["_id"], "status": user["status"]}
if revision, ok := user["repositoryDiscoveryRevision"]; ok {
filter["repositoryDiscoveryRevision"] = revision
} else {
filter["repositoryDiscoveryRevision"] = M{"$exists": false}
}
// Reconnecting/disconnecting while discovery is in flight invalidates this
// publication, including when the old and new saved lists are both empty.
matched, err := g.server.Store.update(ctx, "users", filter, M{"$set": M{"repositories": ids}})
if err != nil {
return err
}
if !matched {
return fail("connection_changed", 409)
}
return nil
}