From f17bac13d7160c281d49afca4a13aa0ac22f7458 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Fri, 7 Jan 2022 11:45:07 -0400 Subject: [PATCH 01/11] WEOS-1243 BDD test updated --- end2end_test.go | 88 ++++++++++++++++++++++++++++++++++- features/edit-content.feature | 29 +++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/end2end_test.go b/end2end_test.go index 9bcce079..969578f8 100644 --- a/end2end_test.go +++ b/end2end_test.go @@ -30,6 +30,7 @@ var errors error var buf bytes.Buffer var payload ContentType var rec *httptest.ResponseRecorder +var header http.Header var db *sql.DB var requests map[string]map[string]interface{} var currScreen string @@ -360,9 +361,15 @@ func theIsSubmitted(contentType string) error { reqBytes, _ := json.Marshal(req) body := bytes.NewReader(reqBytes) - request := httptest.NewRequest("POST", "/"+strings.ToLower(contentType), body) + var request *http.Request + if strings.Contains(currScreen, "create") { + request = httptest.NewRequest("POST", "/"+strings.ToLower(contentType), body) + } else if strings.Contains(currScreen, "update") { + request = httptest.NewRequest("PUT", "/"+strings.ToLower(contentType), body) + } request = request.WithContext(context.TODO()) - request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + request.Header = header request.Close = true rec = httptest.NewRecorder() e.ServeHTTP(rec, request) @@ -455,6 +462,78 @@ func aEntityConfigurationShouldBeSetup(arg1 string, arg2 *godog.DocString) error return nil } +func aHeaderWithValue(key, value string) error { + header.Add(key, value) + return godog.ErrPending +} + +func aResponseShouldBeReturned(code int) error { + if rec.Code != code { + return fmt.Errorf("expected the code to be %d got %d", code, rec.Code) + } + return nil +} + +func isOnTheEditScreenWithId(user, contentType, id string) error { + requests[strings.ToLower(contentType+"_update")] = map[string]interface{}{} + currScreen = strings.ToLower(contentType + "_update") + return nil +} + +func theHeaderShouldBe(key, value string) error { + headers := rec.HeaderMap + val := headers[value] + + if len(val) > 0 { + if strings.EqualFold(val[0], value) { + return nil + } + } + return fmt.Errorf("expected the header %s value to be %s got %v", key, value, val) +} + +func theIsUpdated(contentType string, details *godog.Table) error { + if rec.Result().StatusCode != http.StatusOK { + return fmt.Errorf("expected the status code to be '%d', got '%d'", http.StatusOK, rec.Result().StatusCode) + } + + head := details.Rows[0].Cells + compare := map[string]interface{}{} + + for i := 1; i < len(details.Rows); i++ { + for n, cell := range details.Rows[i].Cells { + compare[head[n].Value] = cell.Value + } + } + + contentEntity := map[string]interface{}{} + var result *gorm.DB + //ETag would help with this + for key, value := range compare { + result = API.Application.DB().Table(strings.Title(contentType)).Find(&contentEntity, key+" = ?", value) + if contentEntity != nil { + break + } + } + + if contentEntity == nil { + return fmt.Errorf("unexpected error finding content type in db") + } + + if result.Error != nil { + return fmt.Errorf("unexpected error finding content type: %s", result.Error) + } + + for key, value := range compare { + if contentEntity[key] != value { + return fmt.Errorf("expected %s %s %s, got %s", contentType, key, value, contentEntity[key]) + } + } + + contentTypeID[strings.ToLower(contentType)] = true + return nil +} + func InitializeScenario(ctx *godog.ScenarioContext) { ctx.Before(reset) //add context steps @@ -478,7 +557,12 @@ func InitializeScenario(ctx *godog.ScenarioContext) { ctx.Step(`^the "([^"]*)" should have an id$`, theShouldHaveAnId) ctx.Step(`^the specification is$`, theSpecificationIs) ctx.Step(`^the "([^"]*)" specification is parsed$`, theSpecificationIsParsed) + ctx.Step(`^the "([^"]*)" header should be "([^"]*)"$`, theHeaderShouldBe) ctx.Step(`^a "([^"]*)" entity configuration should be setup$`, aEntityConfigurationShouldBeSetup) + ctx.Step(`^"([^"]*)" is on the "([^"]*)" edit screen with id "([^"]*)"$`, isOnTheEditScreenWithId) + ctx.Step(`^the "([^"]*)" is updated$`, theIsUpdated) + ctx.Step(`^a header "([^"]*)" with value "([^"]*)"$`, aHeaderWithValue) + ctx.Step(`^a (\d+) response should be returned$`, aResponseShouldBeReturned) } diff --git a/features/edit-content.feature b/features/edit-content.feature index 216b764c..576cea27 100644 --- a/features/edit-content.feature +++ b/features/edit-content.feature @@ -1,4 +1,3 @@ -@skipped Feature: Edit content Background: @@ -13,6 +12,34 @@ Feature: Edit content title: Blog Aggregator Rest API version: 0.1.0 description: REST API for interacting with the Blog Aggregator + servers: + - url: https://prod1.weos.sh/blog/dev + description: WeOS Dev + - url: https://prod1.weos.sh/blog/v1 + x-weos-config: + logger: + level: warn + report-caller: true + formatter: json + database: + driver: sqlite3 + database: e2e.db + event-source: + - title: default + driver: service + endpoint: https://prod1.weos.sh/events/v1 + - title: event + driver: sqlite3 + database: e2e.db + databases: + - title: default + driver: sqlite3 + database: e2e.db + rest: + middleware: + - RequestID + - Recover + - ZapLogger components: schemas: Blog: From a801a2d99414d9087f78167b74e1ae9574568b7d Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Fri, 7 Jan 2022 11:47:00 -0400 Subject: [PATCH 02/11] WEOS-1243 BDD test updated --- end2end_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/end2end_test.go b/end2end_test.go index 969578f8..a7f3f381 100644 --- a/end2end_test.go +++ b/end2end_test.go @@ -110,6 +110,7 @@ func reset(ctx context.Context, sc *godog.Scenario) (context.Context, error) { contentTypeID = map[string]bool{} Developer = &User{} errors = nil + header = make(http.Header) rec = httptest.NewRecorder() os.Remove("e2e.db") var err error From c99f686d76c8d42d03ffe18079150f691e39cd74 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Fri, 7 Jan 2022 11:50:16 -0400 Subject: [PATCH 03/11] WEOS-1243 BDD test updated --- features/define-edit-endpoint-oas.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/define-edit-endpoint-oas.feature b/features/define-edit-endpoint-oas.feature index 58f14916..d3dbfd9e 100644 --- a/features/define-edit-endpoint-oas.feature +++ b/features/define-edit-endpoint-oas.feature @@ -1,3 +1,4 @@ +@skipped @WEOS-1177 Feature: Edit content endpoints From 807a7ca1f7237eb7fb6e8154f80c2c1581404d9d Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Fri, 7 Jan 2022 13:37:52 -0400 Subject: [PATCH 04/11] WEOS-1259 add update event handler to projections add update projections tests add update handler for update event in projections --- projections/gorm.go | 39 ++++- projections/projections.go | 5 +- projections/projections_test.go | 274 +++++++++++++++++++++++++++++++- 3 files changed, 308 insertions(+), 10 deletions(-) diff --git a/projections/gorm.go b/projections/gorm.go index a8516dc5..c64577e1 100644 --- a/projections/gorm.go +++ b/projections/gorm.go @@ -2,6 +2,8 @@ package projections import ( "encoding/json" + "strings" + weosContext "github.com/wepala/weos-service/context" weos "github.com/wepala/weos-service/model" "golang.org/x/net/context" @@ -43,15 +45,38 @@ func (p *GORMProjection) GetEventHandler() weos.EventHandler { return func(ctx context.Context, event weos.Event) { switch event.Type { case "create": - var eventPayload map[string]interface{} contentType := weosContext.GetContentType(ctx) - err := json.Unmarshal(event.Payload, &eventPayload) - if err != nil { - p.logger.Errorf("error unmarshalling event '%s'", err) + eventPayload, ok := p.Schema[strings.Title(contentType.Name)] + if !ok { + p.logger.Errorf("found no content type %s", contentType.Name) + } else { + err := json.Unmarshal(event.Payload, &eventPayload) + if err != nil { + p.logger.Errorf("error unmarshalling event '%s'", err) + } + db := p.db.Table(contentType.Name).Create(eventPayload) + if db.Error != nil { + p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) + } } - db := p.db.Table(contentType.Name).Create(eventPayload) - if db.Error != nil { - p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) + case "update": + contentType := weosContext.GetContentType(ctx) + eventPayload, ok := p.Schema[strings.Title(contentType.Name)] + if !ok { + p.logger.Errorf("found no content type %s", contentType.Name) + } else { + err := json.Unmarshal(event.Payload, &eventPayload) + if err != nil { + p.logger.Errorf("error unmarshalling event '%s'", err) + } + db := p.db.Table(contentType.Name).Updates(eventPayload) + if db.Error != nil { + p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) + } + db = p.db.Table(contentType.Name).Updates(eventPayload) + if db.Error != nil { + p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) + } } } } diff --git a/projections/projections.go b/projections/projections.go index c560aa70..5328aa6b 100644 --- a/projections/projections.go +++ b/projections/projections.go @@ -10,6 +10,7 @@ type Projection interface { } type DefaultProjection struct { - WEOSID string `json:"weos_id" gorm:"unique"` - Table string `json:"table_alias"` + WEOSID string `json:"weos_id" gorm:"unique"` + SequenceNo int64 + Table string `json:"table_alias"` } diff --git a/projections/projections_test.go b/projections/projections_test.go index 28a1b55f..dda4fdf8 100644 --- a/projections/projections_test.go +++ b/projections/projections_test.go @@ -13,6 +13,7 @@ import ( "github.com/labstack/echo/v4" ds "github.com/ompluscator/dynamic-struct" + "github.com/segmentio/ksuid" weosContext "github.com/wepala/weos-service/context" "gorm.io/gorm/clause" @@ -805,6 +806,277 @@ components: }) } +func TestProjections_Update(t *testing.T) { + + t.Run("Basic Update", func(t *testing.T) { + openAPI := `openapi: 3.0.3 +info: + title: Blog + description: Blog example + version: 1.0.0 +servers: + - url: https://prod1.weos.sh/blog/dev + description: WeOS Dev + - url: https://prod1.weos.sh/blog/v1 +x-weos-config: + logger: + level: warn + report-caller: true + formatter: json + database: + driver: sqlite3 + database: test.db + event-source: + - title: default + driver: service + endpoint: https://prod1.weos.sh/events/v1 + - title: event + driver: sqlite3 + database: test.db + databases: + - title: default + driver: sqlite3 + database: test.db + rest: + middleware: + - RequestID + - Recover + - ZapLogger +components: + schemas: + Blog: + type: object + properties: + title: + type: string + description: blog title + description: + type: string +` + + loader := openapi3.NewSwaggerLoader() + swagger, err := loader.LoadSwaggerFromData([]byte(openAPI)) + if err != nil { + t.Fatal(err) + } + + schemes := rest.CreateSchema(context.Background(), echo.New(), swagger) + p, err := projections.NewProjection(context.Background(), app, schemes) + if err != nil { + t.Fatal(err) + } + + err = p.Migrate(context.Background()) + if err != nil { + t.Fatal(err) + } + + gormDB := app.DB() + + id := ksuid.New().String() + gormDB.Table("Blog").Create(map[string]interface{}{"weos_id": id, "title": "hugs"}) + + payload := map[string]interface{}{"id": 1, "title": "testBlog", "description": "This is a create projection test"} + contentEntity := &weos.ContentEntity{ + AggregateRoot: weos.AggregateRoot{ + BasicEntity: weos.BasicEntity{ + ID: id, + }, + }, + Property: payload, + } + + ctxt := context.Background() + ctxt = context.WithValue(ctxt, weosContext.CONTENT_TYPE, &weosContext.ContentType{ + Name: "Blog", + }) + + event := weos.NewEntityEvent("update", contentEntity, contentEntity.ID, &payload) + p.GetEventHandler()(ctxt, *event) + + blog := map[string]interface{}{} + result := gormDB.Table("Blog").Find(&blog, "weos_id = ? ", contentEntity.ID) + if result.Error != nil { + t.Fatalf("unexpected error retreiving created blog '%s'", result.Error) + } + + if blog["title"] != payload["title"] { + t.Fatalf("expected title to be %s, got %s", payload["title"], blog["title"]) + } + + if blog["description"] != payload["description"] { + t.Fatalf("expected desription to be %s, got %s", payload["desription"], blog["desription"]) + } + + err = gormDB.Migrator().DropTable("Blog") + if err != nil { + t.Errorf("error removing table '%s' '%s'", "Blog", err) + } + }) + + t.Run("Update basic many to many relationship", func(t *testing.T) { + openAPI := `openapi: 3.0.3 +info: + title: Blog + description: Blog example + version: 1.0.0 +servers: + - url: https://prod1.weos.sh/blog/dev + description: WeOS Dev + - url: https://prod1.weos.sh/blog/v1 +components: + schemas: + Post: + type: object + properties: + title: + type: string + description: blog title + description: + type: string + Blog: + type: object + properties: + title: + type: string + description: blog title + description: + type: string + posts: + type: array + items: + $ref: "#/components/schemas/Post" +` + + loader := openapi3.NewSwaggerLoader() + swagger, err := loader.LoadSwaggerFromData([]byte(openAPI)) + if err != nil { + t.Fatal(err) + } + + schemes := rest.CreateSchema(context.Background(), echo.New(), swagger) + p, err := projections.NewProjection(context.Background(), app, schemes) + if err != nil { + t.Fatal(err) + } + + err = p.Migrate(context.Background()) + if err != nil { + t.Fatal(err) + } + + gormDB := app.DB() + + blogWeosID := ksuid.New().String() + postWeosID := ksuid.New().String() + postWeosID2 := ksuid.New().String() + gormDB.Table("Post").Create(map[string]interface{}{"weos_id": postWeosID, "title": "hugs"}) + gormDB.Table("Post").Create(map[string]interface{}{"weos_id": postWeosID2, "title": "hugs"}) + gormDB.Table("Blog").Create(map[string]interface{}{"weos_id": blogWeosID, "title": "hugs"}) + + payload := map[string]interface{}{"id": 1, "title": "testBlog", "description": "This is a create projection test", "posts": []map[string]interface{}{ + { + "id": 1, + }, + }} + contentEntity := &weos.ContentEntity{ + AggregateRoot: weos.AggregateRoot{ + BasicEntity: weos.BasicEntity{ + ID: blogWeosID, + }, + }, + Property: payload, + } + + ctxt := context.Background() + ctxt = context.WithValue(ctxt, weosContext.CONTENT_TYPE, &weosContext.ContentType{ + Name: "Blog", + }) + + event := weos.NewEntityEvent("update", contentEntity, contentEntity.ID, &payload) + p.GetEventHandler()(ctxt, *event) + + blog := map[string]interface{}{} + result := gormDB.Table("Blog").Find(&blog, "weos_id = ? ", contentEntity.ID) + if result.Error != nil { + t.Fatalf("unexpected error retreiving created blog '%s'", result.Error) + } + + if blog["title"] != payload["title"] { + t.Fatalf("expected title to be %s, got %s", payload["title"], blog["title"]) + } + + if blog["description"] != payload["description"] { + t.Fatalf("expected desription to be %s, got %s", payload["desription"], blog["desription"]) + } + + blogpost := map[string]interface{}{} + result = gormDB.Table("blog_posts").Find(&blogpost, "id = ? ", 1) + if result.Error != nil { + t.Fatalf("unexpected error retreiving created blog post relation '%s'", result.Error) + } + + if *driver == "mysql" { + id := blogpost["post_id"].(uint64) + if id != 1 { + t.Fatalf("expected post id to be %d, got %v", 1, blogpost["post_id"]) + } + } else { + if blogpost["post_id"] != int64(1) { + t.Fatalf("expected post id to be %d, got %v", 1, blogpost["post_id"]) + } + } + + //test replace associations + payload = map[string]interface{}{"id": 1, "title": "testBlog", "description": "This is a create projection test", "posts": []map[string]interface{}{ + { + "id": 2, + }, + }} + contentEntity = &weos.ContentEntity{ + AggregateRoot: weos.AggregateRoot{ + BasicEntity: weos.BasicEntity{ + ID: blogWeosID, + }, + }, + Property: payload, + } + + ctxt = context.Background() + ctxt = context.WithValue(ctxt, weosContext.CONTENT_TYPE, &weosContext.ContentType{ + Name: "Blog", + }) + + event = weos.NewEntityEvent("update", contentEntity, contentEntity.ID, &payload) + p.GetEventHandler()(ctxt, *event) + + blogposts := []map[string]interface{}{} + result = gormDB.Table("blog_posts").Find(&blogposts, "id = ? ", 1) + if result.Error != nil { + t.Fatalf("unexpected error retreiving created blog post relation '%s'", result.Error) + } + + if len(blogposts) != 1 { + t.Fatalf("expected there to be %d,blog posts got %v", 1, len(blogposts)) + } + err = gormDB.Migrator().DropTable("Blog") + if err != nil { + t.Errorf("error removing table '%s' '%s'", "Blog", err) + } + + err = gormDB.Migrator().DropTable("Post") + if err != nil { + t.Errorf("error removing table '%s' '%s'", "Blog", err) + } + + err = gormDB.Migrator().DropTable("blog_posts") + if err != nil { + t.Errorf("error removing table '%s' '%s'", "blog_posts", err) + } + + }) +} + func TestProjections_GormOperations(t *testing.T) { t.Run("Basic Create using schema", func(t *testing.T) { openAPI := `openapi: 3.0.3 @@ -909,7 +1181,7 @@ components: bytes, _ = json.Marshal(m) json.Unmarshal(bytes, &blog) - result = gormDB.Table("Blog").Where("id = ?", 1).Updates(blog) + result = gormDB.Table("Blog").Updates(blog) if result.Error != nil { t.Errorf("got error updating blog %s", result.Error) } From 190cc5846a13362d12797b872adb3acc50d8d8c6 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Fri, 7 Jan 2022 15:33:48 -0400 Subject: [PATCH 05/11] WEOS-1259 updated prjections update event function to replace associations --- projections/gorm.go | 25 +++++++++++++++++++++---- projections/projections_test.go | 12 ++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/projections/gorm.go b/projections/gorm.go index c64577e1..95cc4cb7 100644 --- a/projections/gorm.go +++ b/projections/gorm.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + ds "github.com/ompluscator/dynamic-struct" weosContext "github.com/wepala/weos-service/context" weos "github.com/wepala/weos-service/model" "golang.org/x/net/context" @@ -62,6 +63,7 @@ func (p *GORMProjection) GetEventHandler() weos.EventHandler { case "update": contentType := weosContext.GetContentType(ctx) eventPayload, ok := p.Schema[strings.Title(contentType.Name)] + mapPayload := map[string]interface{}{} if !ok { p.logger.Errorf("found no content type %s", contentType.Name) } else { @@ -69,11 +71,26 @@ func (p *GORMProjection) GetEventHandler() weos.EventHandler { if err != nil { p.logger.Errorf("error unmarshalling event '%s'", err) } - db := p.db.Table(contentType.Name).Updates(eventPayload) - if db.Error != nil { - p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) + + err = json.Unmarshal(event.Payload, &mapPayload) + if err != nil { + p.logger.Errorf("error unmarshalling event '%s'", err) + } + reader := ds.NewReader(eventPayload) + + //replace associations + for key, entity := range mapPayload { + if _, ok := entity.([]interface{}); ok { + field := reader.GetField(strings.Title(key)) + err = p.db.Debug().Model(eventPayload).Association(strings.Title(key)).Replace(field.Interface()) + if err != nil { + p.logger.Errorf("error clearing association %s for %s, got %s", strings.Title(key), contentType.Name, err) + } + } } - db = p.db.Table(contentType.Name).Updates(eventPayload) + + //update database value + db := p.db.Table(contentType.Name).Updates(eventPayload) if db.Error != nil { p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) } diff --git a/projections/projections_test.go b/projections/projections_test.go index dda4fdf8..af76ba85 100644 --- a/projections/projections_test.go +++ b/projections/projections_test.go @@ -974,7 +974,7 @@ components: gormDB.Table("Post").Create(map[string]interface{}{"weos_id": postWeosID2, "title": "hugs"}) gormDB.Table("Blog").Create(map[string]interface{}{"weos_id": blogWeosID, "title": "hugs"}) - payload := map[string]interface{}{"id": 1, "title": "testBlog", "description": "This is a create projection test", "posts": []map[string]interface{}{ + payload := map[string]interface{}{"weos_id": blogWeosID, "id": 1, "title": "testBlog", "description": "This is a create projection test", "posts": []map[string]interface{}{ { "id": 1, }, @@ -1013,22 +1013,22 @@ components: blogpost := map[string]interface{}{} result = gormDB.Table("blog_posts").Find(&blogpost, "id = ? ", 1) if result.Error != nil { - t.Fatalf("unexpected error retreiving created blog post relation '%s'", result.Error) + t.Errorf("unexpected error retreiving created blog post relation '%s'", result.Error) } if *driver == "mysql" { id := blogpost["post_id"].(uint64) if id != 1 { - t.Fatalf("expected post id to be %d, got %v", 1, blogpost["post_id"]) + t.Errorf("expected post id to be %d, got %v", 1, blogpost["post_id"]) } } else { if blogpost["post_id"] != int64(1) { - t.Fatalf("expected post id to be %d, got %v", 1, blogpost["post_id"]) + t.Errorf("expected post id to be %d, got %v", 1, blogpost["post_id"]) } } //test replace associations - payload = map[string]interface{}{"id": 1, "title": "testBlog", "description": "This is a create projection test", "posts": []map[string]interface{}{ + payload = map[string]interface{}{"id": 1, "weos_id": blogWeosID, "title": "testBlog", "description": "This is a create projection test", "posts": []map[string]interface{}{ { "id": 2, }, @@ -1057,7 +1057,7 @@ components: } if len(blogposts) != 1 { - t.Fatalf("expected there to be %d,blog posts got %v", 1, len(blogposts)) + t.Fatalf("expected there to be %d blog posts got %v", 1, len(blogposts)) } err = gormDB.Migrator().DropTable("Blog") if err != nil { From 0900eb9b7e865502eb691ec395ce01dc239c6620 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Sat, 8 Jan 2022 07:10:36 -0400 Subject: [PATCH 06/11] WEOS-1259 added comments within the update event handler --- projections/gorm.go | 1 + 1 file changed, 1 insertion(+) diff --git a/projections/gorm.go b/projections/gorm.go index 95cc4cb7..145e80d8 100644 --- a/projections/gorm.go +++ b/projections/gorm.go @@ -80,6 +80,7 @@ func (p *GORMProjection) GetEventHandler() weos.EventHandler { //replace associations for key, entity := range mapPayload { + //many to many association if _, ok := entity.([]interface{}); ok { field := reader.GetField(strings.Title(key)) err = p.db.Debug().Model(eventPayload).Association(strings.Title(key)).Replace(field.Interface()) From 2cb29ad4e0f852fae8265fa1025936e8fb0b5c2a Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Sat, 8 Jan 2022 10:59:36 -0400 Subject: [PATCH 07/11] WEOS-1135 added missing database config to the view-content feature file --- features/view-content.feature | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/features/view-content.feature b/features/view-content.feature index f7fe1037..2e66c68c 100644 --- a/features/view-content.feature +++ b/features/view-content.feature @@ -1,4 +1,3 @@ -@skipped Feature: View content Background: @@ -13,6 +12,30 @@ Feature: View content title: Blog Aggregator Rest API version: 0.1.0 description: REST API for interacting with the Blog Aggregator + x-weos-config: + logger: + level: warn + report-caller: true + formatter: json + database: + driver: sqlite3 + database: e2e.db + event-source: + - title: default + driver: service + endpoint: https://prod1.weos.sh/events/v1 + - title: event + driver: sqlite3 + database: e2e.db + databases: + - title: default + driver: sqlite3 + database: e2e.db + rest: + middleware: + - RequestID + - Recover + - ZapLogger components: schemas: Blog: From e9f4da1193cf6ca1d578053f62e5668c3f0637c3 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Sat, 8 Jan 2022 12:10:13 -0400 Subject: [PATCH 08/11] WEOS-1245 updated bdd test --- end2end_test.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/end2end_test.go b/end2end_test.go index a7f3f381..9d6654b7 100644 --- a/end2end_test.go +++ b/end2end_test.go @@ -285,8 +285,32 @@ func anErrorShouldBeReturned() error { return nil } -func blogsInTheApi(arg1 *godog.Table) error { - return godog.ErrPending +func blogsInTheApi(details *godog.Table) error { + + head := details.Rows[0].Cells + + for i := 1; i < len(details.Rows); i++ { + req := make(map[string]interface{}) + for n, cell := range details.Rows[i].Cells { + req[head[n].Value] = cell.Value + } + reqBytes, _ := json.Marshal(req) + body := bytes.NewReader(reqBytes) + var request *http.Request + + request = httptest.NewRequest("POST", "/blog", body) + + request = request.WithContext(context.TODO()) + header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + request.Header = header + request.Close = true + rec = httptest.NewRecorder() + e.ServeHTTP(rec, request) + if rec.Code != http.StatusCreated { + return fmt.Errorf("expected the status to be %d got %d", http.StatusCreated, rec.Code) + } + } + return nil } func entersInTheField(userName, value, field string) error { From 898179a983ecbc117c8eeab69461ca7f4b1901f6 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Sat, 8 Jan 2022 12:25:54 -0400 Subject: [PATCH 09/11] WEOS-1243 updated bdd test --- end2end_test.go | 39 +++++++++++++++++++++++++++++++++++ features/view-content.feature | 6 +++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/end2end_test.go b/end2end_test.go index 9d6654b7..296ea60b 100644 --- a/end2end_test.go +++ b/end2end_test.go @@ -559,6 +559,43 @@ func theIsUpdated(contentType string, details *godog.Table) error { return nil } +func theEndpointIsHit(endpoint, url string) error { + request := httptest.NewRequest(endpoint, url, nil) + request = request.WithContext(context.TODO()) + header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + request.Header = header + request.Close = true + rec = httptest.NewRecorder() + e.ServeHTTP(rec, request) + return nil +} + +func aBlogShouldBeReturned(details *godog.Table) error { + head := details.Rows[0].Cells + compare := map[string]interface{}{} + + for i := 1; i < len(details.Rows); i++ { + for n, cell := range details.Rows[i].Cells { + compare[head[n].Value] = cell.Value + } + } + + contentEntity := map[string]interface{}{} + err := json.NewDecoder(rec.Body).Decode(&contentEntity) + + if err != nil { + return err + } + + for key, value := range compare { + if contentEntity[key] != value { + return fmt.Errorf("expected %s %s %s, got %s", "Blog", key, value, contentEntity[key]) + } + } + + return nil +} + func InitializeScenario(ctx *godog.ScenarioContext) { ctx.Before(reset) //add context steps @@ -588,6 +625,8 @@ func InitializeScenario(ctx *godog.ScenarioContext) { ctx.Step(`^the "([^"]*)" is updated$`, theIsUpdated) ctx.Step(`^a header "([^"]*)" with value "([^"]*)"$`, aHeaderWithValue) ctx.Step(`^a (\d+) response should be returned$`, aResponseShouldBeReturned) + ctx.Step(`^the "([^"]*)" endpoint "([^"]*)" is hit$`, theEndpointIsHit) + ctx.Step(`^a blog should be returned$`, aBlogShouldBeReturned) } diff --git a/features/view-content.feature b/features/view-content.feature index 2e66c68c..4b6101de 100644 --- a/features/view-content.feature +++ b/features/view-content.feature @@ -169,7 +169,7 @@ Feature: View content description: Blog Deleted """ And blogs in the api - | id | entity id | sequence no | title | description | + | id | weos_id | sequence_no | title | description | | 1234 | 22xu1Xa5CS3DK1Om2tB7OBDfWAF | 2 | Blog 1 | Some Blog | | 4567 | 22xu4iw0bWMwxqbrUvjqEqu5dof | 1 | Blog 2 | Some Blog 2 | @@ -179,7 +179,7 @@ Feature: View content The blog should be retrieved using the identifier in the projection. The `ETag` header returned is a combination of the entity id and the sequence no. - When the "POST" endpoint "/blog/1234" is hit + When the "GET" endpoint "/blog/1234" is hit Then a 200 response should be returned And a blog should be returned | id | title | description | @@ -190,7 +190,7 @@ Feature: View content If the view controller gets a parameter `use_entity_id` set to true then it will use the identifier as the entity id - When the "POST" endpoint "/blog/22xu4iw0bWMwxqbrUvjqEqu5dof?use_entity_id=true" is hit + When the "GET" endpoint "/blog/22xu4iw0bWMwxqbrUvjqEqu5dof?use_entity_id=true" is hit Then a 200 response should be returned And a blog should be returned | id | title | description | From a785720e275a61f450cc23278a18db04a1cff791 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Thu, 13 Jan 2022 11:45:04 -0400 Subject: [PATCH 10/11] WEOS-1245 merged with WEOS-1130 and minor updates to tests --- end2end_test.go | 24 +++++++++++++++++++++++- projections/gorm.go | 16 +++++++++++++--- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/end2end_test.go b/end2end_test.go index b8ef6bc5..28425a73 100644 --- a/end2end_test.go +++ b/end2end_test.go @@ -506,8 +506,30 @@ func isOnTheEditScreenWithId(user, contentType, id string) error { } func theHeaderShouldBe(key, value string) error { + if key == "ETag" { + Etag := rec.Result().Header.Get(key) + idEtag, seqNoEtag := api.SplitEtag(Etag) + if Etag == "" { + return fmt.Errorf("expected the Etag to be added to header, got %s", Etag) + } + if idEtag == "" { + return fmt.Errorf("expected the Etag to contain a weos id, got %s", idEtag) + } + if seqNoEtag == "" { + return fmt.Errorf("expected the Etag to contain a sequence no, got %s", seqNoEtag) + } + return nil + } + headers := rec.HeaderMap - val := headers[value] + val := []string{} + + for k, v := range headers { + if strings.EqualFold(k, key) { + val = v + break + } + } if len(val) > 0 { if strings.EqualFold(val[0], value) { diff --git a/projections/gorm.go b/projections/gorm.go index 62ae4711..7632a946 100644 --- a/projections/gorm.go +++ b/projections/gorm.go @@ -3,12 +3,13 @@ package projections import ( "encoding/json" + "strings" + ds "github.com/ompluscator/dynamic-struct" weosContext "github.com/wepala/weos-service/context" weos "github.com/wepala/weos-service/model" "golang.org/x/net/context" "gorm.io/gorm" - "strings" ) //GORMProjection interface struct @@ -60,11 +61,18 @@ func (p *GORMProjection) GetEventHandler() weos.EventHandler { if !ok { p.logger.Errorf("found no content type %s", contentType.Name) } else { - err := json.Unmarshal(event.Payload, &eventPayload) + mapPayload := map[string]interface{}{} + err := json.Unmarshal(event.Payload, &mapPayload) + if err != nil { + p.logger.Errorf("error unmarshalling event '%s'", err) + } + mapPayload["sequence_no"] = event.Meta.SequenceNo + + bytes, _ := json.Marshal(mapPayload) + err = json.Unmarshal(bytes, &eventPayload) if err != nil { p.logger.Errorf("error unmarshalling event '%s'", err) } - eventPayload["sequence_no"] = event.Meta.SequenceNo db := p.db.Table(contentType.Name).Create(eventPayload) if db.Error != nil { p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) @@ -105,6 +113,8 @@ func (p *GORMProjection) GetEventHandler() weos.EventHandler { if db.Error != nil { p.logger.Errorf("error creating %s, got %s", contentType.Name, db.Error) } + } + } } } From a13209af3525e5cbda8213b1af0be4fa9cf3e571 Mon Sep 17 00:00:00 2001 From: Atonia Andall Date: Thu, 13 Jan 2022 12:45:27 -0400 Subject: [PATCH 11/11] WEOS-1245 updated sequence number check --- end2end_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/end2end_test.go b/end2end_test.go index 28425a73..209710c9 100644 --- a/end2end_test.go +++ b/end2end_test.go @@ -518,10 +518,14 @@ func theHeaderShouldBe(key, value string) error { if seqNoEtag == "" { return fmt.Errorf("expected the Etag to contain a sequence no, got %s", seqNoEtag) } + + if seqNoEtag != strings.Split(value, ".")[1] { + return fmt.Errorf("expected the Etag to contain a sequence no %s, got %s", strings.Split(value, ".")[1], seqNoEtag) + } return nil } - headers := rec.HeaderMap + headers := rec.Result().Header val := []string{} for k, v := range headers {