Skip to content

Commit ee7b1fa

Browse files
committed
refactor the rest package introducing middleware concept and the usage of an http router
Signed-off-by: Augustin Husson <husson.augustin@gmail.com>
1 parent bf5821a commit ee7b1fa

6 files changed

Lines changed: 359 additions & 346 deletions

File tree

cmd/promql-langserver/promql-langserver.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
kitlog "github.com/go-kit/kit/log"
2626
"github.com/prometheus-community/promql-langserver/config"
2727
promClient "github.com/prometheus-community/promql-langserver/prometheus"
28+
"github.com/prometheus/common/route"
2829

2930
"github.com/prometheus-community/promql-langserver/langserver"
3031
"github.com/prometheus-community/promql-langserver/rest"
@@ -59,13 +60,17 @@ func main() {
5960
}
6061

6162
logger = kitlog.NewSyncLogger(logger)
62-
63-
handler, err := rest.CreateInstHandler(context.Background(), prometheusClient, logger)
63+
// create the http router
64+
router := route.New()
65+
// create the api
66+
api, err := rest.NewLangServerAPI(context.Background(), prometheusClient, logger, true)
6467
if err != nil {
6568
log.Fatal(err)
6669
}
67-
68-
err = http.ListenAndServe(fmt.Sprint(":", conf.RESTAPIPort), handler)
70+
// register the different route
71+
api.Register(router, "")
72+
// start the http server
73+
err = http.ListenAndServe(fmt.Sprint(":", conf.RESTAPIPort), router)
6974
if err != nil {
7075
log.Fatal(err)
7176
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.13
55
require (
66
github.com/blang/semver v3.5.1+incompatible
77
github.com/go-kit/kit v0.10.0
8+
github.com/google/uuid v1.1.1
89
github.com/kelseyhightower/envconfig v1.4.0
910
github.com/pkg/errors v0.9.1
1011
github.com/prometheus/client_golang v1.7.1

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K
438438
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
439439
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
440440
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
441+
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
441442
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
442443
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
443444
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=

rest/api.go

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
// Copyright 2020 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License. // You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
package rest
13+
14+
import (
15+
"context"
16+
"encoding/json"
17+
"fmt"
18+
"net/http"
19+
20+
"github.com/go-kit/kit/log"
21+
"github.com/pkg/errors"
22+
"github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol"
23+
"github.com/prometheus-community/promql-langserver/langserver"
24+
promClient "github.com/prometheus-community/promql-langserver/prometheus"
25+
"github.com/prometheus/common/route"
26+
)
27+
28+
func returnJSON(w http.ResponseWriter, content interface{}) {
29+
encoder := json.NewEncoder(w)
30+
31+
err := encoder.Encode(content)
32+
if err != nil {
33+
http.Error(w, errors.Wrapf(err, "failed to write response").Error(), 500)
34+
}
35+
}
36+
37+
type lspData struct {
38+
// Expr is the promQL expression and is required for all endpoint available.
39+
Expr string `json:"expr"`
40+
// Limit is the number max of result returned to the client. It will be used for the autocompletion and the diagnostics.
41+
Limit *uint64 `json:"limit,omitempty"`
42+
// PositionLine is the number of the line for which the metadata is queried.
43+
PositionLine *float64 `json:"positionLine,omitempty"`
44+
// PositionChar for which the metadata is queried. Characters are counted as UTF16 Codepoints.
45+
PositionChar *float64 `json:"positionChar,omitempty"`
46+
}
47+
48+
func (d *lspData) UnmarshalJSON(data []byte) error {
49+
var tmp lspData
50+
type plain lspData
51+
if err := json.Unmarshal(data, (*plain)(&tmp)); err != nil {
52+
return err
53+
}
54+
if len(tmp.Expr) == 0 {
55+
return fmt.Errorf("promQL expression is not specified")
56+
}
57+
*d = tmp
58+
return nil
59+
}
60+
61+
func (d *lspData) returnPosition() (protocol.Position, error) {
62+
if d.PositionLine == nil {
63+
return protocol.Position{}, errors.New("positionLine is not specified")
64+
}
65+
if d.PositionChar == nil {
66+
return protocol.Position{}, errors.New("positionChar is not specified")
67+
}
68+
return protocol.Position{
69+
Line: *d.PositionLine,
70+
Character: *d.PositionChar,
71+
}, nil
72+
}
73+
74+
type API struct {
75+
langServer langserver.HeadlessServer
76+
enableMetrics bool
77+
}
78+
79+
func NewLangServerAPI(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger, enableMetrics bool) (*API, error) {
80+
lgs, err := langserver.CreateHeadlessServer(ctx, metadataService, logger)
81+
if err != nil {
82+
return nil, err
83+
}
84+
return &API{
85+
langServer: lgs,
86+
enableMetrics: enableMetrics,
87+
}, nil
88+
}
89+
90+
// Register the API's endpoints in the given router.
91+
func (a *API) Register(r *route.Router, prefix string) {
92+
r.Post(prefix+"/diagnostics", a.handle(a.diagnostics))
93+
r.Post(prefix+"/completion", a.handle(a.completion))
94+
r.Post(prefix+"/hover", a.handle(a.hover))
95+
r.Post(prefix+"/signatureHelp", a.handle(a.signature))
96+
}
97+
98+
func (a *API) handle(h http.HandlerFunc) http.HandlerFunc {
99+
endpoint := manageDocumentMiddleware(a.langServer)(h)
100+
return endpoint
101+
}
102+
103+
func (a *API) diagnostics(w http.ResponseWriter, r *http.Request) {
104+
ctx := r.Context()
105+
requestID, requestData, err := getRequestDataAndID(ctx)
106+
if err != nil {
107+
http.Error(w, err.Error(), 500)
108+
return
109+
}
110+
111+
diagnostics, err := a.langServer.GetDiagnostics(requestID)
112+
if err != nil {
113+
http.Error(w, errors.Wrapf(err, "failed to get diagnostics").Error(), 500)
114+
return
115+
}
116+
117+
items := diagnostics.Diagnostics
118+
limit := requestData.Limit
119+
if limit != nil && uint64(len(items)) > *limit {
120+
items = items[:*limit]
121+
}
122+
123+
returnJSON(w, items)
124+
}
125+
126+
func (a *API) hover(w http.ResponseWriter, r *http.Request) {
127+
ctx := r.Context()
128+
requestID, requestData, err := getRequestDataAndID(ctx)
129+
if err != nil {
130+
http.Error(w, err.Error(), 500)
131+
return
132+
}
133+
position, err := requestData.returnPosition()
134+
if err != nil {
135+
http.Error(w, err.Error(), 400)
136+
return
137+
}
138+
139+
hover, err := a.langServer.Hover(r.Context(), &protocol.HoverParams{
140+
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
141+
TextDocument: protocol.TextDocumentIdentifier{
142+
URI: requestID,
143+
},
144+
Position: position,
145+
},
146+
})
147+
if err != nil {
148+
http.Error(w, errors.Wrapf(err, "failed to get hover info").Error(), 500)
149+
return
150+
}
151+
152+
returnJSON(w, hover)
153+
}
154+
155+
func (a *API) completion(w http.ResponseWriter, r *http.Request) {
156+
ctx := r.Context()
157+
requestID, requestData, err := getRequestDataAndID(ctx)
158+
if err != nil {
159+
http.Error(w, err.Error(), 500)
160+
return
161+
}
162+
position, err := requestData.returnPosition()
163+
if err != nil {
164+
http.Error(w, err.Error(), 400)
165+
return
166+
}
167+
168+
completion, err := a.langServer.Completion(r.Context(), &protocol.CompletionParams{
169+
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
170+
TextDocument: protocol.TextDocumentIdentifier{
171+
URI: requestID,
172+
},
173+
Position: position,
174+
},
175+
})
176+
if err != nil {
177+
http.Error(w, errors.Wrapf(err, "failed to get completion info").Error(), 500)
178+
return
179+
}
180+
181+
items := completion.Items
182+
limit := requestData.Limit
183+
if limit != nil && uint64(len(items)) > *limit {
184+
items = items[:*limit]
185+
}
186+
187+
returnJSON(w, items)
188+
}
189+
190+
func (a *API) signature(w http.ResponseWriter, r *http.Request) {
191+
ctx := r.Context()
192+
requestID, requestData, err := getRequestDataAndID(ctx)
193+
if err != nil {
194+
http.Error(w, err.Error(), 500)
195+
return
196+
}
197+
position, err := requestData.returnPosition()
198+
if err != nil {
199+
http.Error(w, err.Error(), 400)
200+
return
201+
}
202+
203+
signature, err := a.langServer.SignatureHelp(r.Context(), &protocol.SignatureHelpParams{
204+
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
205+
TextDocument: protocol.TextDocumentIdentifier{
206+
URI: requestID,
207+
},
208+
Position: position,
209+
},
210+
})
211+
if err != nil {
212+
http.Error(w, errors.Wrapf(err, "failed to get hover info").Error(), 500)
213+
return
214+
}
215+
216+
returnJSON(w, signature)
217+
}

0 commit comments

Comments
 (0)