forked from aeneasr/workshop-dbg
/
main.go
192 lines (155 loc) · 5.75 KB
/
main.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package main
// The import section defines libraries that we are going to use in our program.
import (
"fmt"
"log"
"net/http"
"encoding/json"
"github.com/rs/cors"
"github.com/gorilla/mux"
"github.com/ory-am/common/pkg"
"github.com/ory-am/common/env"
"github.com/pborman/uuid"
)
// In a 12 factor app, we must obey the environment variables.
var envHost = env.Getenv("HOST", "")
var envPort = env.Getenv("PORT", "5678")
// Contact defines the structure of a contact which including name, department and company.
type Contact struct {
// The unique identifier of this contact.
// omitempty hides this field when exporting to json. Because it is common for json
// to be lowercase, we additionally define `json:"id"` to tell the "exporter" that this
// field should be called id, not ID.
ID string `json:"id,omitempty"`
// Name is the contact's full name.
Name string `json:"name"`
// Department is the contact's department in a company.
Department string `json:"department"`
// Company is the name of the company the contact works for.
Company string `json:"company"`
// Here is room for improvements like adding new fields
}
// Contacts is a list of contacts.
type Contacts map[string]Contact
// MyContacts is an exemplary list of contacts.
var MyContacts = Contacts{
// Each contact hs identified by its ID which is prepended with "my-id":
// We are doing this because it is easier to manage and simpler to read.
"john-bravo": Contact{
Name: "John Bravo",
Department: "IT",
Company: "ACME Inc",
},
"cathrine-mueller": Contact{
Name: "Cathrine Müller",
Department: "HR",
Company: "Grove AG",
},
"maximilian-schmidt": Contact{
Name: "Maximilian Schmidt",
Department: "PR",
Company: "Titanpad AG",
},
}
// The main routine is going the "entry" point.
func main() {
// Create a new router.
router := mux.NewRouter()
// RESTful defines operations
// * GET for fetching data
// * POST for inserting data
// * PUT for updating existing data
// * DELETE for deleting data
router.HandleFunc("/contacts", ListContacts(MyContacts)).Methods("GET")
router.HandleFunc("/contacts/{id}", UpdateContact(MyContacts)).Methods("PUT")
// The info endpoint is for showing demonstration purposes only and is not subject to any task.
router.HandleFunc("/info", InfoHandler).Methods("GET")
// Print where to point the browser at.
fmt.Printf("Listening on %s\n", "http://localhost:5678")
// Cross origin resource requests
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "DELETE", "PUT"}},
)
// Start up the server and check for errors.
listenOn := fmt.Sprintf("%s:%s", envHost, envPort)
err := http.ListenAndServe(listenOn, c.Handler(router))
if err != nil {
log.Fatalf("Could not set up server because %s", err)
}
}
// ListContacts takes a contact list and outputs it.
func ListContacts(contacts Contacts) func(rw http.ResponseWriter, r *http.Request) {
return func(rw http.ResponseWriter, r *http.Request) {
// Write contact list to output
pkg.WriteIndentJSON(rw, contacts)
}
}
// AddContact will add a contact to the list
func AddContact(contacts Contacts) func(rw http.ResponseWriter, r *http.Request) {
return func(rw http.ResponseWriter, r *http.Request) {
// We parse the request's information into contactToBeAdded
contactToBeAdded, err := ReadContactData(rw, r)
// Abort handling the request if an error occurs.
if err != nil {
return
}
// Save newContact to the list of contacts.
contacts[contactToBeAdded.ID] = contactToBeAdded
// Output our newly created contact
pkg.WriteIndentJSON(rw, contactToBeAdded)
}
}
// DeleteContact will delete a contact from the list
func DeleteContact(contacts Contacts) func(rw http.ResponseWriter, r *http.Request) {
return func(rw http.ResponseWriter, r *http.Request) {
// Fetch the ID of the contact that is going to be deleted
contactToBeDeleted := mux.Vars(r)["id"]
// Check if the contact exists and return an error if not
if _, found := contacts[contactToBeDeleted]; !found {
http.Error(rw, "I do not know any contact by that ID.", http.StatusNotFound)
return
}
// Delete the contact from the list
delete(contacts, contactToBeDeleted)
// Per specification, RESTful may return an empty response when a DELETE request was successful
rw.WriteHeader(http.StatusNoContent)
}
}
// UpdateContact will update a contact on the list
func UpdateContact(contacts Contacts) func(rw http.ResponseWriter, r *http.Request) {
return func(rw http.ResponseWriter, r *http.Request) {
// Fetch the ID of the contact that is going to be updated
contactToBeUpdated := mux.Vars(r)["id"]
// Check if the contact exists
if _, found := contacts[contactToBeUpdated]; !found {
http.Error(rw, "I don't know any contact by that ID.", http.StatusNotFound)
return
}
// We parse the request's information into newContactData.
newContactData, err := ReadContactData(rw, r)
// Abort handling the request if an error occurs.
if err != nil {
return
}
// Update the data in the contact list.
delete(contacts, contactToBeUpdated)
contacts[newContactData.ID] = newContactData
// Set the new data
pkg.WriteIndentJSON(rw, newContactData)
}
}
// ReadContactData is a helper function for parsing a HTTP request body. It returns a contact on success and an
// error if something went wrong.
func ReadContactData(rw http.ResponseWriter, r *http.Request) (contact Contact, err error) {
err = json.NewDecoder(r.Body).Decode(&contact)
if err != nil {
http.Error(rw, fmt.Sprintf("Could not read input data because %s", err), http.StatusBadRequest)
return contact, err
}
return contact, nil
}
var thisID = uuid.New()
func InfoHandler(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte(thisID))
}