Skip to content

Commit ba67161

Browse files
authored
Merge pull request #34 from mutablelogic/v1
Added a JSON streaming callback
2 parents c538601 + 78c4a59 commit ba67161

File tree

8 files changed

+174
-6
lines changed

8 files changed

+174
-6
lines changed

client.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"encoding/xml"
7+
"errors"
78
"fmt"
89
"io"
910
"mime"
@@ -47,6 +48,10 @@ type Client struct {
4748

4849
type ClientOpt func(*Client) error
4950

51+
// Callback for json stream events, return an error if you want to stop streaming
52+
// with an error and io.EOF if you want to stop streaming and return success
53+
type JsonStreamCallback func(v any) error
54+
5055
///////////////////////////////////////////////////////////////////////////////
5156
// GLOBALS
5257

@@ -302,8 +307,20 @@ func do(client *http.Client, req *http.Request, accept string, strict bool, out
302307
// Decode the body
303308
switch mimetype {
304309
case ContentTypeJson:
305-
if err := json.NewDecoder(response.Body).Decode(out); err != nil {
306-
return err
310+
// JSON decode is streamable
311+
dec := json.NewDecoder(response.Body)
312+
for {
313+
if err := dec.Decode(out); err == io.EOF {
314+
break
315+
} else if err != nil {
316+
return err
317+
} else if reqopts.jsonStreamCallback != nil {
318+
if err := reqopts.jsonStreamCallback(out); errors.Is(err, io.EOF) {
319+
break
320+
} else if err != nil {
321+
return err
322+
}
323+
}
307324
}
308325
case ContentTypeTextStream:
309326
if err := NewTextStream().Decode(response.Body, reqopts.textStreamCallback); err != nil {

cmd/api/auth.go

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
// Packages
8+
tablewriter "github.com/djthorpe/go-tablewriter"
9+
client "github.com/mutablelogic/go-client"
10+
auth "github.com/mutablelogic/go-server/pkg/handler/auth/client"
11+
)
12+
13+
var (
14+
authClient *auth.Client
15+
authName = "tokenauth"
16+
authDuration time.Duration
17+
)
18+
19+
func authRegister(flags *Flags) {
20+
// Register flags
21+
flags.String(authName, "tokenauth-endpoint", "${TOKENAUTH_ENDPOINT}", "tokenauth endpoint (ie, http://host/api/auth/)")
22+
flags.String(authName, "tokenauth-token", "${TOKENAUTH_TOKEN}", "tokenauth token")
23+
flags.Duration(authName, "expiry", 0, "token expiry duration")
24+
25+
// Register commands
26+
flags.Register(Cmd{
27+
Name: authName,
28+
Description: "Manage token authentication",
29+
Parse: authParse,
30+
Fn: []Fn{
31+
// Default caller
32+
{Call: authList, Description: "List authentication tokens"},
33+
{Name: "list", Call: authList, Description: "List authentication tokens"},
34+
{Name: "create", Call: authCreate, Description: "Create a token", MinArgs: 1},
35+
{Name: "delete", Call: authDelete, Description: "Delete a token", MinArgs: 1, MaxArgs: 1},
36+
},
37+
})
38+
}
39+
40+
func authParse(flags *Flags, opts ...client.ClientOpt) error {
41+
endpoint := flags.GetString("tokenauth-endpoint")
42+
if token := flags.GetString("tokenauth-token"); token != "" {
43+
opts = append(opts, client.OptReqToken(client.Token{
44+
Scheme: "Bearer",
45+
Value: token,
46+
}))
47+
}
48+
49+
if duration := flags.GetString("expiry"); duration != "" {
50+
if d, err := time.ParseDuration(duration); err != nil {
51+
return err
52+
} else {
53+
authDuration = d
54+
}
55+
}
56+
57+
if client, err := auth.New(endpoint, opts...); err != nil {
58+
return err
59+
} else {
60+
authClient = client
61+
}
62+
return nil
63+
}
64+
65+
func authList(_ context.Context, w *tablewriter.Writer, _ []string) error {
66+
tokens, err := authClient.List()
67+
if err != nil {
68+
return err
69+
}
70+
return w.Write(tokens)
71+
}
72+
73+
func authCreate(_ context.Context, w *tablewriter.Writer, params []string) error {
74+
name := params[0]
75+
scopes := params[1:]
76+
token, err := authClient.Create(name, authDuration, scopes...)
77+
if err != nil {
78+
return err
79+
}
80+
return w.Write(token)
81+
}
82+
83+
func authDelete(ctx context.Context, w *tablewriter.Writer, params []string) error {
84+
name := params[0]
85+
err := authClient.Delete(name)
86+
if err != nil {
87+
return err
88+
}
89+
return authList(ctx, w, nil)
90+
}

cmd/api/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func main() {
2020

2121
// Register commands
2222
anthropicRegister(flags)
23+
authRegister(flags)
2324
bwRegister(flags)
2425
elRegister(flags)
2526
haRegister(flags)

cmd/api/nginx.go_old

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package main
2+
3+
import (
4+
"context"
5+
6+
// Packages
7+
tablewriter "github.com/djthorpe/go-tablewriter"
8+
client "github.com/mutablelogic/go-client"
9+
nginx "github.com/mutablelogic/go-server/pkg/handler/nginx/client"
10+
)
11+
12+
var (
13+
nginxClient *nginx.Client
14+
nginxName = "nginx"
15+
nginxEndpoint string
16+
)
17+
18+
func nginxRegister(flags *Flags) {
19+
flags.Register(Cmd{
20+
Name: nginxName,
21+
Description: "Manage nginx instances",
22+
Parse: nginxParse,
23+
Fn: []Fn{
24+
// Default caller
25+
{Call: nginxGetVersion, Description: "Get the nginx version that is running"},
26+
},
27+
})
28+
}
29+
30+
func nginxParse(flags *Flags, opts ...client.ClientOpt) error {
31+
// Register flags
32+
flags.String(nginxName, "nginx-endpoint", "${NGINX_ENDPOINT}", "nginx endpoint")
33+
34+
if client, err := nginx.New(nginxEndpoint, opts...); err != nil {
35+
return err
36+
} else {
37+
nginxClient = client
38+
}
39+
return nil
40+
}
41+
42+
func nginxGetVersion(_ context.Context, w *tablewriter.Writer, _ []string) error {
43+
version, _, err := nginxClient.Health()
44+
if err != nil {
45+
return err
46+
}
47+
return w.Write(version)
48+
}

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ toolchain go1.22.3
77
require (
88
github.com/andreburgaud/crypt2go v1.5.0
99
github.com/djthorpe/go-errors v1.0.3
10-
github.com/djthorpe/go-tablewriter v0.0.6
10+
github.com/djthorpe/go-tablewriter v0.0.7
1111
github.com/go-audio/audio v1.0.0
1212
github.com/go-audio/wav v1.1.0
13+
github.com/mutablelogic/go-server v1.4.7
1314
github.com/stretchr/testify v1.9.0
1415
github.com/xdg-go/pbkdf2 v1.0.0
1516
golang.org/x/crypto v0.23.0

go.sum

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/djthorpe/go-errors v1.0.3 h1:GZeMPkC1mx2vteXLI/gvxZS0Ee9zxzwD1mcYyKU5jD0=
66
github.com/djthorpe/go-errors v1.0.3/go.mod h1:HtfrZnMd6HsX75Mtbv9Qcnn0BqOrrFArvCaj3RMnZhY=
7-
github.com/djthorpe/go-tablewriter v0.0.6 h1:iGi1eln0KEknJUH9AtPeTeOUcioilDRs7QruyRcmtWM=
8-
github.com/djthorpe/go-tablewriter v0.0.6/go.mod h1:LL+Dxaepm8Q0qUVD9EB6+d0xr7I7OgQYEfrugI8fBUA=
7+
github.com/djthorpe/go-tablewriter v0.0.7 h1:jnNsJDjjLLCt0OAqB5DzGZN7V3beT1IpNMQ8GcOwZDU=
8+
github.com/djthorpe/go-tablewriter v0.0.7/go.mod h1:NVBvytpL+6fHfCKn0+3lSi15/G3A1HWf2cLNeHg6YBg=
99
github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4=
1010
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
1111
github.com/go-audio/riff v1.0.0 h1:d8iCGbDvox9BfLagY94fBynxSPHO80LmZCaOsmKxokA=
@@ -14,6 +14,8 @@ github.com/go-audio/wav v1.1.0 h1:jQgLtbqBzY7G+BM8fXF7AHUk1uHUviWS4X39d5rsL2g=
1414
github.com/go-audio/wav v1.1.0/go.mod h1:mpe9qfwbScEbkd8uybLuIpTgHyrISw/OTuvjUW2iGtE=
1515
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
1616
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
17+
github.com/mutablelogic/go-server v1.4.7 h1:NpzG30f/D50Xbwr96dA6uiapyr4QHBziSanc/q/LR7k=
18+
github.com/mutablelogic/go-server v1.4.7/go.mod h1:wrrDg863hlv5/DUpSG/Pb4k9LiSYO7VxRgLPiMhrE6M=
1719
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1820
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1921
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=

pkg/multipart/multipart.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func NewMultipartEncoder(w io.Writer) *Encoder {
5555
}
5656
}
5757

58-
// NewFormEncoder creates a new encoder object, whichwrites
58+
// NewFormEncoder creates a new encoder object, which writes
5959
// application/x-www-form-urlencoded to the io.Writer
6060
func NewFormEncoder(w io.Writer) *Encoder {
6161
return &Encoder{

requestopts.go

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type requestOpts struct {
1717
*http.Request
1818
noTimeout bool // OptNoTimeout
1919
textStreamCallback TextStreamCallback // OptTextStreamCallback
20+
jsonStreamCallback JsonStreamCallback // OptJsonStreamCallback
2021
}
2122

2223
type RequestOpt func(*requestOpts) error
@@ -103,3 +104,11 @@ func OptTextStreamCallback(fn TextStreamCallback) RequestOpt {
103104
return nil
104105
}
105106
}
107+
108+
// OptJsonStreamCallback is called for each decoded JSON event
109+
func OptJsonStreamCallback(fn JsonStreamCallback) RequestOpt {
110+
return func(r *requestOpts) error {
111+
r.jsonStreamCallback = fn
112+
return nil
113+
}
114+
}

0 commit comments

Comments
 (0)