summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--cmd/main.go42
-rw-r--r--go.mod12
-rw-r--r--http/middleware.go16
-rw-r--r--http/rest/registrationHandler.go90
-rw-r--r--log/log.go9
-rw-r--r--nats/constants.go5
-rw-r--r--nats/notify.go31
-rw-r--r--notifier.go7
-rw-r--r--persistence/sqlite/0001_Create_Initial_Tables.sql13
-rw-r--r--persistence/sqlite/sqliteRepository.go100
-rw-r--r--registration.go13
-rw-r--r--repository.go7
-rw-r--r--service.go22
14 files changed, 369 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..51b7223
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.sqlite
+*.sqlite3
diff --git a/cmd/main.go b/cmd/main.go
new file mode 100644
index 0000000..4ff1658
--- /dev/null
+++ b/cmd/main.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+ "context"
+ "log"
+ "net/http"
+ "os"
+ "path"
+
+ "uvok.de/go/training_fellow/registration"
+ myhttp "uvok.de/go/training_fellow/registration/http"
+ "uvok.de/go/training_fellow/registration/http/rest"
+ "uvok.de/go/training_fellow/registration/nats"
+ persistence "uvok.de/go/training_fellow/registration/persistence/sqlite"
+)
+
+func main() {
+ log.Print("Starting application")
+ workDir, err := os.Getwd()
+ context := context.Background()
+ if err != nil {
+ workDir = "."
+ }
+
+ notifier := &nats.NatsNotifier{}
+
+ databasePath := path.Join(workDir, "registrations.sqlite")
+ repository := persistence.NewRepository(databasePath, context)
+ service := &registration.RegistrationService{Notifier: notifier, Repository: repository}
+ handler := &rest.RegistrationHandler{Service: service}
+
+ newRegistrationHandler := rest.HandleRegistration(handler)
+
+ contentHandler := myhttp.IsFormContentCheck(newRegistrationHandler)
+ http.Handle("/register", contentHandler)
+ http.Handle("/registrations", rest.HandleGetRegistrations(handler))
+ log.Print("Starting server")
+ err = http.ListenAndServe(":8080", nil)
+ if err != nil {
+ log.Fatalf("Error starting HTTP server: %v", err)
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..989799b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,12 @@
+module uvok.de/go/training_fellow/registration
+
+go 1.21.4
+
+require (
+ github.com/klauspost/compress v1.17.0 // indirect
+ github.com/nats-io/nats.go v1.31.0 // indirect
+ github.com/nats-io/nkeys v0.4.5 // indirect
+ github.com/nats-io/nuid v1.0.1 // indirect
+ golang.org/x/crypto v0.6.0 // indirect
+ golang.org/x/sys v0.5.0 // indirect
+)
diff --git a/http/middleware.go b/http/middleware.go
new file mode 100644
index 0000000..f2fe90d
--- /dev/null
+++ b/http/middleware.go
@@ -0,0 +1,16 @@
+package http
+
+import "net/http"
+
+func IsFormContentCheck(innerHandler http.Handler) http.Handler {
+ outerHandler := func(w http.ResponseWriter, r *http.Request) {
+ // TOdo:
+ contentType := r.Header.Get("Content-Type")
+ if contentType != "application/x-www-form-urlencoded" {
+ w.WriteHeader(http.StatusNotAcceptable)
+ return
+ }
+ innerHandler.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(outerHandler)
+}
diff --git a/http/rest/registrationHandler.go b/http/rest/registrationHandler.go
new file mode 100644
index 0000000..b38d62c
--- /dev/null
+++ b/http/rest/registrationHandler.go
@@ -0,0 +1,90 @@
+package rest
+
+import (
+ "encoding/json"
+ "log"
+ "net/http"
+ "strconv"
+
+ "github.com/google/uuid"
+ "uvok.de/go/training_fellow/registration"
+ mylog "uvok.de/go/training_fellow/registration/log"
+)
+
+type RegistrationHandler struct {
+ Service *registration.RegistrationService
+}
+
+// adapter to have multiple methods without repeating type
+// (I have to implement ServeHTTP)
+func HandleRegistration(handler *RegistrationHandler) http.Handler {
+ regHandler := func(w http.ResponseWriter, r *http.Request) {
+ handleRegistration(handler, w, r)
+ }
+ return http.HandlerFunc(regHandler)
+}
+
+func HandleGetRegistrations(handler *RegistrationHandler) http.Handler {
+ getRegs := func(w http.ResponseWriter, r *http.Request) {
+ regs, err := handler.Service.GetUnconfirmedRegistrations()
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ content, err := json.Marshal(regs)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ w.Write(content)
+ }
+ return http.HandlerFunc(getRegs)
+}
+
+func handleRegistration(handler *RegistrationHandler, rw http.ResponseWriter, req *http.Request) {
+
+ //bodyReader := http.MaxBytesReader(rw, req.Body, 10)
+ //defer bodyReader.Close()
+ defer req.Body.Close()
+
+ if req.Method != http.MethodPost {
+ rw.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ err := req.ParseForm()
+ if err != nil {
+ rw.WriteHeader(http.StatusNotAcceptable)
+ mylog.LogError.Printf("Form Parse: %v\n", err)
+ return
+ }
+ newRegistration := registration.Registration{}
+
+ newRegistration.Company = req.Form.Get("company")
+ newRegistration.Date = req.Form.Get("date")
+ newRegistration.Email = req.Form.Get("email")
+ newRegistration.FirstName = req.Form.Get("first_name")
+ newRegistration.LastName = req.Form.Get("last_name")
+ newRegistration.TrainingCode = req.Form.Get("training_code")
+ val, err := strconv.ParseBool(req.Form.Get("privacy_acc"))
+ newRegistration.PrivacyPolicyAccepted = false
+ newRegistration.Confirmed = false
+ newRegistration.RegId = uuid.NewString()
+
+ if err != nil {
+ mylog.LogWarning.Printf("Form Parse Bool: %v\n", err)
+ } else {
+ newRegistration.PrivacyPolicyAccepted = val
+ }
+
+ log.Printf("New registration: %v", newRegistration)
+ if handler.Service != nil {
+ err = handler.Service.HandleNewRegistration(&newRegistration)
+ if err != nil {
+ rw.WriteHeader(http.StatusInternalServerError)
+ mylog.LogError.Printf("Error submitting the following registration: %v. Error: %v", newRegistration, err)
+ return
+ }
+ }
+ rw.WriteHeader(http.StatusCreated)
+}
diff --git a/log/log.go b/log/log.go
new file mode 100644
index 0000000..7edb61c
--- /dev/null
+++ b/log/log.go
@@ -0,0 +1,9 @@
+package log
+
+import (
+ "log"
+ "os"
+)
+
+var LogError = log.New(os.Stdout, "Error: ", log.LstdFlags)
+var LogWarning = log.New(os.Stdout, "Warning: ", log.LstdFlags)
diff --git a/nats/constants.go b/nats/constants.go
new file mode 100644
index 0000000..82436b5
--- /dev/null
+++ b/nats/constants.go
@@ -0,0 +1,5 @@
+package nats
+
+const (
+ NATS_TOPIC_REGISTRATION_NEW = "uvok.de.go.training_fellow.registration.new"
+)
diff --git a/nats/notify.go b/nats/notify.go
new file mode 100644
index 0000000..56cbc7f
--- /dev/null
+++ b/nats/notify.go
@@ -0,0 +1,31 @@
+package nats
+
+import (
+ "github.com/nats-io/nats.go"
+ "uvok.de/go/training_fellow/registration"
+)
+
+type NatsNotifier struct {
+}
+
+func (notif *NatsNotifier) NotifyNewRegistration(reg *registration.Registration) error {
+ conn, err := nats.Connect("nats://nats-server:4222")
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ enc_conn, err := nats.NewEncodedConn(conn, nats.JSON_ENCODER)
+
+ if err != nil {
+ return err
+ }
+
+ defer enc_conn.Close()
+
+ err = enc_conn.Publish(NATS_TOPIC_REGISTRATION_NEW, reg)
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/notifier.go b/notifier.go
new file mode 100644
index 0000000..f5169c3
--- /dev/null
+++ b/notifier.go
@@ -0,0 +1,7 @@
+package registration
+
+// RegistrationNotifier forwards new registrations
+type RegistrationNotifier interface {
+ //NotifyNewRegistration notifies about a new registration
+ NotifyNewRegistration(reg *Registration) error
+}
diff --git a/persistence/sqlite/0001_Create_Initial_Tables.sql b/persistence/sqlite/0001_Create_Initial_Tables.sql
new file mode 100644
index 0000000..d563e4a
--- /dev/null
+++ b/persistence/sqlite/0001_Create_Initial_Tables.sql
@@ -0,0 +1,13 @@
+CREATE TABLE IF NOT EXISTS registrations (
+ reg_id TEXT PRIMARY KEY NOT NULL,
+ first_name TEXT NOT NULL,
+ last_name TEXT NOT NULL,
+ email TEXT NOT NULL,
+ company TEXT,
+ training_code TEXT,
+ date TEXT NOT NULL,
+ privacy_policy_accepted INTEGER NOT NULL,
+ confirmed INTEGER NOT NULL
+);
+
+PRAGMA user_version = 1
diff --git a/persistence/sqlite/sqliteRepository.go b/persistence/sqlite/sqliteRepository.go
new file mode 100644
index 0000000..61e89d8
--- /dev/null
+++ b/persistence/sqlite/sqliteRepository.go
@@ -0,0 +1,100 @@
+package persistence
+
+import (
+ "context"
+ "database/sql"
+ "log"
+
+ _ "modernc.org/sqlite"
+
+ "uvok.de/go/training_fellow/registration"
+)
+
+var (
+ debug bool = true
+)
+
+type SqliteRepository struct {
+ FileName string
+ context context.Context
+}
+
+func NewRepository(fileName string, context context.Context) *SqliteRepository {
+ repo := SqliteRepository{FileName: fileName, context: context}
+ return &repo
+}
+
+func (repo *SqliteRepository) SaveRegistration(registration *registration.Registration) error {
+ db, err := repo.checkInit()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ res, err := db.ExecContext(
+ repo.context,
+ `INSERT INTO registrations
+ (reg_id, first_name, last_name, email, company, training_code, date, privacy_policy_accepted, confirmed)
+ VALUES (?,?,?,?,?,?,?,?,?)`,
+ registration.RegId,
+ registration.FirstName,
+ registration.LastName,
+ registration.Email,
+ registration.Company,
+ registration.TrainingCode,
+ registration.Date,
+ registration.PrivacyPolicyAccepted,
+ registration.Confirmed)
+
+ if debug {
+ log.Printf("SQL result: %v", res)
+ }
+ return err
+}
+
+func (repo *SqliteRepository) GetUnconfirmedRegistrations() ([]*registration.Registration, error) {
+ db, err := repo.checkInit()
+ if err != nil {
+ return nil, err
+ }
+ res, err := db.QueryContext(
+ repo.context,
+ `SELECT reg_id, first_name, last_name, email, company, training_code, date, privacy_policy_accepted
+ FROM registrations
+ WHERE confirmed = 0
+ `)
+
+ if err != nil {
+ return nil, err
+ }
+ defer res.Close()
+
+ regArray := make([]*registration.Registration, 0)
+ for res.Next() {
+ reg := registration.Registration{}
+ err = res.Scan(&reg.RegId, &reg.FirstName, &reg.LastName, &reg.Email, &reg.Company, &reg.TrainingCode, &reg.Date, &reg.PrivacyPolicyAccepted)
+ if err != nil {
+ log.Printf("Error scanning: %v", err)
+ }
+ regArray = append(regArray, &reg)
+ }
+ return regArray, nil
+}
+
+func (repo *SqliteRepository) ConfirmRegistration(registrationId string) (*registration.Registration, error) {
+ repo.checkInit()
+ panic("not implemented") // TODO: Implement
+}
+
+func (repo *SqliteRepository) checkInit() (*sql.DB, error) {
+ db, err := sql.Open("sqlite", repo.FileName)
+ if err != nil {
+ return nil, err
+ }
+ err = db.PingContext(repo.context)
+
+ if err != nil {
+ return nil, err
+ }
+ return db, nil
+}
diff --git a/registration.go b/registration.go
new file mode 100644
index 0000000..c6becc6
--- /dev/null
+++ b/registration.go
@@ -0,0 +1,13 @@
+package registration
+
+type Registration struct {
+ FirstName string `json:"FirstName,omitempty"`
+ LastName string `json:"LastName,omitempty"`
+ Email string `json:"Mail,omitempty"`
+ Company string `json:"Company,omitempty"`
+ TrainingCode string `json:"TCode,omitempty"`
+ Date string `json:"Date,omitempty"`
+ PrivacyPolicyAccepted bool `json:"PPol,omitempty"`
+ Confirmed bool `json:"confirmed"`
+ RegId string `json:"id"`
+}
diff --git a/repository.go b/repository.go
new file mode 100644
index 0000000..f559406
--- /dev/null
+++ b/repository.go
@@ -0,0 +1,7 @@
+package registration
+
+type RegistrationRepository interface {
+ SaveRegistration(*Registration) error
+ GetUnconfirmedRegistrations() ([]*Registration, error)
+ ConfirmRegistration(registrationId string) (*Registration, error)
+}
diff --git a/service.go b/service.go
new file mode 100644
index 0000000..6523d21
--- /dev/null
+++ b/service.go
@@ -0,0 +1,22 @@
+package registration
+
+type RegistrationService struct {
+ Notifier RegistrationNotifier
+ Repository RegistrationRepository
+}
+
+func (service *RegistrationService) HandleNewRegistration(reg *Registration) error {
+ return service.Repository.SaveRegistration(reg)
+}
+
+func (service *RegistrationService) GetUnconfirmedRegistrations() ([]*Registration, error) {
+ return service.Repository.GetUnconfirmedRegistrations()
+}
+
+func (service *RegistrationService) ConfirmRegistration(registrationId string) error {
+ registration, err := service.Repository.ConfirmRegistration(registrationId)
+ if err != nil {
+ return err
+ }
+ return service.Notifier.NotifyNewRegistration(registration)
+}