This repository has been archived by the owner on Jan 19, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathapi.go
133 lines (108 loc) · 3.85 KB
/
api.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
// Kiebitz - Privacy-Friendly Appointment Scheduling
// Copyright (C) 2021-2021 The Kiebitz Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version. Additional terms
// as defined in section 7 of the license (e.g. regarding attribution)
// are specified at https://kiebitz.eu/en/docs/open-source/additional-terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package services
import (
"fmt"
"github.com/kiprotect/go-helpers/forms"
"reflect"
)
type Context interface {
Params() map[string]interface{}
Result(data interface{}) Response
Error(code int, message string, data interface{}) Response
InternalError() Response
InvalidParams(err error) Response
IsInternalError(err Response) bool
MethodNotFound() Response
NotFound() Response
Acknowledge() Response
Nil() Response
}
type Response interface {
}
type Request interface {
}
// calls the handler with the validated and coerced form parameters
func callHandler(context Context, handler, params interface{}) (Response, error) {
value := reflect.ValueOf(handler)
if value.Kind() != reflect.Func {
return nil, fmt.Errorf("not a function")
}
paramsValue := reflect.ValueOf(params)
contextValue := reflect.ValueOf(context)
responseValue := value.Call([]reflect.Value{contextValue, paramsValue})
return responseValue[0].Interface().(Response), nil
}
func HandleAPICall(
handler interface{},
form *forms.Form,
validateSettings *ValidateSettings,
context Context) Response {
if params, err := form.ValidateWithContext(context.Params(), map[string]interface{}{"context": context, "settings": validateSettings}); err != nil {
return context.InvalidParams(err)
} else {
if paramsStruct, err := APIHandlerStruct(handler); err != nil {
// this should never happen...
Log.Error(err)
return context.InternalError()
} else if err := form.Coerce(paramsStruct, params); err != nil {
// this shouldn't happen either...
Log.Error(err)
return context.InternalError()
} else {
if response, err := callHandler(context, handler, paramsStruct); err != nil {
// and neither should this...
Log.Error(err)
return context.InternalError()
} else {
if response == nil {
return context.Nil()
}
return response
}
}
}
}
// returns a new struct that we can coerce the valid form parameters into
func APIHandlerStruct(handler interface{}) (interface{}, error) {
value := reflect.ValueOf(handler)
if value.Kind() != reflect.Func {
return nil, fmt.Errorf("not a function")
}
funcType := value.Type()
if funcType.NumIn() != 2 {
return nil, fmt.Errorf("expected a function with 2 arguments")
}
if funcType.NumOut() != 1 {
return nil, fmt.Errorf("expected a function with 1 return value")
}
returnType := funcType.Out(0)
if !returnType.Implements(reflect.TypeOf((*Response)(nil)).Elem()) {
return nil, fmt.Errorf("return value should be a response")
}
contextType := funcType.In(0)
if !contextType.Implements(reflect.TypeOf((*Context)(nil)).Elem()) {
return nil, fmt.Errorf("first argument should accept a context")
}
structType := funcType.In(1)
if structType.Kind() != reflect.Ptr || structType.Elem().Kind() != reflect.Struct {
return nil, fmt.Errorf("second argument should be a struct pointer")
}
// we create a new struct and return it
return reflect.New(structType.Elem()).Interface(), nil
}