fork of https://github.com/sourcegraph/zoekt
0

Configure Feed

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

rest: remove package

The REST package was an experiment to support gerrit ACLs. This
experiment was never concluded successfully, for the following
reasons:

* Gitiles (the Gerrit source browser) does not generate lists of
visible projects, therefore, we can't generate restriction lists.

* The design is potentially O(N^2) in the number of repositories (each
of the N shards has to iterate over N query restrictions.

Fixes #25.

Change-Id: Ic0d324d2a1dd79762555579238045a2ddb98458f

-418
-2
cmd/zoekt-webserver/main.go
··· 108 108 listen := flag.String("listen", ":6070", "listen on this address.") 109 109 index := flag.String("index", build.DefaultDir, "set index directory to use") 110 110 html := flag.Bool("html", true, "enable HTML interface") 111 - restAPI := flag.Bool("rest_api", false, "enable REST API") 112 111 print := flag.Bool("print", false, "enable local result URLs") 113 112 enablePprof := flag.Bool("pprof", false, "set to enable remote profiling.") 114 113 sslCert := flag.String("ssl_cert", "", "set path to SSL .pem holding certificate.") ··· 158 157 159 158 s.Print = *print 160 159 s.HTML = *html 161 - s.RESTAPI = *restAPI 162 160 163 161 if *hostCustomization != "" { 164 162 s.HostCustomQueries = map[string]string{}
-63
rest/api.go
··· 1 - // Copyright 2016 Google Inc. All rights reserved. 2 - // 3 - // Licensed under the Apache License, Version 2.0 (the "License"); 4 - // you may not use this file except in compliance with the License. 5 - // You may obtain a copy of the License at 6 - // 7 - // http://www.apache.org/licenses/LICENSE-2.0 8 - // 9 - // Unless required by applicable law or agreed to in writing, software 10 - // distributed under the License is distributed on an "AS IS" BASIS, 11 - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 - // See the License for the specific language governing permissions and 13 - // limitations under the License. 14 - 15 - package rest 16 - 17 - // SearchRequest is the entry point for the /api/search POST endpoint. 18 - type SearchRequest struct { 19 - Query string 20 - 21 - // A list of OR'd restrictions. 22 - Restrict []SearchRequestRestriction 23 - } 24 - 25 - // A REST search query must provide a restriction. 26 - type SearchRequestRestriction struct { 27 - Repo string 28 - Branches []string 29 - 30 - // TODO - provide way to set number of search results. 31 - } 32 - 33 - // SearchResponse is the return type for /api/search endpoint 34 - type SearchResponse struct { 35 - Files []*SearchResponseFile 36 - Error *string 37 - 38 - // TODO - provide statistics. 39 - } 40 - 41 - // SearchResponseFile holds the matches within a single file. 42 - type SearchResponseFile struct { 43 - Repo string 44 - Branches []string 45 - FileName string 46 - Lines []*SearchResponseLine 47 - } 48 - 49 - // SearchResponseLine holds the matches within a single line. 50 - type SearchResponseLine struct { 51 - LineNumber int 52 - Line string 53 - Matches []*SearchResponseMatch 54 - } 55 - 56 - // SearchResponseMatch is the matching segment of the line. 57 - type SearchResponseMatch struct { 58 - // Start of match, in (unicode) characters. 59 - Start int 60 - 61 - // End of match, in (unicode) characters. 62 - End int 63 - }
-88
rest/rest_test.go
··· 1 - // Copyright 2016 Google Inc. All rights reserved. 2 - // 3 - // Licensed under the Apache License, Version 2.0 (the "License"); 4 - // you may not use this file except in compliance with the License. 5 - // You may obtain a copy of the License at 6 - // 7 - // http://www.apache.org/licenses/LICENSE-2.0 8 - // 9 - // Unless required by applicable law or agreed to in writing, software 10 - // distributed under the License is distributed on an "AS IS" BASIS, 11 - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 - // See the License for the specific language governing permissions and 13 - // limitations under the License. 14 - 15 - package rest 16 - 17 - import ( 18 - "bytes" 19 - "context" 20 - "testing" 21 - 22 - "github.com/google/zoekt" 23 - ) 24 - 25 - type memSeeker struct { 26 - data []byte 27 - } 28 - 29 - func (s *memSeeker) Close() {} 30 - func (s *memSeeker) Read(off, sz uint32) ([]byte, error) { 31 - return s.data[off : off+sz], nil 32 - } 33 - 34 - func (s *memSeeker) Size() (uint32, error) { 35 - return uint32(len(s.data)), nil 36 - } 37 - func (s *memSeeker) Name() string { 38 - return "memSeeker" 39 - } 40 - 41 - func TestUnicodeOffset(t *testing.T) { 42 - repo := zoekt.Repository{ 43 - Name: "name", 44 - Branches: []zoekt.RepositoryBranch{{Name: "master", Version: "master-version"}}, 45 - } 46 - b, err := zoekt.NewIndexBuilder(&repo) 47 - if err != nil { 48 - t.Fatalf("NewIndexBuilder: %v", err) 49 - } 50 - if err := b.Add(zoekt.Document{ 51 - Name: "f2", 52 - Content: []byte("orange\u2318apple"), 53 - // --------------0123456 78901 54 - Branches: []string{"master"}, 55 - }); err != nil { 56 - t.Errorf("Add: %v", err) 57 - } 58 - 59 - var buf bytes.Buffer 60 - b.Write(&buf) 61 - f := &memSeeker{buf.Bytes()} 62 - 63 - searcher, err := zoekt.NewSearcher(f) 64 - if err != nil { 65 - t.Fatalf("NewSearcher: %v", err) 66 - } 67 - 68 - rep, err := serveSearchAPIStructured(context.Background(), searcher, &SearchRequest{ 69 - Query: "orange.*apple", 70 - Restrict: []SearchRequestRestriction{ 71 - { 72 - Repo: "name", 73 - Branches: []string{"master"}, 74 - }}, 75 - }) 76 - 77 - if err != nil { 78 - t.Fatalf("serveSearchAPIStructured: %v", err) 79 - } 80 - 81 - if len(rep.Files) != 1 || len(rep.Files[0].Lines) != 1 || len(rep.Files[0].Lines[0].Matches) != 1 { 82 - t.Fatalf("got %#v, want 1 match", rep) 83 - } 84 - 85 - if end := rep.Files[0].Lines[0].Matches[0].End; end != 12 { 86 - t.Errorf("got end %d, want 12", end) 87 - } 88 - }
-165
rest/serve.go
··· 1 - // Copyright 2016 Google Inc. All rights reserved. 2 - // 3 - // Licensed under the Apache License, Version 2.0 (the "License"); 4 - // you may not use this file except in compliance with the License. 5 - // You may obtain a copy of the License at 6 - // 7 - // http://www.apache.org/licenses/LICENSE-2.0 8 - // 9 - // Unless required by applicable law or agreed to in writing, software 10 - // distributed under the License is distributed on an "AS IS" BASIS, 11 - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 - // See the License for the specific language governing permissions and 13 - // limitations under the License. 14 - 15 - package rest 16 - 17 - import ( 18 - "encoding/json" 19 - "fmt" 20 - "io/ioutil" 21 - "net/http" 22 - 23 - "github.com/google/zoekt" 24 - "github.com/google/zoekt/query" 25 - "golang.org/x/net/context" 26 - ) 27 - 28 - const jsonContentType = "application/json; charset=utf-8" 29 - 30 - type httpError struct { 31 - msg string 32 - status int 33 - } 34 - 35 - func (e *httpError) Error() string { return fmt.Sprintf("%d: %s", e.status, e.msg) } 36 - 37 - func Search(s zoekt.Searcher, w http.ResponseWriter, r *http.Request) { 38 - if err := serveSearchAPIErr(s, w, r); err != nil { 39 - if e, ok := err.(*httpError); ok { 40 - http.Error(w, e.msg, e.status) 41 - } 42 - http.Error(w, err.Error(), http.StatusTeapot) 43 - } 44 - } 45 - 46 - func verifyAPIInput(w http.ResponseWriter, r *http.Request) ([]byte, error) { 47 - if r.Method != http.MethodPost { 48 - return nil, &httpError{"must use POST", http.StatusMethodNotAllowed} 49 - } 50 - 51 - if got := r.Header.Get("Content-Type"); got != jsonContentType { 52 - return nil, &httpError{"must use " + jsonContentType, http.StatusNotAcceptable} 53 - 54 - } 55 - 56 - content, err := ioutil.ReadAll(r.Body) 57 - if err != nil { 58 - return nil, &httpError{err.Error(), http.StatusBadRequest} 59 - } 60 - 61 - return content, nil 62 - } 63 - 64 - func serveSearchAPIErr(s zoekt.Searcher, w http.ResponseWriter, r *http.Request) error { 65 - content, err := verifyAPIInput(w, r) 66 - if err != nil { 67 - return err 68 - } 69 - 70 - var req SearchRequest 71 - if err := json.Unmarshal(content, &req); err != nil { 72 - return &httpError{err.Error(), http.StatusBadRequest} 73 - } 74 - 75 - rep, err := serveSearchAPIStructured(r.Context(), s, &req) 76 - if err != nil { 77 - return err 78 - } 79 - 80 - return dumpAPIOutput(w, rep) 81 - } 82 - 83 - func dumpAPIOutput(w http.ResponseWriter, rep interface{}) error { 84 - content, err := json.Marshal(rep) 85 - if err != nil { 86 - return &httpError{err.Error(), http.StatusInternalServerError} 87 - } 88 - 89 - w.Header().Set("Content-Type", jsonContentType) 90 - if _, err := w.Write(content); err != nil { 91 - return &httpError{err.Error(), http.StatusInternalServerError} 92 - } 93 - return nil 94 - } 95 - 96 - func serveSearchAPIStructured(ctx context.Context, searcher zoekt.Searcher, req *SearchRequest) (*SearchResponse, error) { 97 - q, err := query.Parse(req.Query) 98 - if err != nil { 99 - msg := "parse error: " + err.Error() 100 - return &SearchResponse{Error: &msg}, nil 101 - } 102 - 103 - var restrictions []query.Q 104 - for _, r := range req.Restrict { 105 - var branchQs []query.Q 106 - for _, b := range r.Branches { 107 - branchQs = append(branchQs, &query.Branch{Pattern: b}) 108 - } 109 - 110 - restrictions = append(restrictions, 111 - query.NewAnd(&query.Repo{Pattern: r.Repo}, query.NewOr(branchQs...))) 112 - } 113 - 114 - finalQ := query.Simplify(query.NewAnd(q, query.NewOr(restrictions...))) 115 - var options zoekt.SearchOptions 116 - options.SetDefaults() 117 - 118 - result, err := searcher.Search(ctx, finalQ, &options) 119 - if err != nil { 120 - return nil, &httpError{err.Error(), http.StatusInternalServerError} 121 - } 122 - 123 - // TODO - make this tunable. Use a query param or a JSON struct? 124 - num := 50 125 - if len(result.Files) > num { 126 - result.Files = result.Files[:num] 127 - } 128 - var resp SearchResponse 129 - for _, f := range result.Files { 130 - srf := SearchResponseFile{ 131 - Repo: f.Repository, 132 - Branches: f.Branches, 133 - FileName: f.FileName, 134 - // TODO - set version 135 - } 136 - for _, m := range f.LineMatches { 137 - srl := &SearchResponseLine{ 138 - LineNumber: m.LineNumber, 139 - Line: string(m.Line), 140 - } 141 - 142 - // Convert to unicode indices. 143 - charOffsets := make([]int, len(m.Line), len(m.Line)+1) 144 - j := 0 145 - for i := range srl.Line { 146 - charOffsets[i] = j 147 - j++ 148 - } 149 - charOffsets = append(charOffsets, j) 150 - 151 - for _, fr := range m.LineFragments { 152 - srfr := SearchResponseMatch{ 153 - Start: charOffsets[fr.LineOffset], 154 - End: charOffsets[fr.LineOffset+fr.MatchLength], 155 - } 156 - 157 - srl.Matches = append(srl.Matches, &srfr) 158 - } 159 - srf.Lines = append(srf.Lines, srl) 160 - } 161 - resp.Files = append(resp.Files, &srf) 162 - } 163 - 164 - return &resp, nil 165 - }
-89
web/e2e_test.go
··· 63 63 return searcher 64 64 } 65 65 66 - func TestJSON(t *testing.T) { 67 - b, err := zoekt.NewIndexBuilder(&zoekt.Repository{ 68 - Name: "name", 69 - URL: "repo-url", 70 - CommitURLTemplate: "{{.Version}}", 71 - FileURLTemplate: "url", 72 - LineFragmentTemplate: "line", 73 - Branches: []zoekt.RepositoryBranch{{Name: "master", Version: "1234"}}, 74 - }) 75 - if err != nil { 76 - t.Fatalf("NewIndexBuilder: %v", err) 77 - } 78 - 79 - line := "abc apple orange" 80 - if err := b.Add(zoekt.Document{ 81 - Name: "f2", 82 - Content: []byte(line), 83 - Branches: []string{"master"}, 84 - }); err != nil { 85 - t.Fatalf("Add: %v", err) 86 - } 87 - 88 - s := searcherForTest(t, b) 89 - srv := Server{ 90 - Searcher: s, 91 - Top: Top, 92 - RESTAPI: true, 93 - } 94 - 95 - mux, err := NewMux(&srv) 96 - if err != nil { 97 - t.Fatalf("NewMux: %v", err) 98 - } 99 - 100 - ts := httptest.NewServer(mux) 101 - defer ts.Close() 102 - 103 - req := `{"Query": "apple", "Restrict":[{"Repo": "name", "Branches": ["master"]}]}` 104 - res, err := http.Post(ts.URL+"/api/search", jsonContentType, bytes.NewBufferString(req)) 105 - 106 - if err != nil { 107 - t.Fatal(err) 108 - } 109 - 110 - if got := res.Header.Get("Content-Type"); got != jsonContentType { 111 - t.Errorf("got Content-Type %q, want %q", got, jsonContentType) 112 - } 113 - 114 - resultBytes, err := ioutil.ReadAll(res.Body) 115 - res.Body.Close() 116 - if err != nil { 117 - t.Fatalf("ReadAll: %v", err) 118 - } 119 - 120 - result := string(resultBytes) 121 - for _, want := range []string{`"LineNumber":1`, `"Line":"` + line + `"`} { 122 - if !strings.Contains(result, want) { 123 - t.Errorf("got %s, missing %s", result, want) 124 - } 125 - } 126 - } 127 - 128 - func TestJSONParseError(t *testing.T) { 129 - srv := Server{ 130 - Top: Top, 131 - RESTAPI: true, 132 - } 133 - 134 - mux, err := NewMux(&srv) 135 - if err != nil { 136 - t.Fatalf("NewMux: %v", err) 137 - } 138 - 139 - ts := httptest.NewServer(mux) 140 - defer ts.Close() 141 - 142 - req := `{"Query": "apple\"", "Restrict":[{"Repo": "name", "Branches": ["master"]}]}` 143 - res, err := http.Post(ts.URL+"/api/search", jsonContentType, bytes.NewBufferString(req)) 144 - 145 - if err != nil { 146 - t.Fatalf("POST: %v", err) 147 - } 148 - 149 - body, _ := ioutil.ReadAll(res.Body) 150 - if want := `"Error":"parse error`; !strings.Contains(string(body), want) { 151 - t.Errorf("got %q, want substring %q", body, want) 152 - } 153 - } 154 - 155 66 func TestBasic(t *testing.T) { 156 67 b, err := zoekt.NewIndexBuilder(&zoekt.Repository{ 157 68 Name: "name",
-11
web/server.go
··· 29 29 30 30 "github.com/google/zoekt" 31 31 "github.com/google/zoekt/query" 32 - "github.com/google/zoekt/rest" 33 32 ) 34 33 35 34 var Funcmap = template.FuncMap{ ··· 64 63 // Serve HTML interface 65 64 HTML bool 66 65 67 - // Serve REST API 68 - RESTAPI bool 69 - 70 66 // If set, show files from the index. 71 67 Print bool 72 68 ··· 104 100 lastStatsTS time.Time 105 101 } 106 102 107 - func (s *Server) serveSearchAPI(w http.ResponseWriter, r *http.Request) { 108 - rest.Search(s.Searcher, w, r) 109 - } 110 - 111 103 func (s *Server) getTemplate(str string) *template.Template { 112 104 s.templateMu.Lock() 113 105 defer s.templateMu.Unlock() ··· 154 146 mux.HandleFunc("/search", s.serveSearch) 155 147 mux.HandleFunc("/", s.serveSearchBox) 156 148 mux.HandleFunc("/about", s.serveAbout) 157 - } 158 - if s.RESTAPI { 159 - mux.HandleFunc("/api/search", s.serveSearchAPI) 160 149 } 161 150 if s.Print { 162 151 mux.HandleFunc("/print", s.servePrint)