diff --git a/cmd/precise-code-intel-api-server/internal/server/handler.go b/cmd/precise-code-intel-api-server/internal/server/handler.go index eef4bc202479bb..41e22e63fe5cf9 100644 --- a/cmd/precise-code-intel-api-server/internal/server/handler.go +++ b/cmd/precise-code-intel-api-server/internal/server/handler.go @@ -7,7 +7,7 @@ import ( "github.com/gorilla/mux" "github.com/inconshreveable/log15" "github.com/pkg/errors" - "github.com/sourcegraph/sourcegraph/cmd/precise-code-intel-api-server/internal/api" + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" "github.com/sourcegraph/sourcegraph/internal/codeintel/gitserver" ) diff --git a/cmd/precise-code-intel-api-server/internal/server/locations.go b/cmd/precise-code-intel-api-server/internal/server/locations.go index 7588b7072e972f..a095dd99ec5a19 100644 --- a/cmd/precise-code-intel-api-server/internal/server/locations.go +++ b/cmd/precise-code-intel-api-server/internal/server/locations.go @@ -1,7 +1,7 @@ package server import ( - "github.com/sourcegraph/sourcegraph/cmd/precise-code-intel-api-server/internal/api" + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" ) diff --git a/cmd/precise-code-intel-api-server/internal/server/server.go b/cmd/precise-code-intel-api-server/internal/server/server.go index f0af2d2e3daf95..6c4c93009832fc 100644 --- a/cmd/precise-code-intel-api-server/internal/server/server.go +++ b/cmd/precise-code-intel-api-server/internal/server/server.go @@ -9,7 +9,7 @@ import ( "sync" "github.com/inconshreveable/log15" - "github.com/sourcegraph/sourcegraph/cmd/precise-code-intel-api-server/internal/api" + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" "github.com/sourcegraph/sourcegraph/internal/codeintel/db" "github.com/sourcegraph/sourcegraph/internal/codeintel/enqueuer" diff --git a/cmd/precise-code-intel-api-server/main.go b/cmd/precise-code-intel-api-server/main.go index 3d003907403565..11ba0f16ec9870 100644 --- a/cmd/precise-code-intel-api-server/main.go +++ b/cmd/precise-code-intel-api-server/main.go @@ -9,8 +9,8 @@ import ( "github.com/inconshreveable/log15" "github.com/opentracing/opentracing-go" "github.com/prometheus/client_golang/prometheus" - "github.com/sourcegraph/sourcegraph/cmd/precise-code-intel-api-server/internal/api" "github.com/sourcegraph/sourcegraph/cmd/precise-code-intel-api-server/internal/server" + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" "github.com/sourcegraph/sourcegraph/internal/codeintel/db" "github.com/sourcegraph/sourcegraph/internal/codeintel/gitserver" diff --git a/enterprise/cmd/frontend/main.go b/enterprise/cmd/frontend/main.go index bdeab65a2b8fd3..e6e564c417ba22 100644 --- a/enterprise/cmd/frontend/main.go +++ b/enterprise/cmd/frontend/main.go @@ -32,8 +32,12 @@ import ( campaignsResolvers "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers" "github.com/sourcegraph/sourcegraph/enterprise/internal/codeintel/lsifserver/proxy" codeIntelResolvers "github.com/sourcegraph/sourcegraph/enterprise/internal/codeintel/resolvers" + codeintelapi "github.com/sourcegraph/sourcegraph/internal/codeintel/api" bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" codeinteldb "github.com/sourcegraph/sourcegraph/internal/codeintel/db" + "github.com/sourcegraph/sourcegraph/internal/codeintel/enqueuer" + codeintelgitserver "github.com/sourcegraph/sourcegraph/internal/codeintel/gitserver" + lsifserverclient "github.com/sourcegraph/sourcegraph/internal/codeintel/lsifserver/client" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/db/dbconn" "github.com/sourcegraph/sourcegraph/internal/db/globalstatedb" @@ -49,8 +53,9 @@ func main() { } initLicensing() - initResolvers() - initLSIFEndpoints() + initAuthz() + initCampaigns() + initCodeIntel() clock := func() time.Time { return time.Now().UTC().Truncate(time.Microsecond) @@ -144,9 +149,7 @@ func initLicensing() { } } -func initResolvers() { - graphqlbackend.NewCampaignsResolver = campaignsResolvers.NewResolver - graphqlbackend.NewCodeIntelResolver = codeIntelResolvers.NewResolver +func initAuthz() { graphqlbackend.NewAuthzResolver = func() graphqlbackend.AuthzResolver { return authzResolvers.NewResolver(dbconn.Global, func() time.Time { return time.Now().UTC().Truncate(time.Microsecond) @@ -154,24 +157,41 @@ func initResolvers() { } } +func initCampaigns() { + graphqlbackend.NewCampaignsResolver = campaignsResolvers.NewResolver + +} + var bundleManagerURL = env.Get("PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL", "", "HTTP address for internal LSIF bundle manager server.") -func initLSIFEndpoints() { - httpapi.NewLSIFServerProxy = func() (*httpapi.LSIFServerProxy, error) { - if bundleManagerURL == "" { - log.Fatalf("invalid value for PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL: no value supplied") - } +func initCodeIntel() { + if bundleManagerURL == "" { + log.Fatalf("invalid value for PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL: no value supplied") + } - observationContext := &observation.Context{ - Logger: log15.Root(), - Tracer: &trace.Tracer{Tracer: opentracing.GlobalTracer()}, - Registerer: prometheus.DefaultRegisterer, - } + observationContext := &observation.Context{ + Logger: log15.Root(), + Tracer: &trace.Tracer{Tracer: opentracing.GlobalTracer()}, + Registerer: prometheus.DefaultRegisterer, + } + + db := codeinteldb.NewObserved(codeinteldb.NewWithHandle(dbconn.Global), observationContext) + bundleManagerClient := bundles.New(bundleManagerURL) + + client := lsifserverclient.New( + db, + bundleManagerClient, + codeintelapi.New(db, bundleManagerClient, codeintelgitserver.DefaultClient), + ) + + enqueuer := enqueuer.NewEnqueuer(db, bundleManagerClient) - db := codeinteldb.NewObserved(codeinteldb.NewWithHandle(dbconn.Global), observationContext) - bundleManagerClient := bundles.New(bundleManagerURL) + graphqlbackend.NewCodeIntelResolver = func() graphqlbackend.CodeIntelResolver { + return codeIntelResolvers.NewResolver(client) + } - return proxy.NewProxy(db, bundleManagerClient) + httpapi.NewLSIFServerProxy = func() (*httpapi.LSIFServerProxy, error) { + return proxy.NewProxy(enqueuer, client) } } diff --git a/enterprise/internal/codeintel/lsifserver/proxy/proxy.go b/enterprise/internal/codeintel/lsifserver/proxy/proxy.go index f39cad6a748501..a88f8e96cc97a0 100644 --- a/enterprise/internal/codeintel/lsifserver/proxy/proxy.go +++ b/enterprise/internal/codeintel/lsifserver/proxy/proxy.go @@ -14,21 +14,20 @@ import ( "github.com/sourcegraph/sourcegraph/cmd/frontend/httpapi" "github.com/sourcegraph/sourcegraph/cmd/frontend/types" "github.com/sourcegraph/sourcegraph/internal/api" - bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" - codeinteldb "github.com/sourcegraph/sourcegraph/internal/codeintel/db" "github.com/sourcegraph/sourcegraph/internal/codeintel/enqueuer" + "github.com/sourcegraph/sourcegraph/internal/codeintel/lsifserver/client" "github.com/sourcegraph/sourcegraph/internal/conf" "github.com/sourcegraph/sourcegraph/internal/errcode" "github.com/sourcegraph/sourcegraph/internal/gitserver" ) -func NewProxy(db codeinteldb.DB, bundleManagerClient bundles.BundleManagerClient) (*httpapi.LSIFServerProxy, error) { +func NewProxy(enqueuer *enqueuer.Enqueuer, lsifserverClient *client.Client) (*httpapi.LSIFServerProxy, error) { return &httpapi.LSIFServerProxy{ - UploadHandler: http.HandlerFunc(uploadProxyHandler(enqueuer.NewEnqueuer(db, bundleManagerClient))), + UploadHandler: http.HandlerFunc(uploadProxyHandler(enqueuer, lsifserverClient)), }, nil } -func uploadProxyHandler(enqueuer *enqueuer.Enqueuer) func(http.ResponseWriter, *http.Request) { +func uploadProxyHandler(enqueuer *enqueuer.Enqueuer, lsifserverClient *client.Client) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() repoName := q.Get("repository") diff --git a/enterprise/internal/codeintel/resolvers/query.go b/enterprise/internal/codeintel/resolvers/query.go index 67ae028fc54f70..581b4db0c146a8 100644 --- a/enterprise/internal/codeintel/resolvers/query.go +++ b/enterprise/internal/codeintel/resolvers/query.go @@ -13,6 +13,8 @@ import ( ) type lsifQueryResolver struct { + lsifserverClient *client.Client + repositoryResolver *graphqlbackend.RepositoryResolver // commit is the requested target commit commit api.CommitID @@ -50,7 +52,7 @@ func (r *lsifQueryResolver) Definitions(ctx context.Context, args *graphqlbacken UploadID: upload.ID, } - locations, _, err := client.DefaultClient.Definitions(ctx, opts) + locations, _, err := r.lsifserverClient.Definitions(ctx, opts) if err != nil { return nil, err } @@ -121,7 +123,7 @@ func (r *lsifQueryResolver) References(ctx context.Context, args *graphqlbackend continue } - locations, nextURL, err := client.DefaultClient.References(ctx, opts) + locations, nextURL, err := r.lsifserverClient.References(ctx, opts) if err != nil { return nil, err } @@ -155,7 +157,7 @@ func (r *lsifQueryResolver) Hover(ctx context.Context, args *graphqlbackend.LSIF continue } - text, lspRange, err := client.DefaultClient.Hover(ctx, &struct { + text, lspRange, err := r.lsifserverClient.Hover(ctx, &struct { RepoID api.RepoID Commit api.CommitID Path string diff --git a/enterprise/internal/codeintel/resolvers/resolver.go b/enterprise/internal/codeintel/resolvers/resolver.go index 0b956101417319..0782c7e7c07aae 100644 --- a/enterprise/internal/codeintel/resolvers/resolver.go +++ b/enterprise/internal/codeintel/resolvers/resolver.go @@ -11,12 +11,16 @@ import ( "github.com/sourcegraph/sourcegraph/internal/codeintel/lsifserver/client" ) -type Resolver struct{} +type Resolver struct { + lsifserverClient *client.Client +} var _ graphqlbackend.CodeIntelResolver = &Resolver{} -func NewResolver() graphqlbackend.CodeIntelResolver { - return &Resolver{} +func NewResolver(lsifserverClient *client.Client) graphqlbackend.CodeIntelResolver { + return &Resolver{ + lsifserverClient: lsifserverClient, + } } func (r *Resolver) LSIFUploadByID(ctx context.Context, id graphql.ID) (graphqlbackend.LSIFUploadResolver, error) { @@ -25,7 +29,7 @@ func (r *Resolver) LSIFUploadByID(ctx context.Context, id graphql.ID) (graphqlba return nil, err } - lsifUpload, err := client.DefaultClient.GetUpload(ctx, &struct { + lsifUpload, err := r.lsifserverClient.GetUpload(ctx, &struct { UploadID int64 }{ UploadID: uploadID, @@ -48,7 +52,7 @@ func (r *Resolver) DeleteLSIFUpload(ctx context.Context, id graphql.ID) (*graphq return nil, err } - err = client.DefaultClient.DeleteUpload(ctx, &struct { + err = r.lsifserverClient.DeleteUpload(ctx, &struct { UploadID int64 }{ UploadID: uploadID, @@ -87,11 +91,11 @@ func (r *Resolver) LSIFUploads(ctx context.Context, args *graphqlbackend.LSIFRep opt.NextURL = &nextURL } - return &lsifUploadConnectionResolver{opt: opt}, nil + return &lsifUploadConnectionResolver{lsifserverClient: r.lsifserverClient, opt: opt}, nil } func (r *Resolver) LSIF(ctx context.Context, args *graphqlbackend.LSIFQueryArgs) (graphqlbackend.LSIFQueryResolver, error) { - uploads, err := client.DefaultClient.Exists(ctx, &struct { + uploads, err := r.lsifserverClient.Exists(ctx, &struct { RepoID api.RepoID Commit api.CommitID Path string @@ -110,6 +114,7 @@ func (r *Resolver) LSIF(ctx context.Context, args *graphqlbackend.LSIFQueryArgs) } return &lsifQueryResolver{ + lsifserverClient: r.lsifserverClient, repositoryResolver: args.Repository, commit: args.Commit, path: args.Path, diff --git a/enterprise/internal/codeintel/resolvers/upload.go b/enterprise/internal/codeintel/resolvers/upload.go index 7bb2668b04aa50..fc4f26e2bd7f2f 100644 --- a/enterprise/internal/codeintel/resolvers/upload.go +++ b/enterprise/internal/codeintel/resolvers/upload.go @@ -107,6 +107,8 @@ type LSIFUploadsListOptions struct { } type lsifUploadConnectionResolver struct { + lsifserverClient *client.Client + opt LSIFUploadsListOptions // cache results because they are used by multiple fields @@ -166,7 +168,7 @@ func (r *lsifUploadConnectionResolver) compute(ctx context.Context) ([]*lsif.LSI return } - r.uploads, r.nextURL, r.totalCount, r.err = client.DefaultClient.GetUploads(ctx, &struct { + r.uploads, r.nextURL, r.totalCount, r.err = r.lsifserverClient.GetUploads(ctx, &struct { RepoID api.RepoID Query *string State *string diff --git a/cmd/precise-code-intel-api-server/internal/api/api.go b/internal/codeintel/api/api.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/api.go rename to internal/codeintel/api/api.go diff --git a/cmd/precise-code-intel-api-server/internal/api/api_test.go b/internal/codeintel/api/api_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/api_test.go rename to internal/codeintel/api/api_test.go diff --git a/cmd/precise-code-intel-api-server/internal/api/cursor.go b/internal/codeintel/api/cursor.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/cursor.go rename to internal/codeintel/api/cursor.go diff --git a/cmd/precise-code-intel-api-server/internal/api/cursor_test.go b/internal/codeintel/api/cursor_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/cursor_test.go rename to internal/codeintel/api/cursor_test.go diff --git a/cmd/precise-code-intel-api-server/internal/api/definitions.go b/internal/codeintel/api/definitions.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/definitions.go rename to internal/codeintel/api/definitions.go diff --git a/cmd/precise-code-intel-api-server/internal/api/definitions_test.go b/internal/codeintel/api/definitions_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/definitions_test.go rename to internal/codeintel/api/definitions_test.go diff --git a/cmd/precise-code-intel-api-server/internal/api/exists.go b/internal/codeintel/api/exists.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/exists.go rename to internal/codeintel/api/exists.go diff --git a/cmd/precise-code-intel-api-server/internal/api/exists_test.go b/internal/codeintel/api/exists_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/exists_test.go rename to internal/codeintel/api/exists_test.go diff --git a/cmd/precise-code-intel-api-server/internal/api/helpers_test.go b/internal/codeintel/api/helpers_test.go similarity index 99% rename from cmd/precise-code-intel-api-server/internal/api/helpers_test.go rename to internal/codeintel/api/helpers_test.go index ffa9f779c7c6ae..121578d1169d97 100644 --- a/cmd/precise-code-intel-api-server/internal/api/helpers_test.go +++ b/internal/codeintel/api/helpers_test.go @@ -271,7 +271,7 @@ func setMockBundleClientPackageInformation(t *testing.T, mockBundleClient *bundl } func readTestFilter(t *testing.T, dirname, filename string) []byte { - content, err := ioutil.ReadFile(fmt.Sprintf("../../testdata/filters/%s/%s", dirname, filename)) + content, err := ioutil.ReadFile(fmt.Sprintf("./testdata/filters/%s/%s", dirname, filename)) if err != nil { t.Fatalf("unexpected error reading: %s", err) } diff --git a/cmd/precise-code-intel-api-server/internal/api/hover.go b/internal/codeintel/api/hover.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/hover.go rename to internal/codeintel/api/hover.go diff --git a/cmd/precise-code-intel-api-server/internal/api/hover_test.go b/internal/codeintel/api/hover_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/hover_test.go rename to internal/codeintel/api/hover_test.go diff --git a/cmd/precise-code-intel-api-server/internal/api/locations.go b/internal/codeintel/api/locations.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/locations.go rename to internal/codeintel/api/locations.go diff --git a/cmd/precise-code-intel-api-server/internal/api/monikers.go b/internal/codeintel/api/monikers.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/monikers.go rename to internal/codeintel/api/monikers.go diff --git a/cmd/precise-code-intel-api-server/internal/api/monikers_test.go b/internal/codeintel/api/monikers_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/monikers_test.go rename to internal/codeintel/api/monikers_test.go diff --git a/cmd/precise-code-intel-api-server/internal/api/observability.go b/internal/codeintel/api/observability.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/observability.go rename to internal/codeintel/api/observability.go diff --git a/cmd/precise-code-intel-api-server/internal/api/references.go b/internal/codeintel/api/references.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/references.go rename to internal/codeintel/api/references.go diff --git a/cmd/precise-code-intel-api-server/internal/api/references_test.go b/internal/codeintel/api/references_test.go similarity index 100% rename from cmd/precise-code-intel-api-server/internal/api/references_test.go rename to internal/codeintel/api/references_test.go diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/1 b/internal/codeintel/api/testdata/filters/normal/1 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/1 rename to internal/codeintel/api/testdata/filters/normal/1 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/10 b/internal/codeintel/api/testdata/filters/normal/10 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/10 rename to internal/codeintel/api/testdata/filters/normal/10 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/11 b/internal/codeintel/api/testdata/filters/normal/11 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/11 rename to internal/codeintel/api/testdata/filters/normal/11 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/12 b/internal/codeintel/api/testdata/filters/normal/12 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/12 rename to internal/codeintel/api/testdata/filters/normal/12 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/2 b/internal/codeintel/api/testdata/filters/normal/2 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/2 rename to internal/codeintel/api/testdata/filters/normal/2 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/3 b/internal/codeintel/api/testdata/filters/normal/3 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/3 rename to internal/codeintel/api/testdata/filters/normal/3 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/4 b/internal/codeintel/api/testdata/filters/normal/4 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/4 rename to internal/codeintel/api/testdata/filters/normal/4 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/5 b/internal/codeintel/api/testdata/filters/normal/5 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/5 rename to internal/codeintel/api/testdata/filters/normal/5 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/6 b/internal/codeintel/api/testdata/filters/normal/6 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/6 rename to internal/codeintel/api/testdata/filters/normal/6 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/7 b/internal/codeintel/api/testdata/filters/normal/7 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/7 rename to internal/codeintel/api/testdata/filters/normal/7 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/8 b/internal/codeintel/api/testdata/filters/normal/8 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/8 rename to internal/codeintel/api/testdata/filters/normal/8 diff --git a/cmd/precise-code-intel-api-server/testdata/filters/normal/9 b/internal/codeintel/api/testdata/filters/normal/9 similarity index 100% rename from cmd/precise-code-intel-api-server/testdata/filters/normal/9 rename to internal/codeintel/api/testdata/filters/normal/9 diff --git a/internal/codeintel/lsifserver/client/client.go b/internal/codeintel/lsifserver/client/client.go index cbf51ad4da7700..2701533e56d6ab 100644 --- a/internal/codeintel/lsifserver/client/client.go +++ b/internal/codeintel/lsifserver/client/client.go @@ -6,6 +6,9 @@ import ( "strings" "sync" + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" + bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" + "github.com/sourcegraph/sourcegraph/internal/codeintel/db" "github.com/sourcegraph/sourcegraph/internal/endpoint" "github.com/sourcegraph/sourcegraph/internal/env" "github.com/sourcegraph/sourcegraph/internal/trace/ot" @@ -16,19 +19,27 @@ var ( preciseCodeIntelAPIServerURLsOnce sync.Once preciseCodeIntelAPIServerURLs *endpoint.Map +) + +type Client struct { + endpoint *endpoint.Map + HTTPClient *http.Client + server *Server +} - DefaultClient = &Client{ +func New( + db db.DB, + bundleManagerClient bundles.BundleManagerClient, + codeIntelAPI api.CodeIntelAPI, +) *Client { + return &Client{ endpoint: LSIFURLs(), HTTPClient: &http.Client{ // ot.Transport will propagate opentracing spans Transport: &ot.Transport{}, }, + server: NewServer(db, bundleManagerClient, codeIntelAPI), } -) - -type Client struct { - endpoint *endpoint.Map - HTTPClient *http.Client } func LSIFURLs() *endpoint.Map { diff --git a/internal/codeintel/lsifserver/client/handler.go b/internal/codeintel/lsifserver/client/handler.go new file mode 100644 index 00000000000000..8ff4cd8ddd1091 --- /dev/null +++ b/internal/codeintel/lsifserver/client/handler.go @@ -0,0 +1,216 @@ +package client + +import ( + "fmt" + "net/http" + + "github.com/inconshreveable/log15" + "github.com/pkg/errors" + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" + "github.com/sourcegraph/sourcegraph/internal/codeintel/gitserver" +) + +const DefaultUploadPageSize = 50 +const DefaultReferencesPageSize = 100 + +// GET /uploads/{id:[0-9]+} +func (s *Server) handleGetUploadByID(w http.ResponseWriter, r *http.Request) { + upload, exists, err := s.db.GetUploadByID(r.Context(), int(idFromRequest(r))) + if err != nil { + log15.Error("Failed to retrieve upload", "error", err) + http.Error(w, fmt.Sprintf("failed to retrieve upload: %s", err.Error()), http.StatusInternalServerError) + return + } + if !exists { + http.Error(w, "upload not found", http.StatusNotFound) + return + } + + writeJSON(w, upload) +} + +// DELETE /uploads/{id:[0-9]+} +func (s *Server) handleDeleteUploadByID(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + exists, err := s.db.DeleteUploadByID(ctx, int(idFromRequest(r)), func(repositoryID int) (string, error) { + tipCommit, err := gitserver.Head(ctx, s.db, repositoryID) + if err != nil { + return "", errors.Wrap(err, "gitserver.Head") + } + return tipCommit, nil + }) + if err != nil { + log15.Error("Failed to delete upload", "error", err) + http.Error(w, fmt.Sprintf("failed to delete upload: %s", err.Error()), http.StatusInternalServerError) + return + } + if !exists { + http.Error(w, "upload not found", http.StatusNotFound) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +// GET /uploads/repository/{id:[0-9]+} +func (s *Server) handleGetUploadsByRepo(w http.ResponseWriter, r *http.Request) { + id := int(idFromRequest(r)) + limit := getQueryIntDefault(r, "limit", DefaultUploadPageSize) + offset := getQueryInt(r, "offset") + + uploads, totalCount, err := s.db.GetUploadsByRepo( + r.Context(), + id, + getQuery(r, "state"), + getQuery(r, "query"), + getQueryBool(r, "visibleAtTip"), + limit, + offset, + ) + if err != nil { + log15.Error("Failed to list uploads", "error", err) + http.Error(w, fmt.Sprintf("failed to list uploads: %s", err.Error()), http.StatusInternalServerError) + return + } + + if offset+len(uploads) < totalCount { + w.Header().Set("Link", makeNextLink(r.URL, map[string]interface{}{ + "limit": limit, + "offset": offset + len(uploads), + })) + } + + writeJSON(w, map[string]interface{}{"uploads": uploads, "totalCount": totalCount}) +} + +// GET /exists +func (s *Server) handleExists(w http.ResponseWriter, r *http.Request) { + dumps, err := s.codeIntelAPI.FindClosestDumps( + r.Context(), + getQueryInt(r, "repositoryId"), + getQuery(r, "commit"), + getQuery(r, "path"), + ) + if err != nil { + log15.Error("Failed to handle exists request", "error", err) + http.Error(w, fmt.Sprintf("failed to handle exists request: %s", err.Error()), http.StatusInternalServerError) + return + } + + writeJSON(w, map[string]interface{}{"uploads": dumps}) +} + +// GET /definitions +func (s *Server) handleDefinitions(w http.ResponseWriter, r *http.Request) { + defs, err := s.codeIntelAPI.Definitions( + r.Context(), + getQuery(r, "path"), + getQueryInt(r, "line"), + getQueryInt(r, "character"), + getQueryInt(r, "uploadId"), + ) + if err != nil { + if err == api.ErrMissingDump { + http.Error(w, "no such dump", http.StatusNotFound) + return + } + + log15.Error("Failed to handle definitions request", "error", err) + http.Error(w, fmt.Sprintf("failed to handle definitions request: %s", err.Error()), http.StatusInternalServerError) + return + } + + outers, err := serializeLocations(defs) + if err != nil { + log15.Error("Failed to resolve locations", "error", err) + http.Error(w, fmt.Sprintf("failed to resolve locations: %s", err.Error()), http.StatusInternalServerError) + return + } + + writeJSON(w, map[string]interface{}{"locations": outers}) +} + +// GET /references +func (s *Server) handleReferences(w http.ResponseWriter, r *http.Request) { + cursor, err := api.DecodeOrCreateCursor( + getQuery(r, "path"), + getQueryInt(r, "line"), + getQueryInt(r, "character"), + getQueryInt(r, "uploadId"), + getQuery(r, "cursor"), + s.db, + s.bundleManagerClient, + ) + if err != nil { + if err == api.ErrMissingDump { + http.Error(w, "no such dump", http.StatusNotFound) + return + } + + log15.Error("Failed to prepare cursor", "error", err) + http.Error(w, fmt.Sprintf("failed to prepare cursor: %s", err.Error()), http.StatusInternalServerError) + return + } + + limit := getQueryIntDefault(r, "limit", DefaultReferencesPageSize) + if limit <= 0 { + http.Error(w, "illegal limit", http.StatusBadRequest) + return + } + + locations, newCursor, hasNewCursor, err := s.codeIntelAPI.References( + r.Context(), + getQueryInt(r, "repositoryId"), + getQuery(r, "commit"), + limit, + cursor, + ) + if err != nil { + log15.Error("Failed to handle references request", "error", err) + http.Error(w, fmt.Sprintf("failed to handle references request: %s", err.Error()), http.StatusInternalServerError) + return + } + + outers, err := serializeLocations(locations) + if err != nil { + log15.Error("Failed to resolve locations", "error", err) + http.Error(w, fmt.Sprintf("failed to resolve locations: %s", err.Error()), http.StatusInternalServerError) + return + } + + if hasNewCursor { + w.Header().Set("Link", makeNextLink(r.URL, map[string]interface{}{ + "cursor": api.EncodeCursor(newCursor), + })) + } + + writeJSON(w, map[string]interface{}{"locations": outers}) +} + +// GET /hover +func (s *Server) handleHover(w http.ResponseWriter, r *http.Request) { + text, rn, exists, err := s.codeIntelAPI.Hover( + r.Context(), + getQuery(r, "path"), + getQueryInt(r, "line"), + getQueryInt(r, "character"), + getQueryInt(r, "uploadId"), + ) + if err != nil { + if err == api.ErrMissingDump { + http.Error(w, "no such dump", http.StatusNotFound) + return + } + + log15.Error("Failed to handle hover request", "error", err) + http.Error(w, fmt.Sprintf("failed to handle hover request: %s", err.Error()), http.StatusInternalServerError) + return + } + + if !exists { + writeJSON(w, nil) + } else { + writeJSON(w, map[string]interface{}{"text": text, "range": rn}) + } +} diff --git a/internal/codeintel/lsifserver/client/locations.go b/internal/codeintel/lsifserver/client/locations.go new file mode 100644 index 00000000000000..1b26cb89d4b1ac --- /dev/null +++ b/internal/codeintel/lsifserver/client/locations.go @@ -0,0 +1,27 @@ +package client + +import ( + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" + bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" +) + +type APILocation struct { + RepositoryID int `json:"repositoryId"` + Commit string `json:"commit"` + Path string `json:"path"` + Range bundles.Range `json:"range"` +} + +func serializeLocations(resolvedLocations []api.ResolvedLocation) ([]APILocation, error) { + var apiLocations []APILocation + for _, res := range resolvedLocations { + apiLocations = append(apiLocations, APILocation{ + RepositoryID: res.Dump.RepositoryID, + Commit: res.Dump.Commit, + Path: res.Path, + Range: res.Range, + }) + } + + return apiLocations, nil +} diff --git a/internal/codeintel/lsifserver/client/proxy.go b/internal/codeintel/lsifserver/client/proxy.go index 109a101c96e2a2..51d4c64c796efe 100644 --- a/internal/codeintel/lsifserver/client/proxy.go +++ b/internal/codeintel/lsifserver/client/proxy.go @@ -1,10 +1,15 @@ package client import ( + "bytes" "context" "fmt" + "io" + "io/ioutil" "net/http" + "net/http/httptest" + "github.com/gorilla/mux" "github.com/opentracing-contrib/go-stdlib/nethttp" "github.com/opentracing/opentracing-go/ext" "github.com/pkg/errors" @@ -12,6 +17,36 @@ import ( ) func (c *Client) RawRequest(ctx context.Context, req *http.Request) (_ *http.Response, err error) { + router := mux.NewRouter() + router.Path("/uploads/{id:[0-9]+}").Methods("GET").HandlerFunc(c.server.handleGetUploadByID) + router.Path("/uploads/{id:[0-9]+}").Methods("DELETE").HandlerFunc(c.server.handleDeleteUploadByID) + router.Path("/uploads/repository/{id:[0-9]+}").Methods("GET").HandlerFunc(c.server.handleGetUploadsByRepo) + router.Path("/exists").Methods("GET").HandlerFunc(c.server.handleExists) + router.Path("/definitions").Methods("GET").HandlerFunc(c.server.handleDefinitions) + router.Path("/references").Methods("GET").HandlerFunc(c.server.handleReferences) + router.Path("/hover").Methods("GET").HandlerFunc(c.server.handleHover) + router.Path("/upload").Methods("POST").HandlerFunc(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + resp, err := c.rawRequest(ctx, req) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + return + } + + w.WriteHeader(resp.StatusCode) + _, _ = io.Copy(w, resp.Body) + })) + + rec := httptest.NewRecorder() + router.ServeHTTP(rec, req) + return &http.Response{ + StatusCode: rec.Code, + Header: rec.Header(), + Body: ioutil.NopCloser(bytes.NewReader(rec.Body.Bytes())), + }, nil +} + +func (c *Client) rawRequest(ctx context.Context, req *http.Request) (_ *http.Response, err error) { span, ctx := ot.StartSpanFromContext(ctx, "lsifserver.client.do") defer func() { if err != nil { diff --git a/internal/codeintel/lsifserver/client/server.go b/internal/codeintel/lsifserver/client/server.go new file mode 100644 index 00000000000000..a0ba3b69ac5149 --- /dev/null +++ b/internal/codeintel/lsifserver/client/server.go @@ -0,0 +1,25 @@ +package client + +import ( + "github.com/sourcegraph/sourcegraph/internal/codeintel/api" + bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client" + "github.com/sourcegraph/sourcegraph/internal/codeintel/db" +) + +type Server struct { + db db.DB + bundleManagerClient bundles.BundleManagerClient + codeIntelAPI api.CodeIntelAPI +} + +func NewServer( + db db.DB, + bundleManagerClient bundles.BundleManagerClient, + codeIntelAPI api.CodeIntelAPI, +) *Server { + return &Server{ + db: db, + bundleManagerClient: bundleManagerClient, + codeIntelAPI: codeIntelAPI, + } +} diff --git a/internal/codeintel/lsifserver/client/util.go b/internal/codeintel/lsifserver/client/util.go new file mode 100644 index 00000000000000..d92e88b078b642 --- /dev/null +++ b/internal/codeintel/lsifserver/client/util.go @@ -0,0 +1,94 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/gorilla/mux" + "github.com/inconshreveable/log15" + "github.com/tomnomnom/linkheader" +) + +func hasQuery(r *http.Request, name string) bool { + return r.URL.Query().Get(name) != "" +} + +func getQuery(r *http.Request, name string) string { + return r.URL.Query().Get(name) +} + +func getQueryInt(r *http.Request, name string) int { + value, _ := strconv.Atoi(r.URL.Query().Get(name)) + return value +} + +func getQueryIntDefault(r *http.Request, name string, defaultValue int) int { + value, err := strconv.Atoi(r.URL.Query().Get(name)) + if err != nil { + value = defaultValue + } + return value +} + +func getQueryBool(r *http.Request, name string) bool { + value, _ := strconv.ParseBool(r.URL.Query().Get(name)) + return value +} + +func makeNextLink(url *url.URL, newQueryValues map[string]interface{}) string { + q := url.Query() + for k, v := range newQueryValues { + q.Set(k, fmt.Sprintf("%v", v)) + } + url.RawQuery = q.Encode() + + header := linkheader.Link{ + URL: url.String(), + Rel: "next", + } + return header.String() +} + +// idFromRequest returns the database id from the request URL's path. This method +// must only be called from routes containing the `id:[0-9]+` pattern, as the error +// return from ParseInt is not checked. +func idFromRequest(r *http.Request) int64 { + id, _ := strconv.ParseInt(mux.Vars(r)["id"], 10, 64) + return id +} + +// copyAll writes the contents of r to w and logs on write failure. +func copyAll(w http.ResponseWriter, r io.Reader) { + if _, err := io.Copy(w, r); err != nil { + log15.Error("Failed to write payload to client", "error", err) + } +} + +// writeJSON writes the JSON-encoded payload to w and logs on write failure. +// If there is an encoding error, then a 500-level status is written to w. +func writeJSON(w http.ResponseWriter, payload interface{}) { + data, err := json.Marshal(payload) + if err != nil { + log15.Error("Failed to serialize result", "error", err) + http.Error(w, fmt.Sprintf("failed to serialize result: %s", err.Error()), http.StatusInternalServerError) + return + } + + copyAll(w, bytes.NewReader(data)) +} + +func sanitizeRoot(s string) string { + if s == "" || s == "/" { + return "" + } + if !strings.HasSuffix(s, "/") { + s += "/" + } + return s +}