Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/WEOS-1355 - Add gorm tag for unique field on entity. Make getbyidentifiers method in projections. #108

Merged
merged 3 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ components:
url:
type: string
format: uri
x-unique: true
title:
type: string
description:
Expand Down
9 changes: 9 additions & 0 deletions controllers/rest/middleware_initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ func newSchema(ref *openapi3.Schema, logger echo.Logger) (ds.Builder, map[string
}
}

uniquebytes, _ := json.Marshal(p.Value.Extensions["x-unique"])
if len(uniquebytes) != 0 {
unique := false
json.Unmarshal(uniquebytes, &unique)
if unique {
gormParts = append(gormParts, "unique")
}
}

if strings.Contains(strings.Join(primaryKeys, " "), strings.ToLower(name)) {
gormParts = append(gormParts, "primaryKey", "size:512")
//only add NOT null if it's not already in the array to avoid issue if a user also add the field to the required array
Expand Down
17 changes: 16 additions & 1 deletion projections/gorm.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,21 @@ func (p *GORMDB) GetContentEntities(ctx context.Context, entityFactory weos.Enti
return entities, count, result.Error
}

func (p *GORMDB) GetByIdentifiers(ctxt context.Context, entityFactory weos.EntityFactory, identifiers map[string]interface{}) ([]map[string]interface{}, error) {
results := entityFactory.Builder(ctxt).Build().NewSliceOfStructs()
result := p.db.Table(entityFactory.TableName()).Scopes(ContentQuery()).Find(results, identifiers)
if result.Error != nil {
p.logger.Errorf("unexpected error retrieving created blog, got: '%s'", result.Error)
}
bytes, err := json.Marshal(results)
if err != nil {
return nil, err
}
var entities []map[string]interface{}
json.Unmarshal(bytes, &entities)
return entities, nil
}

//DateTimeChecks checks to make sure the format is correctly as well as it manipulates the date
func DateTimeCheck(entityFactory weos.EntityFactory, properties map[string]FilterProperty) (map[string]FilterProperty, error) {
var err error
Expand Down Expand Up @@ -532,7 +547,7 @@ func NewProjection(ctx context.Context, db *gorm.DB, logger weos.Log) (*GORMDB,
ContentQuery = func() func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if projection.db.Dialector.Name() == "sqlite" {
//gorm sqlite generates the query incorrectly if there are composite keys when preloading
//gorm sqlite generates the query incorrectly if there are composite keys when preloading. This may cause panics.
//https://github.com/go-gorm/gorm/issues/3585
return db
} else {
Expand Down
233 changes: 233 additions & 0 deletions projections/projections_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4129,3 +4129,236 @@ components:
t.Errorf("error removing table '%s' '%s'", "Blog", err)
}
}

func TestProjections_GetByIdentifiers(t *testing.T) {

t.Run("Get By Identifiers", 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
author:
type: string
`

api, err := rest.New(openAPI)
if err != nil {
t.Fatalf("error loading api config '%s'", err)
}

schemes := rest.CreateSchema(context.Background(), echo.New(), api.Swagger)
p, err := projections.NewProjection(context.Background(), gormDB, api.EchoInstance().Logger)
if err != nil {
t.Fatal(err)
}

deletedFields := map[string][]string{}
for name, sch := range api.Swagger.Components.Schemas {
dfs, _ := json.Marshal(sch.Value.Extensions["x-remove"])
json.Unmarshal(dfs, deletedFields[name])
}
err = p.Migrate(context.Background(), schemes, deletedFields)
if err != nil {
t.Fatal(err)
}

blogs := []map[string]interface{}{
{
"title": "once",
"description": "twice",
"author": "once",
},
{
"title": "twice",
"description": "once",
"author": "once",
},
{
"title": "once",
"description": "once",
"author": "twice",
},
}
result := gormDB.Table("Blog").Create(blogs)
if result.Error != nil {
t.Fatalf("got error creating fixtures '%s'", err)
}

ctxt := context.Background()
ctxt = context.WithValue(ctxt, weosContext.ENTITY_FACTORY, new(weos.DefaultEntityFactory).FromSchemaAndBuilder("Blog", api.Swagger.Components.Schemas["Blog"].Value, schemes["Blog"]))

blogEntityFactory := new(weos.DefaultEntityFactory).FromSchemaAndBuilder("Blog", api.Swagger.Components.Schemas["Blog"].Value, schemes["Blog"])
r, err := p.GetByIdentifiers(ctxt, blogEntityFactory, map[string]interface{}{"author": "twice"})
if err != nil {
t.Errorf("got error retrieving blogs '%s'", err)
}
if len(r) != 1 {
t.Errorf("expected to get %d blogs, got %d", 1, len(r))
}

if r[0]["title"] != blogs[2]["title"] || r[0]["description"] != blogs[2]["description"] || r[0]["author"] != blogs[2]["author"] {
t.Errorf("expected blog to be %v got %v", blogs[2], r[0])
}

r, err = p.GetByIdentifiers(ctxt, blogEntityFactory, map[string]interface{}{"description": "twice"})
if err != nil {
t.Errorf("got error retrieving blogs '%s'", err)
}
if len(r) != 1 {
t.Errorf("expected to get %d blogs, got %d", 1, len(r))
}

if r[0]["title"] != blogs[0]["title"] || r[0]["description"] != blogs[0]["description"] || r[0]["author"] != blogs[0]["author"] {
t.Errorf("expected blog to be %v got %v", blogs[2], r[0])
}

r, err = p.GetByIdentifiers(ctxt, blogEntityFactory, map[string]interface{}{"title": "once"})
if err != nil {
t.Errorf("got error retrieving blogs '%s'", err)
}
if len(r) != 2 {
t.Errorf("expected to get %d blogs, got %d", 2, len(r))
}

err = gormDB.Migrator().DropTable("Blog")
if err != nil {
t.Errorf("error removing table '%s' '%s'", "Blog", err)
}
})
}

func TestProjections_XUnique(t *testing.T) {

t.Run("Test unique fields", 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
x-unique: true
description:
type: string
author:
type: string
`

api, err := rest.New(openAPI)
if err != nil {
t.Fatalf("error loading api config '%s'", err)
}

schemes := rest.CreateSchema(context.Background(), echo.New(), api.Swagger)
p, err := projections.NewProjection(context.Background(), gormDB, api.EchoInstance().Logger)
if err != nil {
t.Fatal(err)
}

deletedFields := map[string][]string{}
for name, sch := range api.Swagger.Components.Schemas {
dfs, _ := json.Marshal(sch.Value.Extensions["x-remove"])
json.Unmarshal(dfs, deletedFields[name])
}
err = p.Migrate(context.Background(), schemes, deletedFields)
if err != nil {
t.Fatal(err)
}

blogs := []map[string]interface{}{
{
"title": "once",
"description": "twice",
"author": "once",
},
{
"title": "twice",
"description": "once",
"author": "once",
},
{
"title": "once",
"description": "once",
"author": "twice",
},
}
result := gormDB.Table("Blog").Create(blogs)
if result.Error == nil {
t.Fatalf("expected to get unique error on title 'once'")
}

})
}