-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp.go
130 lines (103 loc) · 3.62 KB
/
http.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
package main
import (
"context"
"fmt"
"log/slog"
"net/http"
"sync/atomic"
)
type (
loggerCtxKeyT struct{}
)
var (
requestIdSource atomic.Uint64
loggerCtxKey loggerCtxKeyT
)
func middlewareEmbedLogger(log *slog.Logger, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rid := requestIdSource.Add(1)
log := log.With("rid", rid)
log.Info("Processing request...", "method", r.Method, "url", r.URL)
next(w, r.WithContext(context.WithValue(r.Context(), loggerCtxKey, log)))
}
}
func getRequestLogger(r *http.Request) *slog.Logger {
return r.Context().Value(loggerCtxKey).(*slog.Logger)
}
func handlerGetVersionList() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
} else {
respondInKind(w, r, versionList())
}
}
}
func handlerGetVersionResourceList(fv FHIRVersion) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != fmt.Sprintf("/%s", fv) && r.URL.Path != fmt.Sprintf("/%s/", fv) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
respondInKind(w, r, versionResourceMap[fv])
}
}
func handlerGetResourceBundle(fv FHIRVersion, rscType string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rp := getRequestParams(r)
rscs := versionResourceMap[fv].GetResourcesByType(rscType, rp.Count)
out := Bundle{
ResourceType: "Bundle",
Entry: make([]BundleEntry, len(rscs)),
}
for i, rsc := range rscs {
out.Entry[i] = BundleEntry{Resource: rsc}
}
respondInKind(w, r, out)
}
}
func handlerGetVersionResource(fv FHIRVersion, rscType string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log := getRequestLogger(r)
rp := getRequestParams(r)
if rp.Count != 0 {
http.Error(w, "_count must be zero or undefined with specific resource ID", http.StatusBadRequest)
return
}
resourceId := r.PathValue("resource_id")
if resourceId == "" {
log.Error("Unable to parse resource_id param from path")
http.Error(w, "missing resource_id path parameter", http.StatusBadRequest)
return
}
rsc := versionResourceMap[fv].GetResource(rscType, resourceId)
if nil != rsc {
respondInKind(w, r, rsc)
return
}
log.Error("Resource not found", "resource_id", resourceId)
http.Error(w, fmt.Sprintf("no version %q resource %q found with id %q", fv.String(), rscType, resourceId), http.StatusNotFound)
}
}
func addHandler(log *slog.Logger, mux *http.ServeMux, route string, hdl http.HandlerFunc) {
log.Info("Adding route handler", "route", route)
mux.HandleFunc(route, middlewareEmbedLogger(log.With("route", route), middlewareParseRequestParams(hdl)))
}
func runWebserver(log *slog.Logger) error {
log.Info("Building routes...")
mux := http.NewServeMux()
for fv, resourceMap := range versionResourceMap {
// get version resource list
addHandler(log, mux, fmt.Sprintf("GET /%s/", fv.String()), handlerGetVersionResourceList(fv))
for _, rscType := range resourceMap.ResourceTypes() {
// get version resource bundle
addHandler(log, mux, fmt.Sprintf("GET /%s/%s/", fv.String(), rscType), handlerGetResourceBundle(fv, rscType))
// get specific version resource by id
addHandler(log, mux, fmt.Sprintf("GET /%s/%s/{resource_id}/", fv.String(), rscType), handlerGetVersionResource(fv, rscType))
}
}
// get version list
addHandler(log, mux, "GET /{$}", handlerGetVersionList())
log.Info("Webserver running", "addr", bindAddr)
return http.ListenAndServe(bindAddr, mux)
}