From c3de3dd21fd63afd1fcc877753487c2a611bb244 Mon Sep 17 00:00:00 2001 From: Martin Pander Date: Mon, 7 Nov 2022 19:19:23 +0100 Subject: [PATCH] Implement Journal read/write --- backend/dash_backend.go | 3 +- backend/database/database.go | 4 +- backend/database/pgDatabase.go | 14 +- backend/mapping/mapper.go | 5 + backend/mapping/mapperImpl.go | 87 +++++++++- backend/mapping/mapperImpl_test.go | 255 +++++++++++++++++++++++++++++ backend/service/api_service.go | 54 +++--- 7 files changed, 378 insertions(+), 44 deletions(-) create mode 100644 backend/mapping/mapperImpl_test.go diff --git a/backend/dash_backend.go b/backend/dash_backend.go index 54664b2..9a4b924 100644 --- a/backend/dash_backend.go +++ b/backend/dash_backend.go @@ -26,8 +26,7 @@ func main() { mapper := mapping.NewMapperImpl() - // DefaultApiService := dashapi.NewDefaultApiService() - DefaultApiService := service.NewDefaultApiService(db, mapper) + DefaultApiService := service.NewApiService(db, mapper) DefaultApiController := dashapi.NewDefaultApiController(DefaultApiService) router := dashapi.NewRouter(DefaultApiController) diff --git a/backend/database/database.go b/backend/database/database.go index 897ed5c..1424194 100644 --- a/backend/database/database.go +++ b/backend/database/database.go @@ -7,6 +7,6 @@ import ( ) type Database interface { - WriteJournalEntry(dbmodel models.Journal) error - GetJournalEntryForDate(date time.Time) (models.Journal, error) + WriteJournalEntry(models.Journal) error + GetJournalEntryForDate(time.Time) (models.Journal, error) } diff --git a/backend/database/pgDatabase.go b/backend/database/pgDatabase.go index 9e47e4f..d11a70f 100644 --- a/backend/database/pgDatabase.go +++ b/backend/database/pgDatabase.go @@ -5,8 +5,10 @@ import ( "log" "time" + "gorm.io/datatypes" "gorm.io/driver/postgres" "gorm.io/gorm" + "gorm.io/gorm/clause" "github.com/moustachioed/dash/backend/database/models" ) @@ -38,10 +40,18 @@ func (db *PgDatabase) migrate() { db.Db.AutoMigrate(&models.Journal{}) } -func (db *PgDatabase) WriteJournalEntry(dbmodel models.Journal) error { +func (db *PgDatabase) WriteJournalEntry(entry models.Journal) error { + err := db.Db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&entry).Error + if err != nil { + log.Print("Error writing journal entry to database.") + return err + } + return nil } func (db *PgDatabase) GetJournalEntryForDate(date time.Time) (models.Journal, error) { - return models.Journal{}, nil + entry := models.Journal{Date: datatypes.Date(date)} + db.Db.First(&entry) + return entry, nil } diff --git a/backend/mapping/mapper.go b/backend/mapping/mapper.go index 6c9c0c1..d77a5ec 100644 --- a/backend/mapping/mapper.go +++ b/backend/mapping/mapper.go @@ -1,6 +1,8 @@ package mapping import ( + "time" + api "github.com/moustachioed/dash/backend/dashapi" db "github.com/moustachioed/dash/backend/database/models" ) @@ -8,4 +10,7 @@ import ( type Mapper interface { JournalApiToDb(api.JournalEntry) db.Journal JournalDbToApi(db.Journal) api.JournalEntry + + StringToDate(string) (time.Time, error) + DateToString(time.Time) string } diff --git a/backend/mapping/mapperImpl.go b/backend/mapping/mapperImpl.go index 1794bf4..9333ad8 100644 --- a/backend/mapping/mapperImpl.go +++ b/backend/mapping/mapperImpl.go @@ -1,6 +1,7 @@ package mapping import ( + "encoding/json" "log" "time" @@ -22,16 +23,88 @@ func (mapper MapperImpl) JournalApiToDb(apimodel api.JournalEntry) db.Journal { log.Printf("[ERROR] Could not parse date `%s`", apimodel.Date) } - dbmodel := db.Journal{ - Date: datatypes.Date(date), - // Thankful: datatypes.JSON([]byte(api.Thankful)), + thankful, err := json.Marshal(apimodel.Thankful) + if err != nil { + thankful = nil } - return dbmodel + lookingForward, err := json.Marshal(apimodel.LookingForward) + if err != nil { + lookingForward = nil + } + + beenGreat, err := json.Marshal(apimodel.BeenGreat) + if err != nil { + beenGreat = nil + } + + doBetter, err := json.Marshal(apimodel.DoBetter) + if err != nil { + doBetter = nil + } + + return db.Journal{ + Date: datatypes.Date(date), + Thankful: datatypes.JSON(thankful), + LookingForward: datatypes.JSON(lookingForward), + BeenGreat: datatypes.JSON(beenGreat), + DoBetter: datatypes.JSON(doBetter), + Journal: apimodel.Journal, + } } -func (mapper MapperImpl) JournalDbToApi(db.Journal) api.JournalEntry { - apimodel := api.JournalEntry{} +func (mapper MapperImpl) JournalDbToApi(dbmodel db.Journal) api.JournalEntry { + dateValue, err := dbmodel.Date.Value() + var date string + if err != nil { + date = "" + } else { + date = dateValue.(time.Time).Format("2006-01-02") + } - return apimodel + var thankful []string + err = json.Unmarshal(dbmodel.Thankful, &thankful) + if err != nil { + thankful = nil + } + + var lookingForward []string + err = json.Unmarshal(dbmodel.LookingForward, &lookingForward) + if err != nil { + thankful = nil + } + + var beenGreat []string + err = json.Unmarshal(dbmodel.BeenGreat, &beenGreat) + if err != nil { + thankful = nil + } + + var doBetter []string + err = json.Unmarshal(dbmodel.DoBetter, &doBetter) + if err != nil { + thankful = nil + } + + return api.JournalEntry{ + Date: date, + Thankful: thankful, + LookingForward: lookingForward, + BeenGreat: beenGreat, + DoBetter: doBetter, + Journal: dbmodel.Journal, + } +} + +func (mapper MapperImpl) StringToDate(dateString string) (time.Time, error) { + date, err := time.Parse("2006-01-02", dateString) + if err != nil { + log.Printf("[ERROR] Could not parse date string %s", dateString) + return time.Now(), err + } + return date, nil +} + +func (mapper MapperImpl) DateToString(date time.Time) string { + return date.Format("2006-01-02") } diff --git a/backend/mapping/mapperImpl_test.go b/backend/mapping/mapperImpl_test.go new file mode 100644 index 0000000..00567d2 --- /dev/null +++ b/backend/mapping/mapperImpl_test.go @@ -0,0 +1,255 @@ +package mapping + +import ( + "encoding/json" + "testing" + "time" + + "github.com/moustachioed/dash/backend/dashapi" + "github.com/moustachioed/dash/backend/database/models" + "gorm.io/datatypes" +) + +func TestJournalApiToDbFullObject(t *testing.T) { + mapper := NewMapperImpl() + + api := dashapi.JournalEntry{ + Date: "2022-02-18", + Thankful: []string{"ttest1", "ttest2", "ttest3"}, + LookingForward: []string{"lftest1", "lftest2", "lftest3"}, + BeenGreat: []string{"bgtest1", "bgtest2", "bgtest3"}, + DoBetter: []string{"dbtest1", "dbtest2", "dbtest3"}, + Journal: "jtest", + } + + db := mapper.JournalApiToDb(api) + + gotDate, _ := db.Date.Value() + got := gotDate.(time.Time).Format("2006-01-02") + wantDate := api.Date + + if got != wantDate { + t.Errorf("Mapped string %s not equal want string %s", got, wantDate) + } + + got = db.Thankful.String() + want, _ := json.Marshal(api.Thankful) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.LookingForward.String() + want, _ = json.Marshal(api.LookingForward) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.BeenGreat.String() + want, _ = json.Marshal(api.BeenGreat) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.DoBetter.String() + want, _ = json.Marshal(api.DoBetter) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.Journal + wantstring := api.Journal + + if got != string(wantstring) { + t.Errorf("Mapped string %s not equal want string %s", got, wantstring) + } +} + +func TestJournalApiToDbPartialObject(t *testing.T) { + mapper := NewMapperImpl() + + api := dashapi.JournalEntry{ + Date: "2022-02-18", + Thankful: nil, + LookingForward: []string{"lftest1", "lftest2", "lftest3"}, + BeenGreat: nil, + DoBetter: []string{"dbtest1", "dbtest2", "dbtest3"}, + Journal: "", + } + + db := mapper.JournalApiToDb(api) + + gotDate, _ := db.Date.Value() + got := gotDate.(time.Time).Format("2006-01-02") + wantDate := api.Date + + if got != wantDate { + t.Errorf("Mapped string %s not equal want string %s", got, wantDate) + } + + got = db.Thankful.String() + want, _ := json.Marshal(api.Thankful) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.LookingForward.String() + want, _ = json.Marshal(api.LookingForward) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.BeenGreat.String() + want, _ = json.Marshal(api.BeenGreat) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.DoBetter.String() + want, _ = json.Marshal(api.DoBetter) + + if got != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got = db.Journal + wantstring := api.Journal + + if got != string(wantstring) { + t.Errorf("Mapped string %s not equal want string %s", got, wantstring) + } +} + +func TestJournalDbToApiFullObject(t *testing.T) { + mapper := NewMapperImpl() + + date, _ := time.Parse("2006-01-02", "2022-02-18") + thankful, _ := json.Marshal([]string{"ttest1", "ttest2", "ttest3"}) + lookingForward, _ := json.Marshal([]string{"lftest1", "lftest2", "lftest3"}) + beenGreat, _ := json.Marshal([]string{"bgtest1", "bgtest2", "bgtest3"}) + doBetter, _ := json.Marshal([]string{"dbtest1", "dbtest2", "test3"}) + journal := "jtest" + + db := models.Journal{ + Date: datatypes.Date(date), + Thankful: datatypes.JSON(thankful), + LookingForward: datatypes.JSON(lookingForward), + BeenGreat: datatypes.JSON(beenGreat), + DoBetter: datatypes.JSON(doBetter), + Journal: journal, + } + + api := mapper.JournalDbToApi(db) + + got, _ := json.Marshal(api.Date) + wantDate := "\"2022-02-18\"" + + if string(got) != wantDate { + t.Errorf("Mapped string %s not equal want string %s", got, wantDate) + } + + got, _ = json.Marshal(api.Thankful) + want := thankful + + if string(got) != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got, _ = json.Marshal(api.LookingForward) + want = lookingForward + + if string(got) != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got, _ = json.Marshal(api.BeenGreat) + want = beenGreat + + if string(got) != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got, _ = json.Marshal(api.DoBetter) + want = doBetter + + if string(got) != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + gotJournal := api.Journal + wantJournal := journal + + if gotJournal != wantJournal { + t.Errorf("Mapped string %s not equal want string %s", gotJournal, wantJournal) + } +} + +func TestJournalDbToApiPartialObject(t *testing.T) { + mapper := NewMapperImpl() + + date, _ := time.Parse("2006-01-02", "2022-02-18") + var thankful []byte = nil + lookingForward, _ := json.Marshal([]string{"lftest1", "lftest2", "lftest3"}) + var beenGreat []byte = nil + doBetter, _ := json.Marshal([]string{"dbtest1", "dbtest2", "test3"}) + journal := "jtest" + + db := models.Journal{ + Date: datatypes.Date(date), + Thankful: datatypes.JSON(thankful), + LookingForward: datatypes.JSON(lookingForward), + BeenGreat: datatypes.JSON(beenGreat), + DoBetter: datatypes.JSON(doBetter), + Journal: journal, + } + + api := mapper.JournalDbToApi(db) + + got, _ := json.Marshal(api.Date) + wantDate := "\"2022-02-18\"" + + if string(got) != wantDate { + t.Errorf("Mapped string %s not equal want string %s", got, wantDate) + } + + got, _ = json.Marshal(api.Thankful) + wantString := "null" + + if string(got) != wantString { + t.Errorf("Mapped string %s not equal want string %s", got, wantString) + } + + got, _ = json.Marshal(api.LookingForward) + want := lookingForward + + if string(got) != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + got, _ = json.Marshal(api.BeenGreat) + wantString = "null" + + if string(got) != wantString { + t.Errorf("Mapped string %s not equal want string %s", got, wantString) + } + + got, _ = json.Marshal(api.DoBetter) + want = doBetter + + if string(got) != string(want) { + t.Errorf("Mapped string %s not equal want string %s", got, want) + } + + gotJournal := api.Journal + wantJournal := journal + + if gotJournal != wantJournal { + t.Errorf("Mapped string %s not equal want string %s", gotJournal, wantJournal) + } +} diff --git a/backend/service/api_service.go b/backend/service/api_service.go index 6a246a4..89d83bf 100644 --- a/backend/service/api_service.go +++ b/backend/service/api_service.go @@ -1,17 +1,9 @@ -/* - * Dash API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * API version: 0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - package service import ( "context" "errors" + "log" "net/http" "github.com/moustachioed/dash/backend/dashapi" @@ -19,44 +11,44 @@ import ( "github.com/moustachioed/dash/backend/mapping" ) -// DefaultApiService is a service that implements the logic for the DefaultApiServicer -// This service should implement the business logic for every endpoint for the DefaultApi API. +// ApiService is a service that implements the logic for the DefaultApiServicer +// This service should implement the business logic for every endpoint for the Api API. // Include any external packages or services that will be required by this service. -type DefaultApiService struct { +type ApiService struct { db database.Database mapper mapping.Mapper } -// NewDefaultApiService creates a default api service -func NewDefaultApiService(db database.Database, mapper mapping.Mapper) dashapi.DefaultApiServicer { - return &DefaultApiService{} +// NewApiService creates a default api service +func NewApiService(db database.Database, mapper mapping.Mapper) dashapi.DefaultApiServicer { + service := ApiService{ + db: db, + mapper: mapper, + } + + return &service } // GetJournalEntryForDate - -func (s *DefaultApiService) GetJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { - // TODO - update GetJournalEntryForDate with the required logic for this service method. - // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. +func (s *ApiService) GetJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { + d, err := s.mapper.StringToDate(date) + if err != nil { + log.Fatal(err) + } - //TODO: Uncomment the next line to return response Response(200, JournalEntry{}) or use other options such as http.Ok ... - //return Response(200, JournalEntry{}), nil - - return dashapi.Response(http.StatusNotImplemented, nil), errors.New("GetJournalEntryForDate method not implemented") + dbEntry, _ := s.db.GetJournalEntryForDate(d) + journal := s.mapper.JournalDbToApi(dbEntry) + return dashapi.Response(200, journal), nil } // WriteJournalEntryForDate - -func (s *DefaultApiService) WriteJournalEntryForDate(ctx context.Context, date string, journalEntry dashapi.JournalEntry) (dashapi.ImplResponse, error) { - // TODO - update WriteJournalEntryForDate with the required logic for this service method. - // Add api_default_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation. - - //TODO: Uncomment the next line to return response Response(200, {}) or use other options such as http.Ok ... - //return Response(200, nil),nil +func (s *ApiService) WriteJournalEntryForDate(ctx context.Context, date string, journalEntry dashapi.JournalEntry) (dashapi.ImplResponse, error) { journal := s.mapper.JournalApiToDb(journalEntry) s.db.WriteJournalEntry(journal) - // log.Printf(journal.Thankful) - return dashapi.Response(http.StatusNotImplemented, nil), errors.New("WriteJournalEntryForDate method not implemented") + return dashapi.Response(200, nil), nil } -func (s *DefaultApiService) DeleteJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { +func (s *ApiService) DeleteJournalEntryForDate(ctx context.Context, date string) (dashapi.ImplResponse, error) { return dashapi.Response(http.StatusNotImplemented, nil), errors.New("DeleteJournalEntryForDate method not implemented") }