Skip to content

Commit a989fce

Browse files
committed
WIP - AsHTTP
1 parent 0455705 commit a989fce

36 files changed

+4311
-333
lines changed

codegen/example/example_client.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ func exampleCLIMain(_ string, root *expr.RootExpr, svr *expr.ServerExpr) *codege
7070
FuncMap: map[string]any{
7171
"join": strings.Join,
7272
"toUpper": strings.ToUpper,
73-
"hasJSONRPC": hasJSONRPCServices(root, svr),
74-
"hasHTTP": hasHTTPServices(root, svr),
73+
"hasJSONRPC": func() bool { return hasJSONRPCServices(root, svr) },
74+
"hasHTTP": func() bool { return hasHTTPServices(root, svr) },
7575
},
7676
}, {
7777
Name: "cli-main-end",
@@ -86,8 +86,8 @@ func exampleCLIMain(_ string, root *expr.RootExpr, svr *expr.ServerExpr) *codege
8686
FuncMap: map[string]any{
8787
"toUpper": strings.ToUpper,
8888
"join": strings.Join,
89-
"hasJSONRPC": hasJSONRPCServices(root, svr),
90-
"hasHTTP": hasHTTPServices(root, svr),
89+
"hasJSONRPC": func() bool { return hasJSONRPCServices(root, svr) },
90+
"hasHTTP": func() bool { return hasHTTPServices(root, svr) },
9191
},
9292
},
9393
}
@@ -97,10 +97,8 @@ func exampleCLIMain(_ string, root *expr.RootExpr, svr *expr.ServerExpr) *codege
9797
// hasJSONRPCServices checks if the given server has any JSON-RPC services.
9898
func hasJSONRPCServices(root *expr.RootExpr, svr *expr.ServerExpr) bool {
9999
for _, svcName := range svr.Services {
100-
if httpSvc := root.API.HTTP.Service(svcName); httpSvc != nil {
101-
if httpSvc.HasJSONRPC {
102-
return true
103-
}
100+
if root.API.JSONRPC.Service(svcName) != nil {
101+
return true
104102
}
105103
}
106104
return false

codegen/example/example_server.go

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,15 @@ func filterJSONRPCOnlyServices(root *expr.RootExpr, services []*service.Data) []
189189
var res []*service.Data
190190
for _, svc := range services {
191191
// Check if this service has JSON-RPC
192-
if httpSvc := root.API.HTTP.Service(svc.Name); httpSvc != nil {
193-
if httpSvc.HasJSONRPC {
194-
hasRegularHTTP := false
195-
for _, ep := range httpSvc.HTTPEndpoints {
196-
if !ep.IsJSONRPC {
197-
hasRegularHTTP = true
198-
break
199-
}
200-
}
201-
if !hasRegularHTTP {
202-
res = append(res, svc)
192+
if jsonrpcSvc := root.API.JSONRPC.Service(svc.Name); jsonrpcSvc != nil {
193+
// Check if it also has HTTP endpoints
194+
if httpSvc := root.API.HTTP.Service(svc.Name); httpSvc != nil {
195+
// If it has HTTP endpoints, skip it (we want JSON-RPC only)
196+
if len(httpSvc.HTTPEndpoints) > 0 {
197+
continue
203198
}
204199
}
200+
res = append(res, svc)
205201
}
206202
}
207203
return res
@@ -210,10 +206,8 @@ func filterJSONRPCOnlyServices(root *expr.RootExpr, services []*service.Data) []
210206
// hasAnyJSONRPCService returns true if any service has JSON-RPC endpoints
211207
func hasAnyJSONRPCService(root *expr.RootExpr, services []*service.Data) bool {
212208
for _, svc := range services {
213-
if httpSvc := root.API.HTTP.Service(svc.Name); httpSvc != nil {
214-
if httpSvc.HasJSONRPC {
215-
return true
216-
}
209+
if root.API.JSONRPC.Service(svc.Name) != nil {
210+
return true
217211
}
218212
}
219213
return false

codegen/example/jsonrpc_server_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ func TestJSONRPCServerGeneration(t *testing.T) {
2525
})
2626
})
2727
dsl.Service("testsvc", func() {
28+
dsl.JSONRPC(func() {
29+
dsl.POST("/jsonrpc")
30+
})
2831
dsl.Method("testmethod", func() {
29-
dsl.Payload(dsl.Int)
30-
dsl.Result(dsl.Int)
31-
dsl.HTTP(func() {
32-
dsl.POST("/test")
32+
dsl.Payload(func() {
33+
dsl.Attribute("value", dsl.Int)
34+
dsl.Required("value")
3335
})
34-
dsl.JSONRPC("testsvc.testmethod", func() {
35-
dsl.POST("/jsonrpc")
36+
dsl.Result(dsl.Int)
37+
dsl.JSONRPC(func() {
3638
})
3739
})
3840
})

codegen/example/templates/server_handler.go.tpl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,8 @@
4343
} else if u.Port() == "" {
4444
u.Host = net.JoinHostPort(u.Host, "{{ $u.Port }}")
4545
}
46-
{{- $hasHTTP := len $.HTTPServices }}
47-
{{- $hasJSONRPCOnly := len $.JSONRPCServices }}
4846
{{- if eq $u.Transport.Type "http" }}
49-
{{- if and $hasHTTP (hasAnyJSONRPC) }}
50-
jsonrpcMount := createJSONRPCMountFunction({{ range $.Services }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }})
51-
handle{{ $u.Transport.VarName }}Server(ctx, u, {{ range $.HTTPServices }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }}&wg, errc, *dbgF, jsonrpcMount)
52-
{{- else if $hasHTTP }}
5347
handle{{ $u.Transport.VarName }}Server(ctx, u, {{ range $.HTTPServices }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }}&wg, errc, *dbgF)
54-
{{- else if $hasJSONRPCOnly }}
55-
handleJSONRPCServer(ctx, u{{- range $.JSONRPCServices }}, {{ .VarName }}Endpoints{{- end }}, &wg, errc, *dbgF)
56-
{{- else }}
57-
handle{{ $u.Transport.VarName }}Server(ctx, u, &wg, errc, *dbgF)
58-
{{- end }}
5948
{{- else }}
6049
handle{{ $u.Transport.VarName }}Server(ctx, u, {{ range $.Services }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }}&wg, errc, *dbgF)
6150
{{- end }}

dsl/jsonrpc.go

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ const (
2626

2727
// JSONRPC configures a service or method to use JSON-RPC 2.0 transport.
2828
// The generated code handles JSON-RPC protocol details: request parsing, method dispatch,
29-
// response formatting, and batch processing. All methods share a single HTTP endpoint.
29+
// response formatting, and batch processing. All service JSON-RPC methods share
30+
// a single HTTP endpoint.
3031
//
3132
// At API level, JSONRPC maps global errors to JSON-RPC error codes.
3233
// At service level, it configures the HTTP endpoint and common settings.
@@ -55,84 +56,83 @@ const (
5556
// Example - Complete service with request/notification handling and streaming:
5657
//
5758
// Service("calc", func() {
58-
// Error("div_zero", ErrorResult)
59+
// Error("timeout", ErrTimeout, "Request timed out") // ErrTimeout must have a limit attribute
5960
//
6061
// JSONRPC(func() {
61-
// POST("/rpc") // All methods use this endpoint
62-
// Response("div_zero", RPCInvalidParams) // Map service error to JSON-RPC code
63-
// Headers(func() { // Common request headers
64-
// Attribute("X-API-Version", String)
62+
// POST("/rpc") // All methods use this endpoint
63+
// Response("timeout", func() { // Custom error response
64+
// Code(5001) // Application error code
65+
// Headers(func() { // Error response headers
66+
// Attribute("limit:X-Rate-Limit", String) // Map "limit" attribute to header
67+
// })
68+
// })
69+
// Headers(func() { // Common request headers
70+
// Attribute("version:X-API-Version", String)
6571
// })
6672
// })
6773
//
68-
// Method("add", func() { // Notification method (no ID mapping)
74+
// Method("add", func() { // Notification method (no ID mapping)
6975
// Payload(func() {
70-
// Attribute("a", Int)
71-
// Attribute("b", Int)
76+
// Attribute("a", Int, "First operand")
77+
// Attribute("b", Int, "Second operand")
7278
// Required("a", "b")
7379
// })
7480
// Result(Int)
7581
// })
7682
//
77-
// Method("divide", func() { // Request/response method
83+
// Method("divide", func() { // Request/response method
7884
// Payload(func() {
79-
// Attribute("req_id", String) // Will contain JSON-RPC request ID
80-
// Attribute("dividend", Int)
81-
// Attribute("divisor", Int)
85+
// Attribute("req_id", String, "Request ID") // Will contain JSON-RPC request ID
86+
// Attribute("dividend", Int, "Dividend")
87+
// Attribute("divisor", Int, "Divisor")
8288
// Required("dividend", "divisor")
8389
// })
8490
// Result(Float64)
85-
// Error("div_zero")
91+
// Error("div_zero", ErrorResult, "Division by zero")
8692
//
8793
// JSONRPC(func() {
88-
// ID("req_id") // Map request ID to payload field
89-
// Response("div_zero", func() { // Custom error response
90-
// Code(5001) // Application error code
91-
// Headers(func() { // Error response headers
92-
// Attribute("X-Rate-Limit", String)
93-
// })
94-
// })
94+
// ID("req_id") // Map request ID to payload field
95+
// Response("div_zero", RPCInvalidParams) // Map div_zero error to JSON-RPC code
9596
// })
9697
// })
9798
//
9899
// Method("updates", func() { // SSE streaming method
99100
// Payload(func() {
100-
// Attribute("client_id", String)
101-
// Attribute("last_event_id", String)
101+
// Attribute("req_id", String, "Request ID")
102+
// Attribute("last_event_id", String, "ID of last event received by client")
102103
// })
103104
// StreamingResult(func() {
104-
// Attribute("event_id", String)
105-
// Attribute("data", Any)
105+
// Attribute("event_id", String, "Event ID")
106+
// Attribute("data", Data, "Event data")
106107
// })
107108
//
108109
// JSONRPC(func() {
109-
// ID("client_id")
110-
// ServerSentEvents(func() { // Use SSE instead of WebSocket
111-
// SSERequestID("last_event_id") // Map SSE Last-Event-ID header to payload
112-
// SSEEventID("event_id") // Use result field as SSE event ID
110+
// ID("req_id") // Map JSON-RPC request ID to "req_id" payload attribute
111+
// ServerSentEvents(func() { // Use SSE instead of WebSocket
112+
// SSERequestID("last_event_id") // Map SSE Last-Event-ID header to payload "last_event_id" attribute
113+
// SSEEventID("event_id") // Use "event_id" result attribute as SSE event ID
113114
// })
114115
// })
115116
// })
116117
// })
117118
func JSONRPC(dsl func()) {
118-
var fn func()
119119
switch actual := eval.Current().(type) {
120120
case *expr.APIExpr:
121-
eval.Execute(fn, actual)
121+
eval.Execute(dsl, actual.JSONRPC)
122122
case *expr.ServiceExpr:
123123
svc := expr.Root.API.JSONRPC.ServiceFor(actual)
124124
if existing := svc.DSLFunc; existing != nil {
125-
svc.DSLFunc = func() { existing(); fn() }
125+
svc.DSLFunc = func() { existing(); dsl() }
126126
} else {
127-
svc.DSLFunc = fn
127+
svc.DSLFunc = dsl
128128
}
129129
case *expr.MethodExpr:
130130
svc := expr.Root.API.JSONRPC.ServiceFor(actual.Service)
131131
e := svc.EndpointFor(actual)
132132
if existing := e.DSLFunc; existing != nil {
133-
e.DSLFunc = func() { existing(); fn() }
133+
e.DSLFunc = func() { existing(); dsl() }
134134
} else {
135-
e.DSLFunc = fn
135+
e.DSLFunc = dsl
136136
}
137137
default:
138138
eval.IncompatibleDSL()

0 commit comments

Comments
 (0)