Monorepo for Tangled

idresolver: add PDSClient() helper to reduce duplication #1

open opened by nolith.dev targeting master from local-dev

Add a PDSClient() method on *Resolver that combines the common pattern of resolving an identity and creating an XRPC client for its PDS endpoint.

This pattern was duplicated across ~15 call sites in knotserver and spindle. Each call site manually called ResolveIdent(), checked Handle.IsInvalidHandle(), called PDSEndpoint(), and constructed an xrpc.Client. The new helper consolidates this into a single call.

Also fixes knotserver/internal.go PushAllowed handler which was creating a new DefaultResolver on every request instead of using the shared resolver.

AI-assisted: GitLab Duo Agentic Chat (Claude Opus 4.6) Signed-off-by: Alessio Caiazza code.git@caiazza.info

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:nzep3slobztdph3kxswzbing/sh.tangled.repo.pull/3mgmn4natsc22
+75 -107
Diff #1
+2 -7
appview/models/label.go
··· 12 12 13 13 "github.com/bluesky-social/indigo/api/atproto" 14 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 - "github.com/bluesky-social/indigo/xrpc" 16 15 "tangled.org/core/api/tangled" 17 16 "tangled.org/core/idresolver" 18 17 ) ··· 515 514 return nil, fmt.Errorf("expected AT-URI pointing %s collection: %s", tangled.LabelDefinitionNSID, atUri) 516 515 } 517 516 518 - owner, err := r.ResolveIdent(ctx, atUri.Authority().String()) 517 + _, pdsClient, err := r.PDSClient(ctx, atUri.Authority().String()) 519 518 if err != nil { 520 519 return nil, fmt.Errorf("failed to resolve default label owner DID %s: %v", atUri.Authority(), err) 521 520 } 522 521 523 - xrpcc := xrpc.Client{ 524 - Host: owner.PDSEndpoint(), 525 - } 526 - 527 522 record, err := atproto.RepoGetRecord( 528 523 ctx, 529 - &xrpcc, 524 + pdsClient, 530 525 "", 531 526 atUri.Collection().String(), 532 527 atUri.Authority().String(),
+20
idresolver/resolver.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "fmt" 5 6 "net" 6 7 "net/http" 7 8 "sync" ··· 10 11 "github.com/bluesky-social/indigo/atproto/identity" 11 12 "github.com/bluesky-social/indigo/atproto/identity/redisdir" 12 13 "github.com/bluesky-social/indigo/atproto/syntax" 14 + "github.com/bluesky-social/indigo/xrpc" 13 15 "github.com/carlmjohnson/versioninfo" 14 16 ) 15 17 ··· 83 85 return r.directory.Lookup(ctx, *id) 84 86 } 85 87 88 + // PDSClient resolves an identity and returns an XRPC client pointing at the 89 + // identity's PDS endpoint. This consolidates the common pattern of resolving 90 + // a DID, validating the handle, and constructing an XRPC client for the PDS. 91 + func (r *Resolver) PDSClient(ctx context.Context, did string) (*identity.Identity, *xrpc.Client, error) { 92 + ident, err := r.ResolveIdent(ctx, did) 93 + if err != nil { 94 + return nil, nil, fmt.Errorf("failed to resolve identity: %w", err) 95 + } 96 + if ident.Handle.IsInvalidHandle() { 97 + return nil, nil, fmt.Errorf("invalid handle for %s", did) 98 + } 99 + pdsEndpoint := ident.PDSEndpoint() 100 + if pdsEndpoint == "" { 101 + return nil, nil, fmt.Errorf("no PDS endpoint for %s", did) 102 + } 103 + return ident, &xrpc.Client{Host: pdsEndpoint}, nil 104 + } 105 + 86 106 func (r *Resolver) ResolveIdents(ctx context.Context, idents []string) []*identity.Identity { 87 107 results := make([]*identity.Identity, len(idents)) 88 108 var wg sync.WaitGroup
+9 -18
knotserver/ingester.go
··· 12 12 13 13 comatproto "github.com/bluesky-social/indigo/api/atproto" 14 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 - "github.com/bluesky-social/indigo/xrpc" 16 15 "github.com/bluesky-social/jetstream/pkg/models" 17 16 securejoin "github.com/cyphar/filepath-securejoin" 18 17 "tangled.org/core/api/tangled" ··· 119 118 } 120 119 121 120 // resolve this aturi to extract the repo record 122 - ident, err := h.resolver.ResolveIdent(ctx, repoAt.Authority().String()) 123 - if err != nil || ident.Handle.IsInvalidHandle() { 124 - return fmt.Errorf("failed to resolve handle: %w", err) 125 - } 126 - 127 - xrpcc := xrpc.Client{ 128 - Host: ident.PDSEndpoint(), 121 + ident, pdsClient, err := h.resolver.PDSClient(ctx, repoAt.Authority().String()) 122 + if err != nil { 123 + return fmt.Errorf("failed to resolve identity: %w", err) 129 124 } 130 125 131 - resp, err := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 126 + resp, err := comatproto.RepoGetRecord(ctx, pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 132 127 if err != nil { 133 - return fmt.Errorf("failed to resolver repo: %w", err) 128 + return fmt.Errorf("failed to resolve repo: %w", err) 134 129 } 135 130 136 131 repo := resp.Value.Val.(*tangled.Repo) ··· 238 233 239 234 // TODO: fix this for good, we need to fetch the record here unfortunately 240 235 // resolve this aturi to extract the repo record 241 - owner, err := h.resolver.ResolveIdent(ctx, repoAt.Authority().String()) 242 - if err != nil || owner.Handle.IsInvalidHandle() { 243 - return fmt.Errorf("failed to resolve handle: %w", err) 244 - } 245 - 246 - xrpcc := xrpc.Client{ 247 - Host: owner.PDSEndpoint(), 236 + owner, pdsClient, err := h.resolver.PDSClient(ctx, repoAt.Authority().String()) 237 + if err != nil { 238 + return fmt.Errorf("failed to resolve owner: %w", err) 248 239 } 249 240 250 - resp, err := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 241 + resp, err := comatproto.RepoGetRecord(ctx, pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 251 242 if err != nil { 252 243 return err 253 244 }
+1 -3
knotserver/internal.go
··· 102 102 repoOwner := components[0] 103 103 repoName := components[1] 104 104 105 - resolver := idresolver.DefaultResolver(h.c.Server.PlcUrl) 106 - 107 - repoOwnerIdent, err := resolver.ResolveIdent(r.Context(), repoOwner) 105 + repoOwnerIdent, err := h.res.ResolveIdent(r.Context(), repoOwner) 108 106 if err != nil || repoOwnerIdent.Handle.IsInvalidHandle() { 109 107 l.Error("Error resolving handle", "handle", repoOwner, "err", err) 110 108 w.WriteHeader(http.StatusInternalServerError)
+3 -8
knotserver/xrpc/create_repo.go
··· 10 10 11 11 comatproto "github.com/bluesky-social/indigo/api/atproto" 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 - "github.com/bluesky-social/indigo/xrpc" 14 13 securejoin "github.com/cyphar/filepath-securejoin" 15 14 gogit "github.com/go-git/go-git/v5" 16 15 "tangled.org/core/api/tangled" ··· 51 50 52 51 rkey := data.Rkey 53 52 54 - ident, err := h.Resolver.ResolveIdent(r.Context(), actorDid.String()) 55 - if err != nil || ident.Handle.IsInvalidHandle() { 53 + _, pdsClient, err := h.Resolver.PDSClient(r.Context(), actorDid.String()) 54 + if err != nil { 56 55 fail(xrpcerr.GenericError(err)) 57 56 return 58 57 } 59 58 60 - xrpcc := xrpc.Client{ 61 - Host: ident.PDSEndpoint(), 62 - } 63 - 64 - resp, err := comatproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, actorDid.String(), rkey) 59 + resp, err := comatproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, actorDid.String(), rkey) 65 60 if err != nil { 66 61 fail(xrpcerr.GenericError(err)) 67 62 return
+4 -7
knotserver/xrpc/delete_branch.go
··· 2 2 3 3 import ( 4 4 "encoding/json" 5 - "fmt" 6 5 "net/http" 7 6 8 7 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 8 "github.com/bluesky-social/indigo/atproto/syntax" 10 - "github.com/bluesky-social/indigo/xrpc" 11 9 securejoin "github.com/cyphar/filepath-securejoin" 12 10 "tangled.org/core/api/tangled" 13 11 "tangled.org/core/knotserver/git" ··· 43 41 } 44 42 45 43 // resolve this aturi to extract the repo record 46 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 47 - if err != nil || ident.Handle.IsInvalidHandle() { 48 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 44 + ident, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 45 + if err != nil { 46 + fail(xrpcerr.GenericError(err)) 49 47 return 50 48 } 51 49 52 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 53 - resp, err := comatproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 50 + resp, err := comatproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 54 51 if err != nil { 55 52 fail(xrpcerr.GenericError(err)) 56 53 return
+3 -8
knotserver/xrpc/delete_repo.go
··· 9 9 10 10 comatproto "github.com/bluesky-social/indigo/api/atproto" 11 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 - "github.com/bluesky-social/indigo/xrpc" 13 12 securejoin "github.com/cyphar/filepath-securejoin" 14 13 "tangled.org/core/api/tangled" 15 14 "tangled.org/core/rbac" ··· 44 43 return 45 44 } 46 45 47 - ident, err := x.Resolver.ResolveIdent(r.Context(), actorDid.String()) 48 - if err != nil || ident.Handle.IsInvalidHandle() { 46 + _, pdsClient, err := x.Resolver.PDSClient(r.Context(), actorDid.String()) 47 + if err != nil { 49 48 fail(xrpcerr.GenericError(err)) 50 49 return 51 50 } 52 51 53 - xrpcc := xrpc.Client{ 54 - Host: ident.PDSEndpoint(), 55 - } 56 - 57 52 // ensure that the record does not exists 58 - _, err = comatproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, actorDid.String(), rkey) 53 + _, err = comatproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, actorDid.String(), rkey) 59 54 if err == nil { 60 55 fail(xrpcerr.RecordExistsError(rkey)) 61 56 return
+4 -6
knotserver/xrpc/hidden_ref.go
··· 7 7 8 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 - "github.com/bluesky-social/indigo/xrpc" 11 10 securejoin "github.com/cyphar/filepath-securejoin" 12 11 "tangled.org/core/api/tangled" 13 12 "tangled.org/core/knotserver/git" ··· 49 48 return 50 49 } 51 50 52 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 53 - if err != nil || ident.Handle.IsInvalidHandle() { 54 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 51 + _, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 52 + if err != nil { 53 + fail(xrpcerr.GenericError(err)) 55 54 return 56 55 } 57 56 58 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 59 - resp, err := comatproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 57 + resp, err := comatproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 60 58 if err != nil { 61 59 fail(xrpcerr.GenericError(err)) 62 60 return
+4 -7
knotserver/xrpc/set_default_branch.go
··· 2 2 3 3 import ( 4 4 "encoding/json" 5 - "fmt" 6 5 "net/http" 7 6 8 7 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 8 "github.com/bluesky-social/indigo/atproto/syntax" 10 - "github.com/bluesky-social/indigo/xrpc" 11 9 securejoin "github.com/cyphar/filepath-securejoin" 12 10 "tangled.org/core/api/tangled" 13 11 "tangled.org/core/knotserver/git" ··· 45 43 } 46 44 47 45 // resolve this aturi to extract the repo record 48 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 49 - if err != nil || ident.Handle.IsInvalidHandle() { 50 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 46 + _, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 47 + if err != nil { 48 + fail(xrpcerr.GenericError(err)) 51 49 return 52 50 } 53 51 54 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 55 - resp, err := comatproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 52 + resp, err := comatproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 56 53 if err != nil { 57 54 fail(xrpcerr.GenericError(err)) 58 55 return
+9 -17
spindle/ingester.go
··· 188 188 } 189 189 190 190 // add collaborators to rbac 191 - owner, err := s.res.ResolveIdent(ctx, did) 192 - if err != nil || owner.Handle.IsInvalidHandle() { 191 + owner, pdsClient, err := s.res.PDSClient(ctx, did) 192 + if err != nil { 193 193 return err 194 194 } 195 - if err := s.fetchAndAddCollaborators(ctx, owner, didSlashRepo); err != nil { 195 + if err := s.fetchAndAddCollaborators(ctx, owner, pdsClient, didSlashRepo); err != nil { 196 196 return err 197 197 } 198 198 ··· 236 236 237 237 // TODO: get rid of this entirely 238 238 // resolve this aturi to extract the repo record 239 - owner, err := s.res.ResolveIdent(ctx, repoAt.Authority().String()) 240 - if err != nil || owner.Handle.IsInvalidHandle() { 241 - return fmt.Errorf("failed to resolve handle: %w", err) 242 - } 243 - 244 - xrpcc := xrpc.Client{ 245 - Host: owner.PDSEndpoint(), 239 + owner, pdsClient, err := s.res.PDSClient(ctx, repoAt.Authority().String()) 240 + if err != nil { 241 + return fmt.Errorf("failed to resolve owner: %w", err) 246 242 } 247 243 248 - resp, err := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 244 + resp, err := comatproto.RepoGetRecord(ctx, pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 249 245 if err != nil { 250 246 return err 251 247 } ··· 269 265 return nil 270 266 } 271 267 272 - func (s *Spindle) fetchAndAddCollaborators(ctx context.Context, owner *identity.Identity, didSlashRepo string) error { 268 + func (s *Spindle) fetchAndAddCollaborators(ctx context.Context, owner *identity.Identity, pdsClient *xrpc.Client, didSlashRepo string) error { 273 269 l := s.l.With("component", "ingester", "handler", "fetchAndAddCollaborators") 274 270 275 271 l.Info("fetching and adding existing collaborators") 276 272 277 - xrpcc := xrpc.Client{ 278 - Host: owner.PDSEndpoint(), 279 - } 280 - 281 - resp, err := comatproto.RepoListRecords(ctx, &xrpcc, tangled.RepoCollaboratorNSID, "", 50, owner.DID.String(), false) 273 + resp, err := comatproto.RepoListRecords(ctx, pdsClient, tangled.RepoCollaboratorNSID, "", 50, owner.DID.String(), false) 282 274 if err != nil { 283 275 return err 284 276 }
+4 -7
spindle/xrpc/add_secret.go
··· 2 2 3 3 import ( 4 4 "encoding/json" 5 - "fmt" 6 5 "net/http" 7 6 "time" 8 7 9 8 "github.com/bluesky-social/indigo/api/atproto" 10 9 "github.com/bluesky-social/indigo/atproto/syntax" 11 - "github.com/bluesky-social/indigo/xrpc" 12 10 securejoin "github.com/cyphar/filepath-securejoin" 13 11 "tangled.org/core/api/tangled" 14 12 "tangled.org/core/rbac" ··· 48 46 } 49 47 50 48 // resolve this aturi to extract the repo record 51 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 52 - if err != nil || ident.Handle.IsInvalidHandle() { 53 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 49 + ident, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 50 + if err != nil { 51 + fail(xrpcerr.GenericError(err)) 54 52 return 55 53 } 56 54 57 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 58 - resp, err := atproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 55 + resp, err := atproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 59 56 if err != nil { 60 57 fail(xrpcerr.GenericError(err)) 61 58 return
+4 -6
spindle/xrpc/list_secrets.go
··· 8 8 9 9 "github.com/bluesky-social/indigo/api/atproto" 10 10 "github.com/bluesky-social/indigo/atproto/syntax" 11 - "github.com/bluesky-social/indigo/xrpc" 12 11 securejoin "github.com/cyphar/filepath-securejoin" 13 12 "tangled.org/core/api/tangled" 14 13 "tangled.org/core/rbac" ··· 43 42 } 44 43 45 44 // resolve this aturi to extract the repo record 46 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 47 - if err != nil || ident.Handle.IsInvalidHandle() { 48 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 45 + ident, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 46 + if err != nil { 47 + fail(xrpcerr.GenericError(err)) 49 48 return 50 49 } 51 50 52 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 53 - resp, err := atproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 51 + resp, err := atproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 54 52 if err != nil { 55 53 fail(xrpcerr.GenericError(err)) 56 54 return
+4 -6
spindle/xrpc/pipeline_cancelPipeline.go
··· 8 8 9 9 "github.com/bluesky-social/indigo/api/atproto" 10 10 "github.com/bluesky-social/indigo/atproto/syntax" 11 - "github.com/bluesky-social/indigo/xrpc" 12 11 securejoin "github.com/cyphar/filepath-securejoin" 13 12 "tangled.org/core/api/tangled" 14 13 "tangled.org/core/rbac" ··· 53 52 return 54 53 } 55 54 56 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 57 - if err != nil || ident.Handle.IsInvalidHandle() { 58 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 55 + ident, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 56 + if err != nil { 57 + fail(xrpcerr.GenericError(err)) 59 58 return 60 59 } 61 60 62 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 63 - resp, err := atproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 61 + resp, err := atproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 64 62 if err != nil { 65 63 fail(xrpcerr.GenericError(err)) 66 64 return
+4 -7
spindle/xrpc/remove_secret.go
··· 2 2 3 3 import ( 4 4 "encoding/json" 5 - "fmt" 6 5 "net/http" 7 6 8 7 "github.com/bluesky-social/indigo/api/atproto" 9 8 "github.com/bluesky-social/indigo/atproto/syntax" 10 - "github.com/bluesky-social/indigo/xrpc" 11 9 securejoin "github.com/cyphar/filepath-securejoin" 12 10 "tangled.org/core/api/tangled" 13 11 "tangled.org/core/rbac" ··· 42 40 } 43 41 44 42 // resolve this aturi to extract the repo record 45 - ident, err := x.Resolver.ResolveIdent(r.Context(), repoAt.Authority().String()) 46 - if err != nil || ident.Handle.IsInvalidHandle() { 47 - fail(xrpcerr.GenericError(fmt.Errorf("failed to resolve handle: %w", err))) 43 + ident, pdsClient, err := x.Resolver.PDSClient(r.Context(), repoAt.Authority().String()) 44 + if err != nil { 45 + fail(xrpcerr.GenericError(err)) 48 46 return 49 47 } 50 48 51 - xrpcc := xrpc.Client{Host: ident.PDSEndpoint()} 52 - resp, err := atproto.RepoGetRecord(r.Context(), &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 49 + resp, err := atproto.RepoGetRecord(r.Context(), pdsClient, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String()) 53 50 if err != nil { 54 51 fail(xrpcerr.GenericError(err)) 55 52 return

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
idresolver: add PDSClient() helper to reduce duplication
no conflicts, ready to merge
expand 0 comments
1 commit
expand
idresolver: add PDSClient() helper to reduce duplication
expand 0 comments