diff --git a/Database.go b/Database.go new file mode 100644 index 0000000..4e1316e --- /dev/null +++ b/Database.go @@ -0,0 +1,27 @@ +package main + +import ( + "database/sql" + "fmt" +) + +var ClimateDb *sql.DB + +func InitDb() error { + db, err := sql.Open("mysql", "admin:sekna123jk@tcp(127.0.0.1:3306)/climate") + if err != nil { + fmt.Println("error connecting to database") + return err + } + ClimateDb = db + return nil +} + +func CloseDb() { + if ClimateDb != nil { + err := ClimateDb.Close() + if err != nil { + fmt.Println("error closing database: " + err.Error()) + } + } +} \ No newline at end of file diff --git a/HandlerHelpers.go b/HandlerHelpers.go new file mode 100644 index 0000000..92ab219 --- /dev/null +++ b/HandlerHelpers.go @@ -0,0 +1,91 @@ +package main + +import ( + "fmt" + "net/http" +) + +type HandlerFuncWithError = func (http.ResponseWriter, *http.Request) error + +type HttpMethod string + +const( + HttpGet HttpMethod = "GET" + HttpPost HttpMethod = "POST" +) + +type RequestContext struct { + ResponseWriter http.ResponseWriter + Request *http.Request + MethodType HttpMethod + Pattern string +} + +type Router struct { + patterns map[string] *PatternRouter +} + +type PatternRouter struct { + Get HandlerFuncWithError + Post HandlerFuncWithError +} + +func (p *Router) MethodFuncForPattern(methodType HttpMethod, pattern string) (HandlerFuncWithError, error) { + var methodFunc HandlerFuncWithError + switch methodType { + case HttpGet: + methodFunc = p.patterns[pattern].Get + case HttpPost: + methodFunc = p.patterns[pattern].Post + } + if methodFunc == nil { + return nil, fmt.Errorf("method %s not defined on pattern %s", methodType, pattern) + } + return methodFunc, nil +} + +func (p *Router) setGet(pattern string, cb HandlerFuncWithError) { + if _, ok := p.patterns[pattern]; ok { + p.patterns[pattern].Get = cb + } else { + p.patterns[pattern] = &PatternRouter{cb, nil} + http.HandleFunc(pattern, getPatternHandler(pattern)) + } +} + +func (p *Router) setPost(pattern string, cb HandlerFuncWithError) { + if _, ok := p.patterns[pattern]; ok { + p.patterns[pattern].Post = cb + } else { + p.patterns[pattern] = &PatternRouter{nil, cb} + http.HandleFunc(pattern, getPatternHandler(pattern)) + } +} + +var MainRouter = Router{make(map[string] *PatternRouter)} + +func getPatternHandler(pattern string) http.HandlerFunc { + return func (w http.ResponseWriter, r *http.Request) { + cb, err := MainRouter.MethodFuncForPattern(HttpMethod(r.Method), pattern) + if err == nil { + err = cb(w, r) + } + if err != nil { + context := RequestContext{w, r, HttpMethod(r.Method), pattern} + sendInternalError(err, context) + } + } +} + +func sendInternalError(err error, context RequestContext) { + errorMessage := "Internal Server Error!" + if DEBUG { + errorMessage += fmt.Sprintf( " Happened during %s request for pattern '%s': %s", + context.MethodType, + context.Pattern, + err.Error()) + } + fmt.Println(errorMessage) + http.Error(context.ResponseWriter, errorMessage, 500) +} + diff --git a/Snapshot.go b/Snapshot.go new file mode 100644 index 0000000..17a63a6 --- /dev/null +++ b/Snapshot.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "time" +) + +type SnapshotRecord struct { + Id int + Timestamp time.Time + Temp float32 + Humidity float32 + Co2 float32 +} + +type SnapshotSubmission struct { + Timestamp float32 `json:"time"` + Temp float32 `json:"temp"` + Humidity float32 `json:"humidity"` + Co2 float32 `json:"co2"` +} + +func createSnapshotFromJson(jsonBodyStream io.ReadCloser) (*SnapshotSubmission, error) { + var snapshotSub SnapshotSubmission + body, err := ioutil.ReadAll(jsonBodyStream) + if err != nil { + return nil, fmt.Errorf("error reading body stream: %w", err) + } + err = jsonBodyStream.Close() + if err != nil { + return nil, fmt.Errorf("couldn't close body stream: %w", err) + } + err = json.Unmarshal(body, &snapshotSub) + if err != nil { + return nil, fmt.Errorf("couldn't unmarshal json: %w", err) + } + return &snapshotSub, nil +} \ No newline at end of file diff --git a/climate-server.go b/climate-server.go new file mode 100644 index 0000000..765704f --- /dev/null +++ b/climate-server.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + _ "github.com/go-sql-driver/mysql" + "log" + "net/http" +) + +const DEBUG = true + +func main() { + err := setup() + defer teardown() + if err == nil { + startServer() + } +} + +func setup() error { + err := InitDb() + if err != nil { + return err + } + return nil +} + +func teardown() { + CloseDb() +} + +func startServer() { + port := "8001" + MainRouter.setGet("/", showCharts) + MainRouter.setPost("/", saveSnapshot) + fmt.Printf("Listening on port %s...\n", port) + log.Fatal(http.ListenAndServe(":" + port, nil)) +} + +func showCharts(w http.ResponseWriter, r *http.Request) error { + _, err := fmt.Fprint(w, "