-
Notifications
You must be signed in to change notification settings - Fork 6
/
actions.go
147 lines (134 loc) · 4.24 KB
/
actions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package restli
import (
"context"
"log"
"net/http"
"github.com/PapaCharlie/go-restli/restlicodec"
"github.com/PapaCharlie/go-restli/restlidata"
)
// DoActionRequest executes a rest.li Action request and places the given restlicodec.Marshaler in the request's body
// and discards the response body. Actions with no params are expected to use the EmptyRecord.
func DoActionRequest(
c *Client,
ctx context.Context,
rp ResourcePath,
query QueryParamsEncoder,
params restlicodec.Marshaler,
) (err error) {
req, err := newActionRequest(c, ctx, rp, query, params)
if err != nil {
return err
}
_, err = DoAndIgnore(c, req)
return err
}
var actionRequiredResponseFields = restlicodec.RequiredFields{restlidata.ValueField}
// DoActionRequestWithResults executes a rest.li Action request and places the given restlicodec.Marshaler in the
// request's body, and returns the results after deserialization. Actions with no params are expected to use the
// EmptyRecord.
func DoActionRequestWithResults[T any](
c *Client,
ctx context.Context,
rp ResourcePath,
query QueryParamsEncoder,
params restlicodec.Marshaler,
unmarshaler restlicodec.GenericUnmarshaler[T],
) (t T, err error) {
req, err := newActionRequest(c, ctx, rp, query, params)
if err != nil {
return t, err
}
t, _, err = DoAndUnmarshal(c, req, func(reader restlicodec.Reader) (t T, err error) {
err = reader.ReadRecord(actionRequiredResponseFields, func(reader restlicodec.Reader, field string) (err error) {
switch field {
case restlidata.ValueField:
t, err = unmarshaler(reader)
return err
default:
return reader.Skip()
}
})
return t, err
})
return t, err
}
func newActionRequest(
c *Client,
ctx context.Context,
rp ResourcePath,
query QueryParamsEncoder,
params restlicodec.Marshaler,
) (*http.Request, error) {
return NewJsonRequest(c, ctx, rp, query, http.MethodPost, Method_action, params, nil)
}
func RegisterAction[RP ResourcePathUnmarshaler[RP], P any](
s Server,
segments []ResourcePathSegment,
name string,
action func(*RequestContext, RP, P) error,
) {
registerAction[RP, P, struct{}](s, segments, name, nil,
func(ctx *RequestContext, rp RP, params P) (struct{}, error) {
return struct{}{}, action(ctx, rp, params)
})
}
func RegisterActionWithResults[RP ResourcePathUnmarshaler[RP], P, R any](
s Server,
segments []ResourcePathSegment,
name string,
resultsMarshaler restlicodec.GenericMarshaler[R],
action func(*RequestContext, RP, P) (R, error),
) {
registerAction(s, segments, name, resultsMarshaler,
func(ctx *RequestContext, rp RP, params P) (R, error) {
return action(ctx, rp, params)
})
}
func registerAction[RP ResourcePathUnmarshaler[RP], P, R any](
s Server,
segments []ResourcePathSegment,
name string,
resultsMarshaler restlicodec.GenericMarshaler[R],
h func(ctx *RequestContext, rp RP, params P) (R, error),
) {
p := s.subNode(segments)
if _, ok := p.actions[name]; ok {
log.Panicf("go-restli: Cannot register action %q twice for %v", name, segments)
}
p.actions[name] = func(
ctx *RequestContext,
segments []restlicodec.Reader,
body []byte,
) (responseBody restlicodec.Marshaler, err error) {
rp, err := UnmarshalResourcePath[RP](segments)
if err != nil {
return newErrorResponsef(nil, http.StatusBadRequest, "Invalid path for action %q: %s", name)
}
// Special case for action params that are EmptyRecord: skip reading the body altogether, as it's valid for an
// action with no parameters to supply an empty POST body
var params P
if !restlidata.IsEmptyRecord(params) {
var r restlicodec.Reader
r, err = restlicodec.NewJsonReader(body)
if err == nil {
params, err = restlicodec.UnmarshalRestLi[P](r)
}
if err != nil {
return newErrorResponsef(err, http.StatusBadRequest, "Invalid arguments for action %q: %s", name)
}
}
results, err := h(ctx, rp, params)
if err != nil {
return newErrorResponsef(err, http.StatusBadRequest, "Action %q failed: %s", name)
}
if resultsMarshaler != nil {
return restlicodec.MarshalerFunc(func(writer restlicodec.Writer) error {
return writer.WriteMap(func(keyWriter func(key string) restlicodec.Writer) (err error) {
return resultsMarshaler(results, keyWriter(restlidata.ValueField))
})
}), nil
} else {
return nil, nil
}
}
}