mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 18:11:48 +00:00
codeintel: Simplify GraphQL resolvers (#10911)
* Remove precise-code-intel-api-server. * Squash commits. * WIP. * Fix unused variable. * Small cleanup.
This commit is contained in:
parent
d41e56e424
commit
b4d12f35f2
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -233,7 +233,6 @@ Dockerfile @sourcegraph/distribution
|
||||
/internal/cmd/precise-code-intel-test @sourcegraph/code-intel
|
||||
/internal/codeintel @sourcegraph/code-intel
|
||||
/enterprise/internal/codeintel @sourcegraph/code-intel
|
||||
/internal/lsif @sourcegraph/code-intel
|
||||
/cmd/frontend/graphqlbackend/codeintel.go @sourcegraph/code-intel
|
||||
|
||||
# Development
|
||||
|
||||
@ -37,7 +37,6 @@ import (
|
||||
bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client"
|
||||
codeinteldb "github.com/sourcegraph/sourcegraph/internal/codeintel/db"
|
||||
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"
|
||||
@ -176,10 +175,13 @@ func initCodeIntel() {
|
||||
db := codeinteldb.NewObserved(codeinteldb.NewWithHandle(dbconn.Global), observationContext)
|
||||
bundleManagerClient := bundles.New(bundleManagerURL)
|
||||
api := codeintelapi.NewObserved(codeintelapi.New(db, bundleManagerClient, codeintelgitserver.DefaultClient), observationContext)
|
||||
client := lsifserverclient.New(db, bundleManagerClient, api)
|
||||
|
||||
graphqlbackend.NewCodeIntelResolver = func() graphqlbackend.CodeIntelResolver {
|
||||
return codeintelResolvers.NewResolver(client)
|
||||
return codeintelResolvers.NewResolver(
|
||||
db,
|
||||
bundleManagerClient,
|
||||
api,
|
||||
)
|
||||
}
|
||||
|
||||
httpapi.NewCodeIntelUploadHandler = func() http.Handler {
|
||||
|
||||
4
enterprise/internal/codeintel/resolvers/consts.go
Normal file
4
enterprise/internal/codeintel/resolvers/consts.go
Normal file
@ -0,0 +1,4 @@
|
||||
package resolvers
|
||||
|
||||
const DefaultUploadPageSize = 50
|
||||
const DefaultReferencesPageSize = 100
|
||||
13
enterprise/internal/codeintel/resolvers/conversion.go
Normal file
13
enterprise/internal/codeintel/resolvers/conversion.go
Normal file
@ -0,0 +1,13 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"github.com/sourcegraph/go-lsp"
|
||||
bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client"
|
||||
)
|
||||
|
||||
func convertRange(r bundles.Range) lsp.Range {
|
||||
return lsp.Range{
|
||||
Start: lsp.Position{Line: r.Start.Line, Character: r.Start.Character},
|
||||
End: lsp.Position{Line: r.End.Line, Character: r.End.Character},
|
||||
}
|
||||
}
|
||||
30
enterprise/internal/codeintel/resolvers/ids.go
Normal file
30
enterprise/internal/codeintel/resolvers/ids.go
Normal file
@ -0,0 +1,30 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
graphql "github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
)
|
||||
|
||||
func marshalLSIFUploadGQLID(lsifUploadID int64) graphql.ID {
|
||||
return relay.MarshalID("LSIFUpload", lsifUploadID)
|
||||
}
|
||||
|
||||
func unmarshalLSIFUploadGQLID(id graphql.ID) (lsifUploadID int64, err error) {
|
||||
// First, try to unmarshal the ID as a string and then convert it to an
|
||||
// integer. This is here to maintain backwards compatibility with the
|
||||
// src-cli lsif upload command, which constructs its own relay identifier
|
||||
// from a the string payload returned by the upload proxy.
|
||||
|
||||
var lsifUploadIDString string
|
||||
err = relay.UnmarshalSpec(id, &lsifUploadIDString)
|
||||
if err == nil {
|
||||
lsifUploadID, err = strconv.ParseInt(lsifUploadIDString, 10, 64)
|
||||
return
|
||||
}
|
||||
|
||||
// If it wasn't unmarshal-able as a string, it's a new-style int identifier
|
||||
err = relay.UnmarshalSpec(id, &lsifUploadID)
|
||||
return
|
||||
}
|
||||
@ -3,18 +3,18 @@ package resolvers
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sourcegraph/go-langserver/pkg/lsp"
|
||||
"github.com/sourcegraph/go-lsp"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/types"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/lsif"
|
||||
codeintelapi "github.com/sourcegraph/sourcegraph/internal/codeintel/api"
|
||||
)
|
||||
|
||||
type locationConnectionResolver struct {
|
||||
repo *types.Repo
|
||||
commit api.CommitID
|
||||
locations []*lsif.LSIFLocation
|
||||
locations []codeintelapi.ResolvedLocation
|
||||
endCursor string
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ func (r *locationConnectionResolver) Nodes(ctx context.Context) ([]graphqlbacken
|
||||
return nil, err
|
||||
}
|
||||
|
||||
treeResolver, err := collectionResolver.resolve(ctx, location.RepositoryID, adjustedCommit, location.Path)
|
||||
treeResolver, err := collectionResolver.resolve(ctx, api.RepoID(location.Dump.RepositoryID), adjustedCommit, location.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -63,21 +63,21 @@ func (r *locationConnectionResolver) PageInfo(ctx context.Context) (*graphqlutil
|
||||
//
|
||||
// A non-nil error means the connection resolver was unable to load the diff between
|
||||
// the requested commit and location's commit.
|
||||
func (r *locationConnectionResolver) adjustLocation(ctx context.Context, location *lsif.LSIFLocation) (string, lsp.Range, error) {
|
||||
if location.RepositoryID != r.repo.ID {
|
||||
return location.Commit, location.Range, nil
|
||||
func (r *locationConnectionResolver) adjustLocation(ctx context.Context, location codeintelapi.ResolvedLocation) (string, lsp.Range, error) {
|
||||
if api.RepoID(location.Dump.RepositoryID) != r.repo.ID {
|
||||
return location.Dump.Commit, convertRange(location.Range), nil
|
||||
}
|
||||
|
||||
adjuster, err := newPositionAdjuster(ctx, r.repo, location.Commit, string(r.commit), location.Path)
|
||||
adjuster, err := newPositionAdjuster(ctx, r.repo, location.Dump.Commit, string(r.commit), location.Path)
|
||||
if err != nil {
|
||||
return "", lsp.Range{}, err
|
||||
}
|
||||
|
||||
if adjustedRange, ok := adjuster.adjustRange(location.Range); ok {
|
||||
if adjustedRange, ok := adjuster.adjustRange(convertRange(location.Range)); ok {
|
||||
return string(r.commit), adjustedRange, nil
|
||||
}
|
||||
|
||||
// Couldn't adjust range, return original result which is precise but
|
||||
// jump the user to another into another commit context on navigation.
|
||||
return location.Commit, location.Range, nil
|
||||
return location.Dump.Commit, convertRange(location.Range), nil
|
||||
}
|
||||
@ -4,23 +4,27 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/sourcegraph/go-lsp"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/lsifserver/client"
|
||||
"github.com/sourcegraph/sourcegraph/internal/lsif"
|
||||
codeintelapi "github.com/sourcegraph/sourcegraph/internal/codeintel/api"
|
||||
bundles "github.com/sourcegraph/sourcegraph/internal/codeintel/bundles/client"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/db"
|
||||
)
|
||||
|
||||
type lsifQueryResolver struct {
|
||||
lsifserverClient *client.Client
|
||||
db db.DB
|
||||
bundleManagerClient bundles.BundleManagerClient
|
||||
codeIntelAPI codeintelapi.CodeIntelAPI
|
||||
|
||||
repositoryResolver *graphqlbackend.RepositoryResolver
|
||||
// commit is the requested target commit
|
||||
commit api.CommitID
|
||||
path string
|
||||
// uploads are ordered by their commit distance from the target commit
|
||||
uploads []*lsif.LSIFUpload
|
||||
uploads []db.Dump
|
||||
}
|
||||
|
||||
var _ graphqlbackend.LSIFQueryResolver = &lsifQueryResolver{}
|
||||
@ -36,23 +40,7 @@ func (r *lsifQueryResolver) Definitions(ctx context.Context, args *graphqlbacken
|
||||
continue
|
||||
}
|
||||
|
||||
opts := &struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
}{
|
||||
RepoID: r.repositoryResolver.Type().ID,
|
||||
Commit: r.commit,
|
||||
Path: r.path,
|
||||
Line: int32(adjustedPosition.Line),
|
||||
Character: int32(adjustedPosition.Character),
|
||||
UploadID: upload.ID,
|
||||
}
|
||||
|
||||
locations, _, err := r.lsifserverClient.Definitions(ctx, opts)
|
||||
locations, err := r.codeIntelAPI.Definitions(ctx, r.path, adjustedPosition.Line, adjustedPosition.Character, upload.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -82,9 +70,9 @@ func (r *lsifQueryResolver) References(ctx context.Context, args *graphqlbackend
|
||||
// We need to maintain a symmetric map for the next page
|
||||
// of results that we can encode into the endCursor of
|
||||
// this request.
|
||||
newCursors := map[int64]string{}
|
||||
newCursors := map[int]string{}
|
||||
|
||||
var allLocations []*lsif.LSIFLocation
|
||||
var allLocations []codeintelapi.ResolvedLocation
|
||||
for _, upload := range r.uploads {
|
||||
adjustedPosition, ok, err := r.adjustPosition(ctx, upload.Commit, args.Line, args.Character)
|
||||
if err != nil {
|
||||
@ -94,28 +82,18 @@ func (r *lsifQueryResolver) References(ctx context.Context, args *graphqlbackend
|
||||
continue
|
||||
}
|
||||
|
||||
opts := &struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}{
|
||||
RepoID: r.repositoryResolver.Type().ID,
|
||||
Commit: r.commit,
|
||||
Path: r.path,
|
||||
Line: int32(adjustedPosition.Line),
|
||||
Character: int32(adjustedPosition.Character),
|
||||
UploadID: upload.ID,
|
||||
}
|
||||
limit := DefaultReferencesPageSize
|
||||
if args.First != nil {
|
||||
opts.Limit = args.First
|
||||
limit = int(*args.First)
|
||||
}
|
||||
if limit <= 0 {
|
||||
// TODO(efritz) - check on defs too
|
||||
return nil, errors.New("illegal limit")
|
||||
}
|
||||
|
||||
rawCursor := ""
|
||||
if nextURL, ok := nextURLs[upload.ID]; ok {
|
||||
opts.Cursor = &nextURL
|
||||
rawCursor = nextURL
|
||||
} else if len(nextURLs) != 0 {
|
||||
// Result set is exhausted or newer than the first page
|
||||
// of results. Skip anything from this upload as it will
|
||||
@ -123,14 +101,31 @@ func (r *lsifQueryResolver) References(ctx context.Context, args *graphqlbackend
|
||||
continue
|
||||
}
|
||||
|
||||
locations, nextURL, err := r.lsifserverClient.References(ctx, opts)
|
||||
cursor, err := codeintelapi.DecodeOrCreateCursor(r.path, adjustedPosition.Line, adjustedPosition.Character, upload.ID, rawCursor, r.db, r.bundleManagerClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
locations, newCursor, hasNewCursor, err := r.codeIntelAPI.References(
|
||||
ctx,
|
||||
int(r.repositoryResolver.Type().ID),
|
||||
string(r.commit),
|
||||
limit,
|
||||
cursor,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cx := ""
|
||||
if hasNewCursor {
|
||||
cx = codeintelapi.EncodeCursor(newCursor)
|
||||
}
|
||||
|
||||
allLocations = append(allLocations, locations...)
|
||||
|
||||
if nextURL != "" {
|
||||
newCursors[upload.ID] = nextURL
|
||||
if cx != "" {
|
||||
newCursors[upload.ID] = cx
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,25 +152,14 @@ func (r *lsifQueryResolver) Hover(ctx context.Context, args *graphqlbackend.LSIF
|
||||
continue
|
||||
}
|
||||
|
||||
text, lspRange, err := r.lsifserverClient.Hover(ctx, &struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
}{
|
||||
RepoID: r.repositoryResolver.Type().ID,
|
||||
Commit: r.commit,
|
||||
Path: r.path,
|
||||
Line: int32(adjustedPosition.Line),
|
||||
Character: int32(adjustedPosition.Character),
|
||||
UploadID: upload.ID,
|
||||
})
|
||||
if err != nil {
|
||||
// TODO(efritz) - codeintelapi should just return an lsp.Hover
|
||||
text, rn, exists, err := r.codeIntelAPI.Hover(ctx, r.path, adjustedPosition.Line, adjustedPosition.Character, int(upload.ID))
|
||||
if err != nil || !exists {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lspRange := convertRange(rn)
|
||||
|
||||
if text != "" {
|
||||
adjustedRange, ok, err := r.adjustRange(ctx, upload.Commit, lspRange)
|
||||
if err != nil {
|
||||
@ -223,7 +207,7 @@ func (r *lsifQueryResolver) adjustRange(ctx context.Context, uploadCommit string
|
||||
|
||||
// readCursor decodes a cursor into a map from upload ids to URLs that
|
||||
// serves the next page of results.
|
||||
func readCursor(after *string) (map[int64]string, error) {
|
||||
func readCursor(after *string) (map[int]string, error) {
|
||||
if after == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@ -233,7 +217,7 @@ func readCursor(after *string) (map[int64]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cursors map[int64]string
|
||||
var cursors map[int]string
|
||||
if err := json.Unmarshal(decoded, &cursors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -243,7 +227,7 @@ func readCursor(after *string) (map[int64]string, error) {
|
||||
// makeCursor encodes a map from upload ids to URLs that serves the next
|
||||
// page of results into a single string that can be sent back for use in
|
||||
// cursor pagination.
|
||||
func makeCursor(cursors map[int64]string) (string, error) {
|
||||
func makeCursor(cursors map[int]string) (string, error) {
|
||||
if len(cursors) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@ -5,21 +5,28 @@ import (
|
||||
"encoding/base64"
|
||||
|
||||
graphql "github.com/graph-gophers/graphql-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/lsifserver/client"
|
||||
codeintelapi "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"
|
||||
)
|
||||
|
||||
type Resolver struct {
|
||||
lsifserverClient *client.Client
|
||||
db db.DB
|
||||
bundleManagerClient bundles.BundleManagerClient
|
||||
codeIntelAPI codeintelapi.CodeIntelAPI
|
||||
}
|
||||
|
||||
var _ graphqlbackend.CodeIntelResolver = &Resolver{}
|
||||
|
||||
func NewResolver(lsifserverClient *client.Client) graphqlbackend.CodeIntelResolver {
|
||||
func NewResolver(db db.DB, bundleManagerClient bundles.BundleManagerClient, codeIntelAPI codeintelapi.CodeIntelAPI) graphqlbackend.CodeIntelResolver {
|
||||
return &Resolver{
|
||||
lsifserverClient: lsifserverClient,
|
||||
db: db,
|
||||
bundleManagerClient: bundleManagerClient,
|
||||
codeIntelAPI: codeIntelAPI,
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,16 +36,12 @@ func (r *Resolver) LSIFUploadByID(ctx context.Context, id graphql.ID) (graphqlba
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lsifUpload, err := r.lsifserverClient.GetUpload(ctx, &struct {
|
||||
UploadID int64
|
||||
}{
|
||||
UploadID: uploadID,
|
||||
})
|
||||
if err != nil {
|
||||
upload, exists, err := r.db.GetUploadByID(ctx, int(uploadID))
|
||||
if err != nil || !exists {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lsifUploadResolver{lsifUpload: lsifUpload}, nil
|
||||
return &lsifUploadResolver{lsifUpload: upload}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) DeleteLSIFUpload(ctx context.Context, id graphql.ID) (*graphqlbackend.EmptyResponse, error) {
|
||||
@ -52,10 +55,12 @@ func (r *Resolver) DeleteLSIFUpload(ctx context.Context, id graphql.ID) (*graphq
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = r.lsifserverClient.DeleteUpload(ctx, &struct {
|
||||
UploadID int64
|
||||
}{
|
||||
UploadID: uploadID,
|
||||
_, err = r.db.DeleteUploadByID(ctx, int(uploadID), func(repositoryID int) (string, error) {
|
||||
tipCommit, err := gitserver.Head(ctx, r.db, repositoryID)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "gitserver.Head")
|
||||
}
|
||||
return tipCommit, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -91,33 +96,25 @@ func (r *Resolver) LSIFUploads(ctx context.Context, args *graphqlbackend.LSIFRep
|
||||
opt.NextURL = &nextURL
|
||||
}
|
||||
|
||||
return &lsifUploadConnectionResolver{lsifserverClient: r.lsifserverClient, opt: opt}, nil
|
||||
return &lsifUploadConnectionResolver{db: r.db, opt: opt}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) LSIF(ctx context.Context, args *graphqlbackend.LSIFQueryArgs) (graphqlbackend.LSIFQueryResolver, error) {
|
||||
uploads, err := r.lsifserverClient.Exists(ctx, &struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
}{
|
||||
RepoID: args.Repository.Type().ID,
|
||||
Commit: args.Commit,
|
||||
Path: args.Path,
|
||||
})
|
||||
|
||||
dumps, err := r.codeIntelAPI.FindClosestDumps(ctx, int(args.Repository.Type().ID), string(args.Commit), args.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uploads) == 0 {
|
||||
if len(dumps) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &lsifQueryResolver{
|
||||
lsifserverClient: r.lsifserverClient,
|
||||
repositoryResolver: args.Repository,
|
||||
commit: args.Commit,
|
||||
path: args.Path,
|
||||
uploads: uploads,
|
||||
db: r.db,
|
||||
bundleManagerClient: r.bundleManagerClient,
|
||||
codeIntelAPI: r.codeIntelAPI,
|
||||
repositoryResolver: args.Repository,
|
||||
commit: args.Commit,
|
||||
path: args.Path,
|
||||
uploads: dumps,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -2,57 +2,35 @@ package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
graphql "github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/lsifserver/client"
|
||||
"github.com/sourcegraph/sourcegraph/internal/lsif"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/db"
|
||||
)
|
||||
|
||||
type lsifUploadResolver struct {
|
||||
repositoryResolver *graphqlbackend.RepositoryResolver
|
||||
lsifUpload *lsif.LSIFUpload
|
||||
lsifUpload db.Upload
|
||||
}
|
||||
|
||||
var _ graphqlbackend.LSIFUploadResolver = &lsifUploadResolver{}
|
||||
|
||||
func (r *lsifUploadResolver) ID() graphql.ID {
|
||||
return marshalLSIFUploadGQLID(r.lsifUpload.ID)
|
||||
}
|
||||
func (r *lsifUploadResolver) ID() graphql.ID { return marshalLSIFUploadGQLID(int64(r.lsifUpload.ID)) }
|
||||
func (r *lsifUploadResolver) InputCommit() string { return r.lsifUpload.Commit }
|
||||
func (r *lsifUploadResolver) InputRoot() string { return r.lsifUpload.Root }
|
||||
func (r *lsifUploadResolver) InputIndexer() string { return r.lsifUpload.Indexer }
|
||||
func (r *lsifUploadResolver) State() string { return strings.ToUpper(r.lsifUpload.State) }
|
||||
func (r *lsifUploadResolver) IsLatestForRepo() bool { return r.lsifUpload.VisibleAtTip }
|
||||
|
||||
func (r *lsifUploadResolver) ProjectRoot(ctx context.Context) (*graphqlbackend.GitTreeEntryResolver, error) {
|
||||
return resolvePath(ctx, r.lsifUpload.RepositoryID, r.lsifUpload.Commit, r.lsifUpload.Root)
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) InputCommit() string {
|
||||
return r.lsifUpload.Commit
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) InputRoot() string {
|
||||
return r.lsifUpload.Root
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) InputIndexer() string {
|
||||
return r.lsifUpload.Indexer
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) State() string {
|
||||
return strings.ToUpper(r.lsifUpload.State)
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) Failure() graphqlbackend.LSIFUploadFailureReasonResolver {
|
||||
if r.lsifUpload.FailureSummary == nil {
|
||||
func (r *lsifUploadResolver) PlaceInQueue() *int32 {
|
||||
if r.lsifUpload.Rank == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &lsifUploadFailureReasonResolver{r.lsifUpload}
|
||||
v := int32(*r.lsifUpload.Rank)
|
||||
return &v
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) UploadedAt() graphqlbackend.DateTime {
|
||||
@ -67,145 +45,14 @@ func (r *lsifUploadResolver) FinishedAt() *graphqlbackend.DateTime {
|
||||
return graphqlbackend.DateTimeOrNil(r.lsifUpload.FinishedAt)
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) IsLatestForRepo() bool {
|
||||
return r.lsifUpload.VisibleAtTip
|
||||
func (r *lsifUploadResolver) ProjectRoot(ctx context.Context) (*graphqlbackend.GitTreeEntryResolver, error) {
|
||||
return resolvePath(ctx, api.RepoID(r.lsifUpload.RepositoryID), r.lsifUpload.Commit, r.lsifUpload.Root)
|
||||
}
|
||||
|
||||
func (r *lsifUploadResolver) PlaceInQueue() *int32 {
|
||||
return r.lsifUpload.PlaceInQueue
|
||||
}
|
||||
|
||||
type lsifUploadFailureReasonResolver struct {
|
||||
lsifUpload *lsif.LSIFUpload
|
||||
}
|
||||
|
||||
var _ graphqlbackend.LSIFUploadFailureReasonResolver = &lsifUploadFailureReasonResolver{}
|
||||
|
||||
func (r *lsifUploadFailureReasonResolver) Summary() string {
|
||||
func (r *lsifUploadResolver) Failure() graphqlbackend.LSIFUploadFailureReasonResolver {
|
||||
if r.lsifUpload.FailureSummary == nil {
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
return *r.lsifUpload.FailureSummary
|
||||
}
|
||||
|
||||
func (r *lsifUploadFailureReasonResolver) Stacktrace() string {
|
||||
if r.lsifUpload.FailureStacktrace == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *r.lsifUpload.FailureStacktrace
|
||||
}
|
||||
|
||||
type LSIFUploadsListOptions struct {
|
||||
RepositoryID graphql.ID
|
||||
Query *string
|
||||
State *string
|
||||
IsLatestForRepo *bool
|
||||
Limit *int32
|
||||
NextURL *string
|
||||
}
|
||||
|
||||
type lsifUploadConnectionResolver struct {
|
||||
lsifserverClient *client.Client
|
||||
|
||||
opt LSIFUploadsListOptions
|
||||
|
||||
// cache results because they are used by multiple fields
|
||||
once sync.Once
|
||||
uploads []*lsif.LSIFUpload
|
||||
repositoryResolver *graphqlbackend.RepositoryResolver
|
||||
totalCount *int
|
||||
nextURL string
|
||||
err error
|
||||
}
|
||||
|
||||
var _ graphqlbackend.LSIFUploadConnectionResolver = &lsifUploadConnectionResolver{}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) Nodes(ctx context.Context) ([]graphqlbackend.LSIFUploadResolver, error) {
|
||||
uploads, repositoryResolver, _, _, err := r.compute(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var l []graphqlbackend.LSIFUploadResolver
|
||||
for _, lsifUpload := range uploads {
|
||||
l = append(l, &lsifUploadResolver{
|
||||
repositoryResolver: repositoryResolver,
|
||||
lsifUpload: lsifUpload,
|
||||
})
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) TotalCount(ctx context.Context) (*int32, error) {
|
||||
_, _, count, _, err := r.compute(ctx)
|
||||
if count == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := int32(*count)
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) PageInfo(ctx context.Context) (*graphqlutil.PageInfo, error) {
|
||||
_, _, _, nextURL, err := r.compute(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if nextURL != "" {
|
||||
return graphqlutil.NextPageCursor(base64.StdEncoding.EncodeToString([]byte(nextURL))), nil
|
||||
}
|
||||
|
||||
return graphqlutil.HasNextPage(false), nil
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) compute(ctx context.Context) ([]*lsif.LSIFUpload, *graphqlbackend.RepositoryResolver, *int, string, error) {
|
||||
r.once.Do(func() {
|
||||
r.repositoryResolver, r.err = graphqlbackend.RepositoryByID(ctx, r.opt.RepositoryID)
|
||||
if r.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.uploads, r.nextURL, r.totalCount, r.err = r.lsifserverClient.GetUploads(ctx, &struct {
|
||||
RepoID api.RepoID
|
||||
Query *string
|
||||
State *string
|
||||
IsLatestForRepo *bool
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}{
|
||||
RepoID: r.repositoryResolver.Type().ID,
|
||||
Query: r.opt.Query,
|
||||
State: r.opt.State,
|
||||
IsLatestForRepo: r.opt.IsLatestForRepo,
|
||||
Limit: r.opt.Limit,
|
||||
Cursor: r.opt.NextURL,
|
||||
})
|
||||
})
|
||||
|
||||
return r.uploads, r.repositoryResolver, r.totalCount, r.nextURL, r.err
|
||||
}
|
||||
|
||||
func marshalLSIFUploadGQLID(lsifUploadID int64) graphql.ID {
|
||||
return relay.MarshalID("LSIFUpload", lsifUploadID)
|
||||
}
|
||||
|
||||
func unmarshalLSIFUploadGQLID(id graphql.ID) (lsifUploadID int64, err error) {
|
||||
// First, try to unmarshal the ID as a string and then convert it to an
|
||||
// integer. This is here to maintain backwards compatibility with the
|
||||
// src-cli lsif upload command, which constructs its own relay identifier
|
||||
// from a the string payload returned by the upload proxy.
|
||||
|
||||
var lsifUploadIDString string
|
||||
err = relay.UnmarshalSpec(id, &lsifUploadIDString)
|
||||
if err == nil {
|
||||
lsifUploadID, err = strconv.ParseInt(lsifUploadIDString, 10, 64)
|
||||
return
|
||||
}
|
||||
|
||||
// If it wasn't unmarshal-able as a string, it's a new-style int identifier
|
||||
err = relay.UnmarshalSpec(id, &lsifUploadID)
|
||||
return
|
||||
return &lsifUploadFailureReasonResolver{r.lsifUpload}
|
||||
}
|
||||
|
||||
137
enterprise/internal/codeintel/resolvers/upload_connection.go
Normal file
137
enterprise/internal/codeintel/resolvers/upload_connection.go
Normal file
@ -0,0 +1,137 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
graphql "github.com/graph-gophers/graphql-go"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/db"
|
||||
)
|
||||
|
||||
type lsifUploadConnectionResolver struct {
|
||||
db db.DB
|
||||
|
||||
opt LSIFUploadsListOptions
|
||||
|
||||
// cache results because they are used by multiple fields
|
||||
once sync.Once
|
||||
uploads []db.Upload
|
||||
repositoryResolver *graphqlbackend.RepositoryResolver
|
||||
totalCount *int
|
||||
nextURL string
|
||||
err error
|
||||
}
|
||||
|
||||
var _ graphqlbackend.LSIFUploadConnectionResolver = &lsifUploadConnectionResolver{}
|
||||
|
||||
type LSIFUploadsListOptions struct {
|
||||
RepositoryID graphql.ID
|
||||
Query *string
|
||||
State *string
|
||||
IsLatestForRepo *bool
|
||||
Limit *int32
|
||||
NextURL *string
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) Nodes(ctx context.Context) ([]graphqlbackend.LSIFUploadResolver, error) {
|
||||
uploads, repositoryResolver, _, _, err := r.compute(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var l []graphqlbackend.LSIFUploadResolver
|
||||
for _, lsifUpload := range uploads {
|
||||
l = append(l, &lsifUploadResolver{
|
||||
repositoryResolver: repositoryResolver,
|
||||
lsifUpload: lsifUpload,
|
||||
})
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) TotalCount(ctx context.Context) (*int32, error) {
|
||||
_, _, count, _, err := r.compute(ctx)
|
||||
if count == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := int32(*count)
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) PageInfo(ctx context.Context) (*graphqlutil.PageInfo, error) {
|
||||
_, _, _, nextURL, err := r.compute(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if nextURL != "" {
|
||||
return graphqlutil.NextPageCursor(base64.StdEncoding.EncodeToString([]byte(nextURL))), nil
|
||||
}
|
||||
|
||||
return graphqlutil.HasNextPage(false), nil
|
||||
}
|
||||
|
||||
func (r *lsifUploadConnectionResolver) compute(ctx context.Context) ([]db.Upload, *graphqlbackend.RepositoryResolver, *int, string, error) {
|
||||
r.once.Do(func() {
|
||||
r.repositoryResolver, r.err = graphqlbackend.RepositoryByID(ctx, r.opt.RepositoryID)
|
||||
if r.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
id := int(r.repositoryResolver.Type().ID)
|
||||
query := ""
|
||||
if r.opt.Query != nil {
|
||||
query = *r.opt.Query
|
||||
}
|
||||
visibileAtTip := r.opt.IsLatestForRepo != nil && *r.opt.IsLatestForRepo
|
||||
|
||||
state := ""
|
||||
if r.opt.State != nil {
|
||||
state = strings.ToLower(*r.opt.State)
|
||||
}
|
||||
|
||||
limit := DefaultUploadPageSize
|
||||
if r.opt.Limit != nil {
|
||||
limit = int(*r.opt.Limit)
|
||||
}
|
||||
|
||||
offset := 0
|
||||
if r.opt.NextURL != nil {
|
||||
offset, _ = strconv.Atoi(*r.opt.NextURL)
|
||||
}
|
||||
|
||||
uploads, totalCount, err := r.db.GetUploadsByRepo(
|
||||
ctx,
|
||||
id,
|
||||
state,
|
||||
query,
|
||||
visibileAtTip,
|
||||
limit,
|
||||
offset,
|
||||
)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return
|
||||
}
|
||||
|
||||
cursor := ""
|
||||
if offset+len(uploads) < totalCount {
|
||||
cursor = fmt.Sprintf("%d", offset+len(uploads))
|
||||
}
|
||||
|
||||
us := uploads
|
||||
|
||||
r.uploads = us
|
||||
r.nextURL = cursor
|
||||
r.totalCount = &totalCount
|
||||
})
|
||||
|
||||
return r.uploads, r.repositoryResolver, r.totalCount, r.nextURL, r.err
|
||||
}
|
||||
28
enterprise/internal/codeintel/resolvers/upload_failure.go
Normal file
28
enterprise/internal/codeintel/resolvers/upload_failure.go
Normal file
@ -0,0 +1,28 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"
|
||||
"github.com/sourcegraph/sourcegraph/internal/codeintel/db"
|
||||
)
|
||||
|
||||
type lsifUploadFailureReasonResolver struct {
|
||||
lsifUpload db.Upload
|
||||
}
|
||||
|
||||
var _ graphqlbackend.LSIFUploadFailureReasonResolver = &lsifUploadFailureReasonResolver{}
|
||||
|
||||
func (r *lsifUploadFailureReasonResolver) Summary() string {
|
||||
return dereferenceString(r.lsifUpload.FailureSummary)
|
||||
}
|
||||
|
||||
func (r *lsifUploadFailureReasonResolver) Stacktrace() string {
|
||||
return dereferenceString(r.lsifUpload.FailureStacktrace)
|
||||
}
|
||||
|
||||
func dereferenceString(s *string) string {
|
||||
if s != nil {
|
||||
return *s
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@ -36,7 +36,7 @@ type codeIntelAPI struct {
|
||||
|
||||
var _ CodeIntelAPI = &codeIntelAPI{}
|
||||
|
||||
var ErrMissingDump = errors.New("no dump")
|
||||
var ErrMissingDump = errors.New("missing dump")
|
||||
|
||||
func New(db db.DB, bundleManagerClient bundles.BundleManagerClient, gitserverClient gitserver.Client) CodeIntelAPI {
|
||||
return &codeIntelAPI{
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"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"
|
||||
)
|
||||
|
||||
var (
|
||||
preciseCodeIntelAPIServerURL = env.Get("PRECISE_CODE_INTEL_API_SERVER_URL", "k8s+http://precise-code-intel:3186", "precise-code-intel-api-server URL (or space separated list of precise-code-intel-api-server URLs)")
|
||||
|
||||
preciseCodeIntelAPIServerURLsOnce sync.Once
|
||||
preciseCodeIntelAPIServerURLs *endpoint.Map
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
endpoint *endpoint.Map
|
||||
HTTPClient *http.Client
|
||||
server *Server
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
func LSIFURLs() *endpoint.Map {
|
||||
preciseCodeIntelAPIServerURLsOnce.Do(func() {
|
||||
if len(strings.Fields(preciseCodeIntelAPIServerURL)) == 0 {
|
||||
preciseCodeIntelAPIServerURLs = endpoint.Empty(errors.New("an precise-code-intel-api-server has not been configured"))
|
||||
} else {
|
||||
preciseCodeIntelAPIServerURLs = endpoint.New(preciseCodeIntelAPIServerURL)
|
||||
}
|
||||
})
|
||||
return preciseCodeIntelAPIServerURLs
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type lsifError struct {
|
||||
StatusCode int
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *lsifError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func IsNotFound(err error) bool {
|
||||
if e, ok := errors.Cause(err).(*lsifError); ok {
|
||||
return e.StatusCode == http.StatusNotFound
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@ -1,216 +0,0 @@
|
||||
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})
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
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
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
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"
|
||||
"github.com/sourcegraph/sourcegraph/internal/trace/ot"
|
||||
)
|
||||
|
||||
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 {
|
||||
ext.Error.Set(span, true)
|
||||
span.SetTag("err", err.Error())
|
||||
}
|
||||
span.Finish()
|
||||
}()
|
||||
|
||||
req, ht := nethttp.TraceRequest(
|
||||
span.Tracer(),
|
||||
req.WithContext(ctx),
|
||||
nethttp.OperationName("LSIF client"),
|
||||
nethttp.ClientTrace(false),
|
||||
)
|
||||
defer ht.Finish()
|
||||
|
||||
// Do not use ctxhttp.Do here as it will re-wrap the request
|
||||
// with a context and this will causes the ot-headers not to
|
||||
// propagate correctly.
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
return nil, errors.Wrap(err, "lsif request failed")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func SelectRandomHost() (string, error) {
|
||||
endpoints, err := LSIFURLs().Endpoints()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for host := range endpoints {
|
||||
return host, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no endpoints available")
|
||||
}
|
||||
@ -1,174 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/sourcegraph/go-lsp"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/lsif"
|
||||
)
|
||||
|
||||
func (c *Client) Exists(ctx context.Context, args *struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
}) ([]*lsif.LSIFUpload, error) {
|
||||
query := queryValues{}
|
||||
query.SetInt("repositoryId", int64(args.RepoID))
|
||||
query.Set("commit", string(args.Commit))
|
||||
query.Set("path", args.Path)
|
||||
|
||||
req := &lsifRequest{
|
||||
path: "/exists",
|
||||
query: query,
|
||||
routingKey: fmt.Sprintf("%d:%s", args.RepoID, args.Commit),
|
||||
}
|
||||
|
||||
payload := struct {
|
||||
Uploads []*lsif.LSIFUpload `json:"uploads"`
|
||||
}{}
|
||||
|
||||
_, err := c.do(ctx, req, &payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload.Uploads, nil
|
||||
}
|
||||
|
||||
func (c *Client) Definitions(ctx context.Context, args *struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
}) ([]*lsif.LSIFLocation, string, error) {
|
||||
return c.locationQuery(ctx, &struct {
|
||||
Operation string
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}{
|
||||
Operation: "definitions",
|
||||
RepoID: args.RepoID,
|
||||
Commit: args.Commit,
|
||||
Path: args.Path,
|
||||
Line: args.Line,
|
||||
Character: args.Character,
|
||||
UploadID: args.UploadID,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) References(ctx context.Context, args *struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}) ([]*lsif.LSIFLocation, string, error) {
|
||||
return c.locationQuery(ctx, &struct {
|
||||
Operation string
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}{
|
||||
Operation: "references",
|
||||
RepoID: args.RepoID,
|
||||
Commit: args.Commit,
|
||||
Path: args.Path,
|
||||
Line: args.Line,
|
||||
Character: args.Character,
|
||||
UploadID: args.UploadID,
|
||||
Limit: args.Limit,
|
||||
Cursor: args.Cursor,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) locationQuery(ctx context.Context, args *struct {
|
||||
Operation string
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}) ([]*lsif.LSIFLocation, string, error) {
|
||||
query := queryValues{}
|
||||
query.SetInt("repositoryId", int64(args.RepoID))
|
||||
query.Set("commit", string(args.Commit))
|
||||
query.Set("path", args.Path)
|
||||
query.SetInt("line", int64(args.Line))
|
||||
query.SetInt("character", int64(args.Character))
|
||||
query.SetInt("uploadId", int64(args.UploadID))
|
||||
query.SetOptionalInt32("limit", args.Limit)
|
||||
|
||||
req := &lsifRequest{
|
||||
path: fmt.Sprintf("/%s", args.Operation),
|
||||
cursor: args.Cursor,
|
||||
query: query,
|
||||
routingKey: fmt.Sprintf("%d:%s", args.RepoID, args.Commit),
|
||||
}
|
||||
|
||||
payload := struct {
|
||||
Locations []*lsif.LSIFLocation
|
||||
}{}
|
||||
|
||||
meta, err := c.do(ctx, req, &payload)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return payload.Locations, meta.nextURL, nil
|
||||
}
|
||||
|
||||
func (c *Client) Hover(ctx context.Context, args *struct {
|
||||
RepoID api.RepoID
|
||||
Commit api.CommitID
|
||||
Path string
|
||||
Line int32
|
||||
Character int32
|
||||
UploadID int64
|
||||
}) (string, lsp.Range, error) {
|
||||
query := queryValues{}
|
||||
query.SetInt("repositoryId", int64(args.RepoID))
|
||||
query.Set("commit", string(args.Commit))
|
||||
query.Set("path", args.Path)
|
||||
query.SetInt("line", int64(args.Line))
|
||||
query.SetInt("character", int64(args.Character))
|
||||
query.SetInt("uploadId", int64(args.UploadID))
|
||||
|
||||
req := &lsifRequest{
|
||||
path: "/hover",
|
||||
query: query,
|
||||
routingKey: fmt.Sprintf("%d:%s", args.RepoID, args.Commit),
|
||||
}
|
||||
|
||||
payload := struct {
|
||||
Text string `json:"text"`
|
||||
Range lsp.Range `json:"range"`
|
||||
}{}
|
||||
|
||||
_, err := c.do(ctx, req, &payload)
|
||||
if err != nil {
|
||||
return "", lsp.Range{}, err
|
||||
}
|
||||
|
||||
return payload.Text, payload.Range, nil
|
||||
}
|
||||
@ -1,154 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sourcegraph/sourcegraph/internal/linkheader"
|
||||
)
|
||||
|
||||
type lsifRequest struct {
|
||||
method string
|
||||
path string
|
||||
cursor *string
|
||||
query queryValues
|
||||
body io.ReadCloser
|
||||
|
||||
// (Optional) used in routing to select the "hot" precise-code-intel-api-server that
|
||||
// was used in recent requests for similar data. Requests that are likely to open the
|
||||
// same dump should hit the same server so that the SQLite database is already in the
|
||||
// cache.
|
||||
routingKey string
|
||||
}
|
||||
|
||||
type lsifResponseMeta struct {
|
||||
statusCode int
|
||||
nextURL string
|
||||
}
|
||||
|
||||
// do will make a request to LSIF API server. This method will return an error if the
|
||||
// request cannot be made or the status code is 400 or 500-level. If a non-nil payload
|
||||
// is given, the request body will be unmarshalled into it.
|
||||
func (c *Client) do(ctx context.Context, lsifRequest *lsifRequest, payload interface{}) (*lsifResponseMeta, error) {
|
||||
method := lsifRequest.method
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
serverURL, err := c.endpoint.Get(lsifRequest.routingKey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url, err := buildURL(serverURL, lsifRequest.path, lsifRequest.cursor, lsifRequest.query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, lsifRequest.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.RawRequest(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, errors.WithStack(&lsifError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(content),
|
||||
})
|
||||
}
|
||||
|
||||
if payload != nil {
|
||||
if err := json.Unmarshal(content, &payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nextURL, _ := linkheader.ExtractNextURL(resp)
|
||||
|
||||
return &lsifResponseMeta{
|
||||
statusCode: resp.StatusCode,
|
||||
nextURL: nextURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildURL constructs a URL to the backend LSIF api server with the given path and
|
||||
// query values. If the provided cursor is non-nil, that will be used instead of the
|
||||
// given path. If path is relative (indicated by a leading slash), then the configured
|
||||
// LSIF API server url is prepended. Otherwise, it is treated as an absolute URL. The
|
||||
// given query values will override any query string that is present in the given path.
|
||||
//
|
||||
// This method can be used to construct a LSIF request URL either from a root
|
||||
// relative path on the first request of a paginated endpoint or from the URL
|
||||
// provided by the Link header in a previous response.
|
||||
func buildURL(baseURL, path string, cursor *string, query queryValues) (string, error) {
|
||||
if cursor != nil {
|
||||
path = *cursor
|
||||
}
|
||||
|
||||
build := url.Parse
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
build = func(path string) (*url.URL, error) {
|
||||
return url.Parse(baseURL + path)
|
||||
}
|
||||
}
|
||||
|
||||
u, err := build(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
for key, values := range query {
|
||||
q.Set(key, values[0])
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
type queryValues url.Values
|
||||
|
||||
func (qv queryValues) Set(name string, value string) {
|
||||
qv[name] = []string{value}
|
||||
}
|
||||
|
||||
func (qv queryValues) SetInt(name string, value int64) {
|
||||
qv.Set(name, strconv.FormatInt(int64(value), 10))
|
||||
}
|
||||
|
||||
func (qv queryValues) SetOptionalString(name string, value *string) {
|
||||
if value != nil {
|
||||
qv.Set(name, *value)
|
||||
}
|
||||
}
|
||||
|
||||
func (qv queryValues) SetOptionalInt32(name string, value *int32) {
|
||||
if value != nil {
|
||||
qv.SetInt(name, int64(*value))
|
||||
}
|
||||
}
|
||||
|
||||
func (qv queryValues) SetOptionalBool(name string, value *bool) {
|
||||
if value != nil {
|
||||
qv.Set(name, fmt.Sprintf("%v", *value))
|
||||
}
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
"github.com/sourcegraph/sourcegraph/internal/lsif"
|
||||
)
|
||||
|
||||
func (c *Client) GetUploads(ctx context.Context, args *struct {
|
||||
RepoID api.RepoID
|
||||
Query *string
|
||||
State *string
|
||||
IsLatestForRepo *bool
|
||||
Limit *int32
|
||||
Cursor *string
|
||||
}) ([]*lsif.LSIFUpload, string, *int, error) {
|
||||
query := queryValues{}
|
||||
query.SetOptionalString("query", args.Query)
|
||||
query.SetOptionalBool("visibleAtTip", args.IsLatestForRepo)
|
||||
query.SetOptionalInt32("limit", args.Limit)
|
||||
|
||||
if args.State != nil {
|
||||
query.Set("state", strings.ToLower(*args.State))
|
||||
}
|
||||
|
||||
req := &lsifRequest{
|
||||
path: fmt.Sprintf("/uploads/repository/%d", args.RepoID),
|
||||
cursor: args.Cursor,
|
||||
query: query,
|
||||
}
|
||||
|
||||
payload := struct {
|
||||
Uploads []*lsif.LSIFUpload `json:"uploads"`
|
||||
TotalCount *int `json:"totalCount"`
|
||||
}{
|
||||
Uploads: []*lsif.LSIFUpload{},
|
||||
}
|
||||
|
||||
meta, err := c.do(ctx, req, &payload)
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
|
||||
return payload.Uploads, meta.nextURL, payload.TotalCount, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetUpload(ctx context.Context, args *struct {
|
||||
UploadID int64
|
||||
}) (*lsif.LSIFUpload, error) {
|
||||
req := &lsifRequest{
|
||||
path: fmt.Sprintf("/uploads/%d", args.UploadID),
|
||||
}
|
||||
|
||||
payload := &lsif.LSIFUpload{}
|
||||
_, err := c.do(ctx, req, &payload)
|
||||
return payload, err
|
||||
}
|
||||
|
||||
func (c *Client) DeleteUpload(ctx context.Context, args *struct {
|
||||
UploadID int64
|
||||
}) error {
|
||||
req := &lsifRequest{
|
||||
path: fmt.Sprintf("/uploads/%d", args.UploadID),
|
||||
method: "DELETE",
|
||||
}
|
||||
|
||||
if _, err := c.do(ctx, req, nil); !IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
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
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package lsif
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sourcegraph/go-lsp"
|
||||
"github.com/sourcegraph/sourcegraph/internal/api"
|
||||
)
|
||||
|
||||
type LSIFUpload struct {
|
||||
ID int64 `json:"id"`
|
||||
RepositoryID api.RepoID `json:"repositoryId"`
|
||||
Commit string `json:"commit"`
|
||||
Root string `json:"root"`
|
||||
Indexer string `json:"indexer"`
|
||||
Filename string `json:"filename"`
|
||||
State string `json:"state"`
|
||||
UploadedAt time.Time `json:"uploadedAt"`
|
||||
StartedAt *time.Time `json:"startedAt"`
|
||||
FinishedAt *time.Time `json:"finishedAt"`
|
||||
FailureSummary *string `json:"failureSummary"`
|
||||
FailureStacktrace *string `json:"failureStacktrace"`
|
||||
VisibleAtTip bool `json:"visibleAtTip"`
|
||||
PlaceInQueue *int32 `json:"placeInQueue"`
|
||||
}
|
||||
|
||||
type LSIFLocation struct {
|
||||
RepositoryID api.RepoID `json:"repositoryId"`
|
||||
Commit string `json:"commit"`
|
||||
Path string `json:"path"`
|
||||
Range lsp.Range `json:"range"`
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user