-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommon.go
137 lines (114 loc) · 4.72 KB
/
common.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
/*
Gondul GO API
Copyright 2020, Kristian Lyngstøl <[email protected]>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
Package gondulapi provides the framework for building a HTTP REST-API
backed by a Postgresql Database. The package can be split into three:
1. The HTTP bit, found in receiver/. This deals with accepting HTTP
requests, parsing requests and mapping them to data types. It ensures proper division
of labour, and makes it less easy to make inconsistent APIs by enforcing that
if you GET something on an URL, PUT or POST to the same URL will accept the
exact same data type back. In other words, you can do:
GET http://foo/blatti/x > file
vim file // Change a field
lwp-request -m PUT http://foo/blatti/x < file
And it will do what you expect, assuming the datastructure implements both
the Getter-interface and the Putter interface.
2. The SQL bit, found in db/. This is an attempt to use reflection to avoid
having to write database queries by hand. It is not meant to cover 100% of
all SQL access. Since it makes mapping a Go data type to an SQL table easy,
it is meant to inspire good database models, where the API can mostly just
get items back and forth.
3. "Misc" - or maybe I should say, the actual Gondul API. Which at this
moment isn't actually written. Some other bits fall under this category
though, such as config file management and logging. Not very exotic.
*/
package gondulapi
import "fmt"
// Report is an update report on write-requests. The precise meaning might
// vary, but the gist should be the same.
type Report struct {
Affected int `json:",omitempty"`
Ok int `json:",omitempty"`
Failed int `json:",omitempty"`
Error error `json:",omitempty"`
Code int `json:"-"`
Headers map[string]string `json:"-"`
}
// Auther allows objects to enforce (basic) authentication optionally. For
// every request, a basepath (the path the object is registered to), an
// element (the item being worked on, if any) a method (GET/PUT/POST, etc)
// and username and password is provided.
//
// See gondulapi/auth for some convenience-implementations.
type Auther interface {
Auth(basepath string, element string, method string, user string, password string) error
}
// Getter implements Get method, which should fetch the object represented
// by the element path.
type Getter interface {
Get(element string) (Report, error)
}
// Putter is an idempotent method that requires an absolute path. It should
// (over-)write the object found at the element path.
type Putter interface {
Put(element string) (Report, error)
}
// Poster is not necessarily idempotent, but can be. It should write the
// object provided, potentially generating a new ID for it if one isn't
// provided in the data structure itself.
type Poster interface {
Post() (Report, error)
}
// Deleter should delete the object identified by the element. It should be
// idempotent, in that it should be safe to call it on already-deleted
// items.
type Deleter interface {
Delete(element string) (Report, error)
}
// Errorf is a convenience-function to provide an Error data structure,
// which is essentially the same as fmt.Errorf(), but with an HTTP status
// code embedded into it which can be extracted.
func Errorf(code int, str string, v ...interface{}) Error {
return Errori(code, fmt.Sprintf(str, v...))
}
// Errori creates an error with the given status-code, with i as the
// message. i should either be a text string or implement fmt.Stringer
func Errori(code int, i interface{}) Error {
e := Error{
Code: code,
Message: i,
}
return e
}
// Error is used to combine a text-based error with a HTTP error code.
type Error struct {
Code int `json:"-"`
Message interface{}
}
// InternalError is provided for the common case of returning an opaque
// error that can be passed to a user.
var InternalError = Error{500, "Internal Server Error"}
// Error allows Error to implement the error interface. That's a whole lot
// of error in one sentence...
func (e Error) Error() string {
if m, ok := e.Message.(string); ok {
return m
}
if m, ok := e.Message.(fmt.Stringer); ok {
return m.String()
}
return fmt.Sprintf("%v", e.Message)
}