Skip to content
This repository was archived by the owner on Jan 16, 2021. It is now read-only.

Commit ac445ad

Browse files
committed
[format] change webhooks format to json
- csv is not future proof, especially if webhook format changes from logs it appears that no one has used 'configure hooks' yet. hence we need not be backward compatible
1 parent f2d808c commit ac445ad

File tree

3 files changed

+205
-354
lines changed

3 files changed

+205
-354
lines changed

configure.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func NewConfigureCmd(e *parsecli.Env) *cobra.Command {
221221
hooksCmd := &cobra.Command{
222222
Use: "hooks [filename]",
223223
Short: "Configure webhooks according to given config file",
224-
Long: `Configure webhooks for the app based on the given configuration csv file.
224+
Long: `Configure webhooks for the app based on the given configuration json file.
225225
For more details read: https://parse.com/docs/js/guide#command-line-webhooks
226226
`,
227227
Run: parsecli.RunWithArgsClient(e, c.hooks.HooksCmd),

webhooks/hooks.go

Lines changed: 52 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package webhooks
22

33
import (
4-
"bufio"
4+
"encoding/json"
55
"errors"
66
"fmt"
77
"io"
@@ -20,11 +20,16 @@ var (
2020
`
2121
invalid format.
2222
valid formats should look like:
23-
[put|post],functionName,https_url
24-
delete,functionName
23+
{"hooks": [OPERATION]}
2524
26-
[put|post],className:triggerName,https_url
27-
delete,className:triggerName
25+
OPERATION ->
26+
{"op": "put", "function": {"functionName": "name", "url": "https_url"}}
27+
{"op": "post", "function": {"functionName": "name", "url": "https_url"}}
28+
{"op": "delete", "function": {"functionName": "name"}}
29+
30+
{"op": "put", "trigger": {"className": "cname", "triggerName": "tname", "url":"https_url"}}
31+
{"op": "post", "trigger": {"className": "cname", "triggerName": "tname", "url":"https_url"}}
32+
{"op": "delete", "trigger": {"className": "cname", "triggerName": "tname",}}
2833
`)
2934

3035
errPostToPut = errors.New(
@@ -41,9 +46,9 @@ delete,className:triggerName
4146
)
4247

4348
type hookOperation struct {
44-
method string
45-
function *functionHook
46-
trigger *triggerHook
49+
Method string `json:"op,omitempty"`
50+
Function *functionHook `json:"function,omitempty"`
51+
Trigger *triggerHook `json:"trigger,omitempty"`
4752
}
4853

4954
func validateURL(urlStr string) error {
@@ -98,162 +103,61 @@ func (h *Hooks) checkTriggerName(s string) error {
98103
)
99104
}
100105

101-
func (h *Hooks) postOrPutHook(
106+
func (h *Hooks) appendHookOperation(
102107
e *parsecli.Env,
108+
hookOp *hookOperation,
103109
hooksOps []*hookOperation,
104-
fields ...string,
105110
) (bool, []*hookOperation, error) {
106-
restOp := strings.ToUpper(fields[0])
107-
if restOp != "POST" && restOp != "PUT" {
108-
return false, nil, stackerr.Wrap(errInvalidFormat)
109-
}
110-
111-
switch len(fields) {
112-
case 3:
113-
hooksOps = append(hooksOps, &hookOperation{
114-
method: restOp,
115-
function: &functionHook{FunctionName: fields[1], URL: fields[2]},
116-
})
117-
return true, hooksOps, nil
118-
119-
case 4:
120-
if err := h.checkTriggerName(fields[2]); err != nil {
121-
return false, nil, err
122-
}
123-
hooksOps = append(hooksOps, &hookOperation{
124-
method: restOp,
125-
trigger: &triggerHook{ClassName: fields[1], TriggerName: fields[2], URL: fields[3]},
126-
})
127-
return true, hooksOps, nil
111+
if hookOp == nil || (hookOp.Function == nil && hookOp.Trigger == nil) ||
112+
(hookOp.Function != nil && hookOp.Trigger != nil) {
113+
return false, hooksOps, nil
128114
}
129-
return false, nil, stackerr.Wrap(errInvalidFormat)
130-
131-
}
132115

133-
func (h *Hooks) deleteHook(
134-
e *parsecli.Env,
135-
hooksOps []*hookOperation,
136-
fields ...string,
137-
) (bool, []*hookOperation, error) {
138-
restOp := strings.ToUpper(fields[0])
139-
if restOp != "DELETE" {
116+
method := strings.ToUpper(hookOp.Method)
117+
if method != "POST" && method != "PUT" && method != "DELETE" {
140118
return false, nil, stackerr.Wrap(errInvalidFormat)
141119
}
142120

143-
switch len(fields) {
144-
case 2:
145-
hooksOps = append(hooksOps, &hookOperation{
146-
method: "DELETE",
147-
function: &functionHook{FunctionName: fields[1]},
148-
})
149-
return true, hooksOps, nil
150-
case 3:
151-
if err := h.checkTriggerName(fields[2]); err != nil {
121+
hookOp.Method = method
122+
if hookOp.Trigger != nil {
123+
if err := h.checkTriggerName(hookOp.Trigger.TriggerName); err != nil {
152124
return false, nil, err
153125
}
154-
hooksOps = append(hooksOps, &hookOperation{
155-
method: "DELETE",
156-
trigger: &triggerHook{ClassName: fields[1], TriggerName: fields[2]},
157-
})
158-
return true, hooksOps, nil
159-
}
160-
161-
return false, nil, stackerr.Wrap(errInvalidFormat)
162-
}
163-
164-
func (h *Hooks) appendHookOperation(
165-
e *parsecli.Env,
166-
fields []string,
167-
hooks []*hookOperation,
168-
) (bool, []*hookOperation, error) {
169-
if len(fields) == 0 {
170-
return false, hooks, nil
171-
}
172-
173-
switch strings.ToLower(fields[0]) {
174-
case "post", "put":
175-
return h.postOrPutHook(e, hooks, fields...)
176-
177-
case "delete":
178-
return h.deleteHook(e, hooks, fields...)
179-
}
180-
return false, nil, stackerr.Wrap(errInvalidFormat)
181-
}
182-
183-
func (h *Hooks) processHooksOperation(e *parsecli.Env, op string) ([]string, error) {
184-
op = strings.TrimSpace(op)
185-
if op == "" {
186-
return nil, nil
187-
}
188-
189-
fields := strings.SplitN(op, ",", 3)
190-
switch restOp := strings.ToLower(fields[0]); restOp {
191-
case "post", "put":
192-
if len(fields) < 3 {
193-
return nil, stackerr.Wrap(errInvalidFormat)
194-
}
195-
case "delete":
196-
if len(fields) != 2 {
197-
return nil, stackerr.Wrap(errInvalidFormat)
198-
}
199-
subFields := strings.SplitN(fields[1], ":", 2)
200-
if len(subFields) == 2 {
201-
fields = append(fields, subFields[1])
202-
fields[1] = subFields[0]
203-
}
204-
return fields, nil
205-
default:
206-
return nil, stackerr.Wrap(errInvalidFormat)
207-
}
208-
209-
if h.baseWebhookURL != nil {
210-
u, err := h.baseWebhookURL.Parse(fields[2])
211-
if err != nil {
212-
return nil, stackerr.Wrap(err)
213-
}
214-
fields[2] = u.String()
215126
}
216127

217-
switch subFields := strings.SplitN(fields[1], ":", 2); len(subFields) {
218-
case 1:
219-
return fields, nil
220-
case 2:
221-
fields = append(fields, fields[2])
222-
fields[3] = fields[2]
223-
fields[2] = subFields[1]
224-
fields[1] = subFields[0]
225-
return fields, nil
226-
}
227-
return nil, stackerr.Wrap(errInvalidFormat)
128+
hooksOps = append(hooksOps, hookOp)
129+
return true, hooksOps, nil
228130
}
229131

230132
func (h *Hooks) createHooksOperations(
231133
e *parsecli.Env,
232134
reader io.Reader,
233135
) ([]*hookOperation, error) {
234-
scanner := bufio.NewScanner(reader)
136+
var input struct {
137+
HooksOps []*hookOperation `json:"hooks,omitempty"`
138+
}
139+
err := json.NewDecoder(ioutil.NopCloser(reader)).Decode(&input)
140+
if err != nil {
141+
return nil, stackerr.Wrap(err)
142+
}
143+
235144
var (
236145
hooksOps []*hookOperation
237146
added bool
238147
)
239-
lineNum := 0
240-
for scanner.Scan() {
241-
lineNum++
242-
fields, err := h.processHooksOperation(e, scanner.Text())
243-
if err != nil {
244-
return nil, err
245-
}
246-
added, hooksOps, err = h.appendHookOperation(e, fields, hooksOps)
148+
for _, hookOp := range input.HooksOps {
149+
added, hooksOps, err = h.appendHookOperation(e, hookOp, hooksOps)
247150
if err != nil {
248151
return nil, err
249152
}
250153
if !added {
251-
fmt.Fprintf(e.Out, "Ignoring line: %d\n", lineNum)
154+
op, err := json.MarshalIndent(hookOp, "", " ")
155+
if err == nil {
156+
fmt.Fprintf(e.Out, "Ignoring hook operation: \n%s\n", op)
157+
}
252158
}
253159
}
254-
if err := scanner.Err(); err != nil {
255-
return nil, stackerr.Wrap(err)
256-
}
160+
257161
return hooksOps, nil
258162
}
259163

@@ -305,20 +209,20 @@ func (h *Hooks) functionHookExists(e *parsecli.Env, name string) (bool, error) {
305209
}
306210

307211
func (h *Hooks) deployFunctionHook(e *parsecli.Env, op *hookOperation) error {
308-
if op.function == nil {
212+
if op.Function == nil {
309213
return stackerr.New("cannot deploy nil function hook")
310214
}
311-
exists, err := h.functionHookExists(e, op.function.FunctionName)
215+
exists, err := h.functionHookExists(e, op.Function.FunctionName)
312216
if err != nil {
313217
return err
314218
}
315219

316-
restOp, suppressed, err := h.checkStrictMode(op.method, exists)
220+
restOp, suppressed, err := h.checkStrictMode(op.Method, exists)
317221
if err != nil {
318222
return err
319223
}
320224

321-
function := &functionHooksCmd{Function: op.function}
225+
function := &functionHooksCmd{Function: op.Function}
322226
switch restOp {
323227
case "POST":
324228
return function.functionHooksCreate(e, nil)
@@ -357,20 +261,20 @@ func (h *Hooks) triggerHookExists(e *parsecli.Env, className, triggerName string
357261
}
358262

359263
func (h *Hooks) deployTriggerHook(e *parsecli.Env, op *hookOperation) error {
360-
if op.trigger == nil {
264+
if op.Trigger == nil {
361265
return stackerr.New("cannot deploy nil trigger hook")
362266
}
363267

364-
exists, err := h.triggerHookExists(e, op.trigger.ClassName, op.trigger.TriggerName)
268+
exists, err := h.triggerHookExists(e, op.Trigger.ClassName, op.Trigger.TriggerName)
365269
if err != nil {
366270
return err
367271
}
368-
restOp, suppressed, err := h.checkStrictMode(op.method, exists)
272+
restOp, suppressed, err := h.checkStrictMode(op.Method, exists)
369273
if err != nil {
370274
return err
371275
}
372276

373-
trigger := &triggerHooksCmd{Trigger: op.trigger, All: false}
277+
trigger := &triggerHooksCmd{Trigger: op.Trigger, All: false}
374278
switch restOp {
375279
case "POST":
376280
return trigger.triggerHooksCreate(e, nil)
@@ -388,13 +292,13 @@ func (h *Hooks) deployTriggerHook(e *parsecli.Env, op *hookOperation) error {
388292

389293
func (h *Hooks) deployWebhooksConfig(e *parsecli.Env, hooksOps []*hookOperation) error {
390294
for _, op := range hooksOps {
391-
if op.function == nil && op.trigger == nil {
295+
if op.Function == nil && op.Trigger == nil {
392296
return stackerr.New("hook operation is neither a function, not a trigger.")
393297
}
394-
if op.function != nil && op.trigger != nil {
298+
if op.Function != nil && op.Trigger != nil {
395299
return stackerr.New("a hook cannot be both a function and a trigger.")
396300
}
397-
if op.function != nil {
301+
if op.Function != nil {
398302
if err := h.deployFunctionHook(e, op); err != nil {
399303
return err
400304
}
@@ -437,7 +341,7 @@ func (h *Hooks) HooksCmd(e *parsecli.Env, ctx *parsecli.Context, args []string)
437341
if err != nil {
438342
return stackerr.Wrap(err)
439343
}
440-
reader = ioutil.NopCloser(file)
344+
reader = file
441345
} else {
442346
fmt.Fprintln(e.Out, "Since a webhooks config file was not provided reading from stdin.")
443347
}

0 commit comments

Comments
 (0)