diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f059fc3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +*/temp/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b152dae --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.0 + hooks: + - id: go-fmt + - id: go-imports + - id: no-go-testing + - id: go-unit-tests + # - id: validate-toml + - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook + rev: v8.0.0 + hooks: + - id: commitlint + stages: [commit-msg] + additional_dependencies: ['@commitlint/config-conventional'] diff --git a/README.md b/README.md index b5276c7..bb57243 100644 --- a/README.md +++ b/README.md @@ -10,352 +10,38 @@ It can be generated easily complex objects by using this, and maintain easily th ## Install ``` -$ go get -u github.com/bluele/factory-go/factory +$ go get -u github.com/hyuti/factory-go/factory ``` -## Usage +## Example -All of the following code on [examples](https://github.com/bluele/factory-go/tree/master/examples). +* [Define a simple factory](https://github.com/hyuti/factory-go#define-a-simple-factory) +* [Define a factory which has input type different from output type](https://github.com/hyuti/factory-go#define-a-factory-which-has-input-type-different-from-output-type) +* [Use factory with random yet realistic values](https://github.com/hyuti/factory-go#use-factory-with-random-yet-realistic-values) +* [Define a factory includes sub-factory](https://github.com/hyuti/factory-go#define-a-factory-includes-sub-factory) +* [Define a factory includes a slice for sub-factory](https://github.com/hyuti/factory-go#define-a-factory-includes-a-slice-for-sub-factory) +* [Define a factory includes sub-factory that contains self-reference](https://github.com/hyuti/factory-go#define-a-factory-includes-sub-factory-that-contains-self-reference) +* [Define a sub-factory refers to parent factory](https://github.com/hyuti/factory-go#define-a-sub-factory-refers-to-parent-factory) +* [Define a factory has input type different from output type](https://github.com/hyuti/factory-go#define-a-sub-factory-refers-to-parent-factory) -* [Define a simple factory](https://github.com/bluele/factory-go#define-a-simple-factory) -* [Use factory with random yet realistic values](https://github.com/bluele/factory-go#use-factory-with-random-yet-realistic-values) -* [Define a factory includes sub-factory](https://github.com/bluele/factory-go#define-a-factory-includes-sub-factory) -* [Define a factory includes a slice for sub-factory](https://github.com/bluele/factory-go#define-a-factory-includes-a-slice-for-sub-factory) -* [Define a factory includes sub-factory that contains self-reference](https://github.com/bluele/factory-go#define-a-factory-includes-sub-factory-that-contains-self-reference) -* [Define a sub-factory refers to parent factory](https://github.com/bluele/factory-go#define-a-sub-factory-refers-to-parent-factory) +## Features -### Define a simple factory - -Declare an factory has a set of simple attribute, and generate a fixture object. - -```go -package main - -import ( - "fmt" - "github.com/bluele/factory-go/factory" -) - -type User struct { - ID int - Name string - Location string -} - -// 'Location: "Tokyo"' is default value. -var UserFactory = factory.NewFactory( - &User{Location: "Tokyo"}, -).SeqInt("ID", func(n int) (interface{}, error) { - return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - user := args.Instance().(*User) - return fmt.Sprintf("user-%d", user.ID), nil -}) - -func main() { - for i := 0; i < 3; i++ { - user := UserFactory.MustCreate().(*User) - fmt.Println("ID:", user.ID, " Name:", user.Name, " Location:", user.Location) - } -} -``` - -Output: - -``` -ID: 1 Name: user-1 Location: Tokyo -ID: 2 Name: user-2 Location: Tokyo -ID: 3 Name: user-3 Location: Tokyo -``` - -### Use factory with random yet realistic values. - -Tests look better with random yet realistic values. For example, you can use [go-randomdata](https://github.com/Pallinder/go-randomdata) library to get them: - -```go -package main - -import ( - "fmt" - "github.com/Pallinder/go-randomdata" - "github.com/bluele/factory-go/factory" -) - -type User struct { - ID int - Name string - Location string -} - -// 'Location: "Tokyo"' is default value. -var UserFactory = factory.NewFactory( - &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { - return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - return randomdata.FullName(randomdata.RandomGender), nil -}).Attr("Location", func(args factory.Args) (interface{}, error) { - return randomdata.City(), nil -}) - -func main() { - for i := 0; i < 3; i++ { - user := UserFactory.MustCreate().(*User) - fmt.Println("ID:", user.ID, " Name:", user.Name, " Location:", user.Location) - } -} -``` - -Output: - -``` -ID: 1 Name: Benjamin Thomas Location: Burrton -ID: 2 Name: Madison Davis Location: Brandwell -ID: 3 Name: Aubrey Robinson Location: Campden -``` - -### Define a factory includes sub-factory - -```go -package main - -import ( - "fmt" - "github.com/bluele/factory-go/factory" -) - -type Group struct { - ID int - Name string -} - -type User struct { - ID int - Name string - Location string - Group *Group -} - -var GroupFactory = factory.NewFactory( - &Group{}, -).SeqInt("ID", func(n int) (interface{}, error) { - return 2 - n%2, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - group := args.Instance().(*Group) - return fmt.Sprintf("group-%d", group.ID), nil -}) - -// 'Location: "Tokyo"' is default value. -var UserFactory = factory.NewFactory( - &User{Location: "Tokyo"}, -).SeqInt("ID", func(n int) (interface{}, error) { - return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - user := args.Instance().(*User) - return fmt.Sprintf("user-%d", user.ID), nil -}).SubFactory("Group", GroupFactory) - -func main() { - for i := 0; i < 3; i++ { - user := UserFactory.MustCreate().(*User) - fmt.Println( - "ID:", user.ID, " Name:", user.Name, " Location:", user.Location, - " Group.ID:", user.Group.ID, " Group.Name", user.Group.Name) - } -} -``` - -Output: - -``` -ID: 1 Name: user-1 Location: Tokyo Group.ID: 1 Group.Name group-1 -ID: 2 Name: user-2 Location: Tokyo Group.ID: 2 Group.Name group-2 -ID: 3 Name: user-3 Location: Tokyo Group.ID: 1 Group.Name group-1 -``` - -### Define a factory includes a slice for sub-factory. - -```go -package main - -import ( - "fmt" - "github.com/bluele/factory-go/factory" -) - -type Post struct { - ID int - Content string -} - -type User struct { - ID int - Name string - Posts []*Post -} - -var PostFactory = factory.NewFactory( - &Post{}, -).SeqInt("ID", func(n int) (interface{}, error) { - return n, nil -}).Attr("Content", func(args factory.Args) (interface{}, error) { - post := args.Instance().(*Post) - return fmt.Sprintf("post-%d", post.ID), nil -}) - -var UserFactory = factory.NewFactory( - &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { - return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - user := args.Instance().(*User) - return fmt.Sprintf("user-%d", user.ID), nil -}).SubSliceFactory("Posts", PostFactory, func() int { return 3 }) - -func main() { - for i := 0; i < 3; i++ { - user := UserFactory.MustCreate().(*User) - fmt.Println("ID:", user.ID, " Name:", user.Name) - for _, post := range user.Posts { - fmt.Printf("\tPost.ID: %v Post.Content: %v\n", post.ID, post.Content) - } - } -} -``` - -Output: - -``` -ID: 1 Name: user-1 - Post.ID: 1 Post.Content: post-1 - Post.ID: 2 Post.Content: post-2 - Post.ID: 3 Post.Content: post-3 -ID: 2 Name: user-2 - Post.ID: 4 Post.Content: post-4 - Post.ID: 5 Post.Content: post-5 - Post.ID: 6 Post.Content: post-6 -ID: 3 Name: user-3 - Post.ID: 7 Post.Content: post-7 - Post.ID: 8 Post.Content: post-8 - Post.ID: 9 Post.Content: post-9 -``` - -### Define a factory includes sub-factory that contains self-reference. - -```go -package main - -import ( - "fmt" - "github.com/Pallinder/go-randomdata" - "github.com/bluele/factory-go/factory" -) - -type User struct { - ID int - Name string - CloseFriend *User -} - -var UserFactory = factory.NewFactory( - &User{}, -) - -func init() { - UserFactory.SeqInt("ID", func(n int) (interface{}, error) { - return n, nil - }).Attr("Name", func(args factory.Args) (interface{}, error) { - return randomdata.FullName(randomdata.RandomGender), nil - }).SubRecursiveFactory("CloseFriend", UserFactory, func() int { return 2 }) // recursive depth is always 2 -} - -func main() { - user := UserFactory.MustCreate().(*User) - fmt.Println("ID:", user.ID, " Name:", user.Name, - " CloseFriend.ID:", user.CloseFriend.ID, " CloseFriend.Name:", user.CloseFriend.Name) - // `user.CloseFriend.CloseFriend.CloseFriend ` depth is 3, so this value is always nil. - fmt.Printf("%v %v\n", user.CloseFriend.CloseFriend, user.CloseFriend.CloseFriend.CloseFriend) -} -``` - -Output: -``` -ID: 1 Name: Mia Williams CloseFriend.ID: 2 CloseFriend.Name: Joseph Wilson -&{3 Liam Wilson } -``` - -### Define a sub-factory refers to parent factory - -```go -package main - -import ( - "fmt" - "github.com/bluele/factory-go/factory" -) - -type User struct { - ID int - Name string - Group *Group -} - -type Group struct { - ID int - Name string - Users []*User -} - -var UserFactory = factory.NewFactory( - &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { - return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - user := args.Instance().(*User) - return fmt.Sprintf("user-%d", user.ID), nil -}).Attr("Group", func(args factory.Args) (interface{}, error) { - if parent := args.Parent(); parent != nil { - // if args have parent, use it. - return parent.Instance(), nil - } - return nil, nil -}) - -var GroupFactory = factory.NewFactory( - &Group{}, -).SeqInt("ID", func(n int) (interface{}, error) { - return 2 - n%2, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { - group := args.Instance().(*Group) - return fmt.Sprintf("group-%d", group.ID), nil -}).SubSliceFactory("Users", UserFactory, func() int { return 3 }) - -func main() { - group := GroupFactory.MustCreate().(*Group) - fmt.Println("Group.ID:", group.ID) - for _, user := range group.Users { - fmt.Println("\tUser.ID:", user.ID, " User.Name:", user.Name, " User.Group.ID:", user.Group.ID) - } -} -``` - -Output: -``` -Group.ID: 1 - User.ID: 1 User.Name: user-1 User.Group.ID: 1 - User.ID: 2 User.Name: user-2 User.Group.ID: 1 - User.ID: 3 User.Name: user-3 User.Group.ID: 1 -``` +## Roadmap +- 🚧️ Add Features section for README +- 🚧️ Bulk create feature +- 🚧️ Bulk update feature +- 🚧️ Bulk delete feature ## Persistent models -Currently this project has no support for directly integration with ORM like [gorm](https://github.com/jinzhu/gorm), so you need to do manually. +Here is a list of integration examples with some popular ORM libraries (Please pay atttention this is not an official integration): +- [gorm](https://github.com/hyuti/factory-go/blob/master/examples/gorm_integration.go) (what is [gorm](https://github.com/jinzhu/gorm)) +- [ent](https://github.com/hyuti/factory-go/blob/master/examples/ent-integration/ent/factory.go) (what is [ent](https://github.com/ent/ent)) -Here is an example: https://github.com/bluele/factory-go/blob/master/examples/gorm_integration.go +# Contributors +The [original repo](https://github.com/bluele/factory-go) of this project has not been actived for a long time, so this is a fork and maintained by me. Any contributions will be appreciated. # Author **Jun Kimura** - -* * diff --git a/examples/create_with_option.go b/examples/create-with-option/main.go similarity index 67% rename from examples/create_with_option.go rename to examples/create-with-option/main.go index e13469a..c575dd7 100644 --- a/examples/create_with_option.go +++ b/examples/create-with-option/main.go @@ -2,7 +2,8 @@ package main import ( "fmt" - "github.com/bluele/factory-go/factory" + + "github.com/hyuti/factory-go/factory" ) type Group struct { @@ -16,15 +17,15 @@ type User struct { var UserFactory = factory.NewFactory( &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil }) func main() { for i := 1; i <= 3; i++ { - user := UserFactory.MustCreateWithOption(map[string]interface{}{ + user := UserFactory.MustCreateWithOption(map[string]any{ "Groups": []*Group{ - &Group{i}, &Group{i + 1}, + {i}, {i + 1}, }, }).(*User) fmt.Println("ID:", user.ID) diff --git a/examples/ent-integration/ent/book.go b/examples/ent-integration/ent/book.go new file mode 100644 index 0000000..03619f3 --- /dev/null +++ b/examples/ent-integration/ent/book.go @@ -0,0 +1,122 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent/dialect/sql" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" +) + +// Book is the model entity for the Book schema. +type Book struct { + config + // ID of the ent. + ID int `json:"id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the BookQuery when eager-loading is set. + Edges BookEdges `json:"edges"` + customer_books *int +} + +// BookEdges holds the relations/edges for other nodes in the graph. +type BookEdges struct { + // Owner holds the value of the owner edge. + Owner *Customer `json:"owner,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// OwnerOrErr returns the Owner value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e BookEdges) OwnerOrErr() (*Customer, error) { + if e.loadedTypes[0] { + if e.Owner == nil { + // Edge was loaded but was not found. + return nil, &NotFoundError{label: customer.Label} + } + return e.Owner, nil + } + return nil, &NotLoadedError{edge: "owner"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Book) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case book.FieldID: + values[i] = new(sql.NullInt64) + case book.ForeignKeys[0]: // customer_books + values[i] = new(sql.NullInt64) + default: + return nil, fmt.Errorf("unexpected column %q for type Book", columns[i]) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Book fields. +func (b *Book) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case book.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + b.ID = int(value.Int64) + case book.ForeignKeys[0]: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field customer_books", value) + } else if value.Valid { + b.customer_books = new(int) + *b.customer_books = int(value.Int64) + } + } + } + return nil +} + +// QueryOwner queries the "owner" edge of the Book entity. +func (b *Book) QueryOwner() *CustomerQuery { + return NewBookClient(b.config).QueryOwner(b) +} + +// Update returns a builder for updating this Book. +// Note that you need to call Book.Unwrap() before calling this method if this Book +// was returned from a transaction, and the transaction was committed or rolled back. +func (b *Book) Update() *BookUpdateOne { + return NewBookClient(b.config).UpdateOne(b) +} + +// Unwrap unwraps the Book entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (b *Book) Unwrap() *Book { + _tx, ok := b.config.driver.(*txDriver) + if !ok { + panic("ent: Book is not a transactional entity") + } + b.config.driver = _tx.drv + return b +} + +// String implements the fmt.Stringer. +func (b *Book) String() string { + var builder strings.Builder + builder.WriteString("Book(") + builder.WriteString(fmt.Sprintf("id=%v", b.ID)) + builder.WriteByte(')') + return builder.String() +} + +// Books is a parsable slice of Book. +type Books []*Book diff --git a/examples/ent-integration/ent/book/book.go b/examples/ent-integration/ent/book/book.go new file mode 100644 index 0000000..8940cb9 --- /dev/null +++ b/examples/ent-integration/ent/book/book.go @@ -0,0 +1,47 @@ +// Code generated by ent, DO NOT EDIT. + +package book + +const ( + // Label holds the string label denoting the book type in the database. + Label = "book" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // EdgeOwner holds the string denoting the owner edge name in mutations. + EdgeOwner = "owner" + // Table holds the table name of the book in the database. + Table = "books" + // OwnerTable is the table that holds the owner relation/edge. + OwnerTable = "books" + // OwnerInverseTable is the table name for the Customer entity. + // It exists in this package in order to avoid circular dependency with the "customer" package. + OwnerInverseTable = "customers" + // OwnerColumn is the table column denoting the owner relation/edge. + OwnerColumn = "customer_books" +) + +// Columns holds all SQL columns for book fields. +var Columns = []string{ + FieldID, +} + +// ForeignKeys holds the SQL foreign-keys that are owned by the "books" +// table and are not defined as standalone fields in the schema. +var ForeignKeys = []string{ + "customer_books", +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + for i := range ForeignKeys { + if column == ForeignKeys[i] { + return true + } + } + return false +} diff --git a/examples/ent-integration/ent/book/where.go b/examples/ent-integration/ent/book/where.go new file mode 100644 index 0000000..78e7ad7 --- /dev/null +++ b/examples/ent-integration/ent/book/where.go @@ -0,0 +1,113 @@ +// Code generated by ent, DO NOT EDIT. + +package book + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.Book { + return predicate.Book(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.Book { + return predicate.Book(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.Book { + return predicate.Book(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.Book { + return predicate.Book(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.Book { + return predicate.Book(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.Book { + return predicate.Book(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.Book { + return predicate.Book(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.Book { + return predicate.Book(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.Book { + return predicate.Book(sql.FieldLTE(FieldID, id)) +} + +// HasOwner applies the HasEdge predicate on the "owner" edge. +func HasOwner() predicate.Book { + return predicate.Book(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, OwnerTable, OwnerColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasOwnerWith applies the HasEdge predicate on the "owner" edge with a given conditions (other predicates). +func HasOwnerWith(preds ...predicate.Customer) predicate.Book { + return predicate.Book(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(OwnerInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, OwnerTable, OwnerColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Book) predicate.Book { + return predicate.Book(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for _, p := range predicates { + p(s1) + } + s.Where(s1.P()) + }) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Book) predicate.Book { + return predicate.Book(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for i, p := range predicates { + if i > 0 { + s1.Or() + } + p(s1) + } + s.Where(s1.P()) + }) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Book) predicate.Book { + return predicate.Book(func(s *sql.Selector) { + p(s.Not()) + }) +} diff --git a/examples/ent-integration/ent/book_create.go b/examples/ent-integration/ent/book_create.go new file mode 100644 index 0000000..c4c473c --- /dev/null +++ b/examples/ent-integration/ent/book_create.go @@ -0,0 +1,202 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" +) + +// BookCreate is the builder for creating a Book entity. +type BookCreate struct { + config + mutation *BookMutation + hooks []Hook +} + +// SetOwnerID sets the "owner" edge to the Customer entity by ID. +func (bc *BookCreate) SetOwnerID(id int) *BookCreate { + bc.mutation.SetOwnerID(id) + return bc +} + +// SetNillableOwnerID sets the "owner" edge to the Customer entity by ID if the given value is not nil. +func (bc *BookCreate) SetNillableOwnerID(id *int) *BookCreate { + if id != nil { + bc = bc.SetOwnerID(*id) + } + return bc +} + +// SetOwner sets the "owner" edge to the Customer entity. +func (bc *BookCreate) SetOwner(c *Customer) *BookCreate { + return bc.SetOwnerID(c.ID) +} + +// Mutation returns the BookMutation object of the builder. +func (bc *BookCreate) Mutation() *BookMutation { + return bc.mutation +} + +// Save creates the Book in the database. +func (bc *BookCreate) Save(ctx context.Context) (*Book, error) { + return withHooks[*Book, BookMutation](ctx, bc.sqlSave, bc.mutation, bc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (bc *BookCreate) SaveX(ctx context.Context) *Book { + v, err := bc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (bc *BookCreate) Exec(ctx context.Context) error { + _, err := bc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bc *BookCreate) ExecX(ctx context.Context) { + if err := bc.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (bc *BookCreate) check() error { + return nil +} + +func (bc *BookCreate) sqlSave(ctx context.Context) (*Book, error) { + if err := bc.check(); err != nil { + return nil, err + } + _node, _spec := bc.createSpec() + if err := sqlgraph.CreateNode(ctx, bc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int(id) + bc.mutation.id = &_node.ID + bc.mutation.done = true + return _node, nil +} + +func (bc *BookCreate) createSpec() (*Book, *sqlgraph.CreateSpec) { + var ( + _node = &Book{config: bc.config} + _spec = sqlgraph.NewCreateSpec(book.Table, sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt)) + ) + if nodes := bc.mutation.OwnerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: book.OwnerTable, + Columns: []string{book.OwnerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.customer_books = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// BookCreateBulk is the builder for creating many Book entities in bulk. +type BookCreateBulk struct { + config + builders []*BookCreate +} + +// Save creates the Book entities in the database. +func (bcb *BookCreateBulk) Save(ctx context.Context) ([]*Book, error) { + specs := make([]*sqlgraph.CreateSpec, len(bcb.builders)) + nodes := make([]*Book, len(bcb.builders)) + mutators := make([]Mutator, len(bcb.builders)) + for i := range bcb.builders { + func(i int, root context.Context) { + builder := bcb.builders[i] + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*BookMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + nodes[i], specs[i] = builder.createSpec() + var err error + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, bcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, bcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, bcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (bcb *BookCreateBulk) SaveX(ctx context.Context) []*Book { + v, err := bcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (bcb *BookCreateBulk) Exec(ctx context.Context) error { + _, err := bcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bcb *BookCreateBulk) ExecX(ctx context.Context) { + if err := bcb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/examples/ent-integration/ent/book_delete.go b/examples/ent-integration/ent/book_delete.go new file mode 100644 index 0000000..eec499e --- /dev/null +++ b/examples/ent-integration/ent/book_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// BookDelete is the builder for deleting a Book entity. +type BookDelete struct { + config + hooks []Hook + mutation *BookMutation +} + +// Where appends a list predicates to the BookDelete builder. +func (bd *BookDelete) Where(ps ...predicate.Book) *BookDelete { + bd.mutation.Where(ps...) + return bd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (bd *BookDelete) Exec(ctx context.Context) (int, error) { + return withHooks[int, BookMutation](ctx, bd.sqlExec, bd.mutation, bd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (bd *BookDelete) ExecX(ctx context.Context) int { + n, err := bd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (bd *BookDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(book.Table, sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt)) + if ps := bd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, bd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + bd.mutation.done = true + return affected, err +} + +// BookDeleteOne is the builder for deleting a single Book entity. +type BookDeleteOne struct { + bd *BookDelete +} + +// Where appends a list predicates to the BookDelete builder. +func (bdo *BookDeleteOne) Where(ps ...predicate.Book) *BookDeleteOne { + bdo.bd.mutation.Where(ps...) + return bdo +} + +// Exec executes the deletion query. +func (bdo *BookDeleteOne) Exec(ctx context.Context) error { + n, err := bdo.bd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{book.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (bdo *BookDeleteOne) ExecX(ctx context.Context) { + if err := bdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/examples/ent-integration/ent/book_query.go b/examples/ent-integration/ent/book_query.go new file mode 100644 index 0000000..3d4a586 --- /dev/null +++ b/examples/ent-integration/ent/book_query.go @@ -0,0 +1,591 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// BookQuery is the builder for querying Book entities. +type BookQuery struct { + config + ctx *QueryContext + order []OrderFunc + inters []Interceptor + predicates []predicate.Book + withOwner *CustomerQuery + withFKs bool + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the BookQuery builder. +func (bq *BookQuery) Where(ps ...predicate.Book) *BookQuery { + bq.predicates = append(bq.predicates, ps...) + return bq +} + +// Limit the number of records to be returned by this query. +func (bq *BookQuery) Limit(limit int) *BookQuery { + bq.ctx.Limit = &limit + return bq +} + +// Offset to start from. +func (bq *BookQuery) Offset(offset int) *BookQuery { + bq.ctx.Offset = &offset + return bq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (bq *BookQuery) Unique(unique bool) *BookQuery { + bq.ctx.Unique = &unique + return bq +} + +// Order specifies how the records should be ordered. +func (bq *BookQuery) Order(o ...OrderFunc) *BookQuery { + bq.order = append(bq.order, o...) + return bq +} + +// QueryOwner chains the current query on the "owner" edge. +func (bq *BookQuery) QueryOwner() *CustomerQuery { + query := (&CustomerClient{config: bq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := bq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := bq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(book.Table, book.FieldID, selector), + sqlgraph.To(customer.Table, customer.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, book.OwnerTable, book.OwnerColumn), + ) + fromU = sqlgraph.SetNeighbors(bq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Book entity from the query. +// Returns a *NotFoundError when no Book was found. +func (bq *BookQuery) First(ctx context.Context) (*Book, error) { + nodes, err := bq.Limit(1).All(setContextOp(ctx, bq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{book.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (bq *BookQuery) FirstX(ctx context.Context) *Book { + node, err := bq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Book ID from the query. +// Returns a *NotFoundError when no Book ID was found. +func (bq *BookQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = bq.Limit(1).IDs(setContextOp(ctx, bq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{book.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (bq *BookQuery) FirstIDX(ctx context.Context) int { + id, err := bq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Book entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Book entity is found. +// Returns a *NotFoundError when no Book entities are found. +func (bq *BookQuery) Only(ctx context.Context) (*Book, error) { + nodes, err := bq.Limit(2).All(setContextOp(ctx, bq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{book.Label} + default: + return nil, &NotSingularError{book.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (bq *BookQuery) OnlyX(ctx context.Context) *Book { + node, err := bq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Book ID in the query. +// Returns a *NotSingularError when more than one Book ID is found. +// Returns a *NotFoundError when no entities are found. +func (bq *BookQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = bq.Limit(2).IDs(setContextOp(ctx, bq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{book.Label} + default: + err = &NotSingularError{book.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (bq *BookQuery) OnlyIDX(ctx context.Context) int { + id, err := bq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Books. +func (bq *BookQuery) All(ctx context.Context) ([]*Book, error) { + ctx = setContextOp(ctx, bq.ctx, "All") + if err := bq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Book, *BookQuery]() + return withInterceptors[[]*Book](ctx, bq, qr, bq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (bq *BookQuery) AllX(ctx context.Context) []*Book { + nodes, err := bq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Book IDs. +func (bq *BookQuery) IDs(ctx context.Context) (ids []int, err error) { + if bq.ctx.Unique == nil && bq.path != nil { + bq.Unique(true) + } + ctx = setContextOp(ctx, bq.ctx, "IDs") + if err = bq.Select(book.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (bq *BookQuery) IDsX(ctx context.Context) []int { + ids, err := bq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (bq *BookQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, bq.ctx, "Count") + if err := bq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, bq, querierCount[*BookQuery](), bq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (bq *BookQuery) CountX(ctx context.Context) int { + count, err := bq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (bq *BookQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, bq.ctx, "Exist") + switch _, err := bq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (bq *BookQuery) ExistX(ctx context.Context) bool { + exist, err := bq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the BookQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (bq *BookQuery) Clone() *BookQuery { + if bq == nil { + return nil + } + return &BookQuery{ + config: bq.config, + ctx: bq.ctx.Clone(), + order: append([]OrderFunc{}, bq.order...), + inters: append([]Interceptor{}, bq.inters...), + predicates: append([]predicate.Book{}, bq.predicates...), + withOwner: bq.withOwner.Clone(), + // clone intermediate query. + sql: bq.sql.Clone(), + path: bq.path, + } +} + +// WithOwner tells the query-builder to eager-load the nodes that are connected to +// the "owner" edge. The optional arguments are used to configure the query builder of the edge. +func (bq *BookQuery) WithOwner(opts ...func(*CustomerQuery)) *BookQuery { + query := (&CustomerClient{config: bq.config}).Query() + for _, opt := range opts { + opt(query) + } + bq.withOwner = query + return bq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +func (bq *BookQuery) GroupBy(field string, fields ...string) *BookGroupBy { + bq.ctx.Fields = append([]string{field}, fields...) + grbuild := &BookGroupBy{build: bq} + grbuild.flds = &bq.ctx.Fields + grbuild.label = book.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +func (bq *BookQuery) Select(fields ...string) *BookSelect { + bq.ctx.Fields = append(bq.ctx.Fields, fields...) + sbuild := &BookSelect{BookQuery: bq} + sbuild.label = book.Label + sbuild.flds, sbuild.scan = &bq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a BookSelect configured with the given aggregations. +func (bq *BookQuery) Aggregate(fns ...AggregateFunc) *BookSelect { + return bq.Select().Aggregate(fns...) +} + +func (bq *BookQuery) prepareQuery(ctx context.Context) error { + for _, inter := range bq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, bq); err != nil { + return err + } + } + } + for _, f := range bq.ctx.Fields { + if !book.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if bq.path != nil { + prev, err := bq.path(ctx) + if err != nil { + return err + } + bq.sql = prev + } + return nil +} + +func (bq *BookQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Book, error) { + var ( + nodes = []*Book{} + withFKs = bq.withFKs + _spec = bq.querySpec() + loadedTypes = [1]bool{ + bq.withOwner != nil, + } + ) + if bq.withOwner != nil { + withFKs = true + } + if withFKs { + _spec.Node.Columns = append(_spec.Node.Columns, book.ForeignKeys...) + } + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Book).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Book{config: bq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, bq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := bq.withOwner; query != nil { + if err := bq.loadOwner(ctx, query, nodes, nil, + func(n *Book, e *Customer) { n.Edges.Owner = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (bq *BookQuery) loadOwner(ctx context.Context, query *CustomerQuery, nodes []*Book, init func(*Book), assign func(*Book, *Customer)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Book) + for i := range nodes { + if nodes[i].customer_books == nil { + continue + } + fk := *nodes[i].customer_books + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(customer.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "customer_books" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (bq *BookQuery) sqlCount(ctx context.Context) (int, error) { + _spec := bq.querySpec() + _spec.Node.Columns = bq.ctx.Fields + if len(bq.ctx.Fields) > 0 { + _spec.Unique = bq.ctx.Unique != nil && *bq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, bq.driver, _spec) +} + +func (bq *BookQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(book.Table, book.Columns, sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt)) + _spec.From = bq.sql + if unique := bq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if bq.path != nil { + _spec.Unique = true + } + if fields := bq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, book.FieldID) + for i := range fields { + if fields[i] != book.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := bq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := bq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := bq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := bq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (bq *BookQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(bq.driver.Dialect()) + t1 := builder.Table(book.Table) + columns := bq.ctx.Fields + if len(columns) == 0 { + columns = book.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if bq.sql != nil { + selector = bq.sql + selector.Select(selector.Columns(columns...)...) + } + if bq.ctx.Unique != nil && *bq.ctx.Unique { + selector.Distinct() + } + for _, p := range bq.predicates { + p(selector) + } + for _, p := range bq.order { + p(selector) + } + if offset := bq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := bq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// BookGroupBy is the group-by builder for Book entities. +type BookGroupBy struct { + selector + build *BookQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (bgb *BookGroupBy) Aggregate(fns ...AggregateFunc) *BookGroupBy { + bgb.fns = append(bgb.fns, fns...) + return bgb +} + +// Scan applies the selector query and scans the result into the given value. +func (bgb *BookGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, bgb.build.ctx, "GroupBy") + if err := bgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BookQuery, *BookGroupBy](ctx, bgb.build, bgb, bgb.build.inters, v) +} + +func (bgb *BookGroupBy) sqlScan(ctx context.Context, root *BookQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(bgb.fns)) + for _, fn := range bgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*bgb.flds)+len(bgb.fns)) + for _, f := range *bgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*bgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := bgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// BookSelect is the builder for selecting fields of Book entities. +type BookSelect struct { + *BookQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (bs *BookSelect) Aggregate(fns ...AggregateFunc) *BookSelect { + bs.fns = append(bs.fns, fns...) + return bs +} + +// Scan applies the selector query and scans the result into the given value. +func (bs *BookSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, bs.ctx, "Select") + if err := bs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BookQuery, *BookSelect](ctx, bs.BookQuery, bs, bs.inters, v) +} + +func (bs *BookSelect) sqlScan(ctx context.Context, root *BookQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(bs.fns)) + for _, fn := range bs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*bs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := bs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/examples/ent-integration/ent/book_update.go b/examples/ent-integration/ent/book_update.go new file mode 100644 index 0000000..daf3d54 --- /dev/null +++ b/examples/ent-integration/ent/book_update.go @@ -0,0 +1,284 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// BookUpdate is the builder for updating Book entities. +type BookUpdate struct { + config + hooks []Hook + mutation *BookMutation +} + +// Where appends a list predicates to the BookUpdate builder. +func (bu *BookUpdate) Where(ps ...predicate.Book) *BookUpdate { + bu.mutation.Where(ps...) + return bu +} + +// SetOwnerID sets the "owner" edge to the Customer entity by ID. +func (bu *BookUpdate) SetOwnerID(id int) *BookUpdate { + bu.mutation.SetOwnerID(id) + return bu +} + +// SetNillableOwnerID sets the "owner" edge to the Customer entity by ID if the given value is not nil. +func (bu *BookUpdate) SetNillableOwnerID(id *int) *BookUpdate { + if id != nil { + bu = bu.SetOwnerID(*id) + } + return bu +} + +// SetOwner sets the "owner" edge to the Customer entity. +func (bu *BookUpdate) SetOwner(c *Customer) *BookUpdate { + return bu.SetOwnerID(c.ID) +} + +// Mutation returns the BookMutation object of the builder. +func (bu *BookUpdate) Mutation() *BookMutation { + return bu.mutation +} + +// ClearOwner clears the "owner" edge to the Customer entity. +func (bu *BookUpdate) ClearOwner() *BookUpdate { + bu.mutation.ClearOwner() + return bu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (bu *BookUpdate) Save(ctx context.Context) (int, error) { + return withHooks[int, BookMutation](ctx, bu.sqlSave, bu.mutation, bu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (bu *BookUpdate) SaveX(ctx context.Context) int { + affected, err := bu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (bu *BookUpdate) Exec(ctx context.Context) error { + _, err := bu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (bu *BookUpdate) ExecX(ctx context.Context) { + if err := bu.Exec(ctx); err != nil { + panic(err) + } +} + +func (bu *BookUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := sqlgraph.NewUpdateSpec(book.Table, book.Columns, sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt)) + if ps := bu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if bu.mutation.OwnerCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: book.OwnerTable, + Columns: []string{book.OwnerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := bu.mutation.OwnerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: book.OwnerTable, + Columns: []string{book.OwnerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, bu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{book.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + bu.mutation.done = true + return n, nil +} + +// BookUpdateOne is the builder for updating a single Book entity. +type BookUpdateOne struct { + config + fields []string + hooks []Hook + mutation *BookMutation +} + +// SetOwnerID sets the "owner" edge to the Customer entity by ID. +func (buo *BookUpdateOne) SetOwnerID(id int) *BookUpdateOne { + buo.mutation.SetOwnerID(id) + return buo +} + +// SetNillableOwnerID sets the "owner" edge to the Customer entity by ID if the given value is not nil. +func (buo *BookUpdateOne) SetNillableOwnerID(id *int) *BookUpdateOne { + if id != nil { + buo = buo.SetOwnerID(*id) + } + return buo +} + +// SetOwner sets the "owner" edge to the Customer entity. +func (buo *BookUpdateOne) SetOwner(c *Customer) *BookUpdateOne { + return buo.SetOwnerID(c.ID) +} + +// Mutation returns the BookMutation object of the builder. +func (buo *BookUpdateOne) Mutation() *BookMutation { + return buo.mutation +} + +// ClearOwner clears the "owner" edge to the Customer entity. +func (buo *BookUpdateOne) ClearOwner() *BookUpdateOne { + buo.mutation.ClearOwner() + return buo +} + +// Where appends a list predicates to the BookUpdate builder. +func (buo *BookUpdateOne) Where(ps ...predicate.Book) *BookUpdateOne { + buo.mutation.Where(ps...) + return buo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (buo *BookUpdateOne) Select(field string, fields ...string) *BookUpdateOne { + buo.fields = append([]string{field}, fields...) + return buo +} + +// Save executes the query and returns the updated Book entity. +func (buo *BookUpdateOne) Save(ctx context.Context) (*Book, error) { + return withHooks[*Book, BookMutation](ctx, buo.sqlSave, buo.mutation, buo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (buo *BookUpdateOne) SaveX(ctx context.Context) *Book { + node, err := buo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (buo *BookUpdateOne) Exec(ctx context.Context) error { + _, err := buo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (buo *BookUpdateOne) ExecX(ctx context.Context) { + if err := buo.Exec(ctx); err != nil { + panic(err) + } +} + +func (buo *BookUpdateOne) sqlSave(ctx context.Context) (_node *Book, err error) { + _spec := sqlgraph.NewUpdateSpec(book.Table, book.Columns, sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt)) + id, ok := buo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Book.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := buo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, book.FieldID) + for _, f := range fields { + if !book.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != book.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := buo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if buo.mutation.OwnerCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: book.OwnerTable, + Columns: []string{book.OwnerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := buo.mutation.OwnerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: book.OwnerTable, + Columns: []string{book.OwnerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Book{config: buo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, buo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{book.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + buo.mutation.done = true + return _node, nil +} diff --git a/examples/ent-integration/ent/client.go b/examples/ent-integration/ent/client.go new file mode 100644 index 0000000..d6feed1 --- /dev/null +++ b/examples/ent-integration/ent/client.go @@ -0,0 +1,477 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "log" + + "github.com/hyuti/factory-go/examples/ent-integration/ent/migrate" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" +) + +// Client is the client that holds all ent builders. +type Client struct { + config + // Schema is the client for creating, migrating and dropping schema. + Schema *migrate.Schema + // Book is the client for interacting with the Book builders. + Book *BookClient + // Customer is the client for interacting with the Customer builders. + Customer *CustomerClient +} + +// NewClient creates a new client configured with the given options. +func NewClient(opts ...Option) *Client { + cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}} + cfg.options(opts...) + client := &Client{config: cfg} + client.init() + return client +} + +func (c *Client) init() { + c.Schema = migrate.NewSchema(c.driver) + c.Book = NewBookClient(c.config) + c.Customer = NewCustomerClient(c.config) +} + +type ( + // config is the configuration for the client and its builder. + config struct { + // driver used for executing database requests. + driver dialect.Driver + // debug enable a debug logging. + debug bool + // log used for logging on debug mode. + log func(...any) + // hooks to execute on mutations. + hooks *hooks + // interceptors to execute on queries. + inters *inters + } + // Option function to configure the client. + Option func(*config) +) + +// options applies the options on the config object. +func (c *config) options(opts ...Option) { + for _, opt := range opts { + opt(c) + } + if c.debug { + c.driver = dialect.Debug(c.driver, c.log) + } +} + +// Debug enables debug logging on the ent.Driver. +func Debug() Option { + return func(c *config) { + c.debug = true + } +} + +// Log sets the logging function for debug mode. +func Log(fn func(...any)) Option { + return func(c *config) { + c.log = fn + } +} + +// Driver configures the client driver. +func Driver(driver dialect.Driver) Option { + return func(c *config) { + c.driver = driver + } +} + +// Open opens a database/sql.DB specified by the driver name and +// the data source name, and returns a new client attached to it. +// Optional parameters can be added for configuring the client. +func Open(driverName, dataSourceName string, options ...Option) (*Client, error) { + switch driverName { + case dialect.MySQL, dialect.Postgres, dialect.SQLite: + drv, err := sql.Open(driverName, dataSourceName) + if err != nil { + return nil, err + } + return NewClient(append(options, Driver(drv))...), nil + default: + return nil, fmt.Errorf("unsupported driver: %q", driverName) + } +} + +// Tx returns a new transactional client. The provided context +// is used until the transaction is committed or rolled back. +func (c *Client) Tx(ctx context.Context) (*Tx, error) { + if _, ok := c.driver.(*txDriver); ok { + return nil, errors.New("ent: cannot start a transaction within a transaction") + } + tx, err := newTx(ctx, c.driver) + if err != nil { + return nil, fmt.Errorf("ent: starting a transaction: %w", err) + } + cfg := c.config + cfg.driver = tx + return &Tx{ + ctx: ctx, + config: cfg, + Book: NewBookClient(cfg), + Customer: NewCustomerClient(cfg), + }, nil +} + +// BeginTx returns a transactional client with specified options. +func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { + if _, ok := c.driver.(*txDriver); ok { + return nil, errors.New("ent: cannot start a transaction within a transaction") + } + tx, err := c.driver.(interface { + BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) + }).BeginTx(ctx, opts) + if err != nil { + return nil, fmt.Errorf("ent: starting a transaction: %w", err) + } + cfg := c.config + cfg.driver = &txDriver{tx: tx, drv: c.driver} + return &Tx{ + ctx: ctx, + config: cfg, + Book: NewBookClient(cfg), + Customer: NewCustomerClient(cfg), + }, nil +} + +// Debug returns a new debug-client. It's used to get verbose logging on specific operations. +// +// client.Debug(). +// Book. +// Query(). +// Count(ctx) +func (c *Client) Debug() *Client { + if c.debug { + return c + } + cfg := c.config + cfg.driver = dialect.Debug(c.driver, c.log) + client := &Client{config: cfg} + client.init() + return client +} + +// Close closes the database connection and prevents new queries from starting. +func (c *Client) Close() error { + return c.driver.Close() +} + +// Use adds the mutation hooks to all the entity clients. +// In order to add hooks to a specific client, call: `client.Node.Use(...)`. +func (c *Client) Use(hooks ...Hook) { + c.Book.Use(hooks...) + c.Customer.Use(hooks...) +} + +// Intercept adds the query interceptors to all the entity clients. +// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. +func (c *Client) Intercept(interceptors ...Interceptor) { + c.Book.Intercept(interceptors...) + c.Customer.Intercept(interceptors...) +} + +// Mutate implements the ent.Mutator interface. +func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { + switch m := m.(type) { + case *BookMutation: + return c.Book.mutate(ctx, m) + case *CustomerMutation: + return c.Customer.mutate(ctx, m) + default: + return nil, fmt.Errorf("ent: unknown mutation type %T", m) + } +} + +// BookClient is a client for the Book schema. +type BookClient struct { + config +} + +// NewBookClient returns a client for the Book from the given config. +func NewBookClient(c config) *BookClient { + return &BookClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `book.Hooks(f(g(h())))`. +func (c *BookClient) Use(hooks ...Hook) { + c.hooks.Book = append(c.hooks.Book, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `book.Intercept(f(g(h())))`. +func (c *BookClient) Intercept(interceptors ...Interceptor) { + c.inters.Book = append(c.inters.Book, interceptors...) +} + +// Create returns a builder for creating a Book entity. +func (c *BookClient) Create() *BookCreate { + mutation := newBookMutation(c.config, OpCreate) + return &BookCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Book entities. +func (c *BookClient) CreateBulk(builders ...*BookCreate) *BookCreateBulk { + return &BookCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Book. +func (c *BookClient) Update() *BookUpdate { + mutation := newBookMutation(c.config, OpUpdate) + return &BookUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *BookClient) UpdateOne(b *Book) *BookUpdateOne { + mutation := newBookMutation(c.config, OpUpdateOne, withBook(b)) + return &BookUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *BookClient) UpdateOneID(id int) *BookUpdateOne { + mutation := newBookMutation(c.config, OpUpdateOne, withBookID(id)) + return &BookUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Book. +func (c *BookClient) Delete() *BookDelete { + mutation := newBookMutation(c.config, OpDelete) + return &BookDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *BookClient) DeleteOne(b *Book) *BookDeleteOne { + return c.DeleteOneID(b.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *BookClient) DeleteOneID(id int) *BookDeleteOne { + builder := c.Delete().Where(book.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &BookDeleteOne{builder} +} + +// Query returns a query builder for Book. +func (c *BookClient) Query() *BookQuery { + return &BookQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeBook}, + inters: c.Interceptors(), + } +} + +// Get returns a Book entity by its id. +func (c *BookClient) Get(ctx context.Context, id int) (*Book, error) { + return c.Query().Where(book.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *BookClient) GetX(ctx context.Context, id int) *Book { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryOwner queries the owner edge of a Book. +func (c *BookClient) QueryOwner(b *Book) *CustomerQuery { + query := (&CustomerClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := b.ID + step := sqlgraph.NewStep( + sqlgraph.From(book.Table, book.FieldID, id), + sqlgraph.To(customer.Table, customer.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, book.OwnerTable, book.OwnerColumn), + ) + fromV = sqlgraph.Neighbors(b.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *BookClient) Hooks() []Hook { + return c.hooks.Book +} + +// Interceptors returns the client interceptors. +func (c *BookClient) Interceptors() []Interceptor { + return c.inters.Book +} + +func (c *BookClient) mutate(ctx context.Context, m *BookMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&BookCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&BookUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&BookUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&BookDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Book mutation op: %q", m.Op()) + } +} + +// CustomerClient is a client for the Customer schema. +type CustomerClient struct { + config +} + +// NewCustomerClient returns a client for the Customer from the given config. +func NewCustomerClient(c config) *CustomerClient { + return &CustomerClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `customer.Hooks(f(g(h())))`. +func (c *CustomerClient) Use(hooks ...Hook) { + c.hooks.Customer = append(c.hooks.Customer, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `customer.Intercept(f(g(h())))`. +func (c *CustomerClient) Intercept(interceptors ...Interceptor) { + c.inters.Customer = append(c.inters.Customer, interceptors...) +} + +// Create returns a builder for creating a Customer entity. +func (c *CustomerClient) Create() *CustomerCreate { + mutation := newCustomerMutation(c.config, OpCreate) + return &CustomerCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Customer entities. +func (c *CustomerClient) CreateBulk(builders ...*CustomerCreate) *CustomerCreateBulk { + return &CustomerCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Customer. +func (c *CustomerClient) Update() *CustomerUpdate { + mutation := newCustomerMutation(c.config, OpUpdate) + return &CustomerUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *CustomerClient) UpdateOne(cu *Customer) *CustomerUpdateOne { + mutation := newCustomerMutation(c.config, OpUpdateOne, withCustomer(cu)) + return &CustomerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *CustomerClient) UpdateOneID(id int) *CustomerUpdateOne { + mutation := newCustomerMutation(c.config, OpUpdateOne, withCustomerID(id)) + return &CustomerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Customer. +func (c *CustomerClient) Delete() *CustomerDelete { + mutation := newCustomerMutation(c.config, OpDelete) + return &CustomerDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *CustomerClient) DeleteOne(cu *Customer) *CustomerDeleteOne { + return c.DeleteOneID(cu.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *CustomerClient) DeleteOneID(id int) *CustomerDeleteOne { + builder := c.Delete().Where(customer.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &CustomerDeleteOne{builder} +} + +// Query returns a query builder for Customer. +func (c *CustomerClient) Query() *CustomerQuery { + return &CustomerQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeCustomer}, + inters: c.Interceptors(), + } +} + +// Get returns a Customer entity by its id. +func (c *CustomerClient) Get(ctx context.Context, id int) (*Customer, error) { + return c.Query().Where(customer.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *CustomerClient) GetX(ctx context.Context, id int) *Customer { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryBooks queries the books edge of a Customer. +func (c *CustomerClient) QueryBooks(cu *Customer) *BookQuery { + query := (&BookClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := cu.ID + step := sqlgraph.NewStep( + sqlgraph.From(customer.Table, customer.FieldID, id), + sqlgraph.To(book.Table, book.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customer.BooksTable, customer.BooksColumn), + ) + fromV = sqlgraph.Neighbors(cu.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *CustomerClient) Hooks() []Hook { + return c.hooks.Customer +} + +// Interceptors returns the client interceptors. +func (c *CustomerClient) Interceptors() []Interceptor { + return c.inters.Customer +} + +func (c *CustomerClient) mutate(ctx context.Context, m *CustomerMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&CustomerCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&CustomerUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&CustomerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&CustomerDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Customer mutation op: %q", m.Op()) + } +} + +// hooks and interceptors per client, for fast access. +type ( + hooks struct { + Book, Customer []ent.Hook + } + inters struct { + Book, Customer []ent.Interceptor + } +) diff --git a/examples/ent-integration/ent/customer.go b/examples/ent-integration/ent/customer.go new file mode 100644 index 0000000..51f8abe --- /dev/null +++ b/examples/ent-integration/ent/customer.go @@ -0,0 +1,119 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent/dialect/sql" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" +) + +// Customer is the model entity for the Customer schema. +type Customer struct { + config `json:"-"` + // ID of the ent. + ID int `json:"id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CustomerQuery when eager-loading is set. + Edges CustomerEdges `json:"edges"` +} + +// CustomerEdges holds the relations/edges for other nodes in the graph. +type CustomerEdges struct { + // Books holds the value of the books edge. + Books []*Book `json:"books,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// BooksOrErr returns the Books value or an error if the edge +// was not loaded in eager-loading. +func (e CustomerEdges) BooksOrErr() ([]*Book, error) { + if e.loadedTypes[0] { + return e.Books, nil + } + return nil, &NotLoadedError{edge: "books"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Customer) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case customer.FieldID: + values[i] = new(sql.NullInt64) + case customer.FieldName: + values[i] = new(sql.NullString) + default: + return nil, fmt.Errorf("unexpected column %q for type Customer", columns[i]) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Customer fields. +func (c *Customer) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case customer.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + c.ID = int(value.Int64) + case customer.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + c.Name = value.String + } + } + } + return nil +} + +// QueryBooks queries the "books" edge of the Customer entity. +func (c *Customer) QueryBooks() *BookQuery { + return NewCustomerClient(c.config).QueryBooks(c) +} + +// Update returns a builder for updating this Customer. +// Note that you need to call Customer.Unwrap() before calling this method if this Customer +// was returned from a transaction, and the transaction was committed or rolled back. +func (c *Customer) Update() *CustomerUpdateOne { + return NewCustomerClient(c.config).UpdateOne(c) +} + +// Unwrap unwraps the Customer entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (c *Customer) Unwrap() *Customer { + _tx, ok := c.config.driver.(*txDriver) + if !ok { + panic("ent: Customer is not a transactional entity") + } + c.config.driver = _tx.drv + return c +} + +// String implements the fmt.Stringer. +func (c *Customer) String() string { + var builder strings.Builder + builder.WriteString("Customer(") + builder.WriteString(fmt.Sprintf("id=%v, ", c.ID)) + builder.WriteString("name=") + builder.WriteString(c.Name) + builder.WriteByte(')') + return builder.String() +} + +// Customers is a parsable slice of Customer. +type Customers []*Customer diff --git a/examples/ent-integration/ent/customer/customer.go b/examples/ent-integration/ent/customer/customer.go new file mode 100644 index 0000000..85ab2a6 --- /dev/null +++ b/examples/ent-integration/ent/customer/customer.go @@ -0,0 +1,39 @@ +// Code generated by ent, DO NOT EDIT. + +package customer + +const ( + // Label holds the string label denoting the customer type in the database. + Label = "customer" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // EdgeBooks holds the string denoting the books edge name in mutations. + EdgeBooks = "books" + // Table holds the table name of the customer in the database. + Table = "customers" + // BooksTable is the table that holds the books relation/edge. + BooksTable = "books" + // BooksInverseTable is the table name for the Book entity. + // It exists in this package in order to avoid circular dependency with the "book" package. + BooksInverseTable = "books" + // BooksColumn is the table column denoting the books relation/edge. + BooksColumn = "customer_books" +) + +// Columns holds all SQL columns for customer fields. +var Columns = []string{ + FieldID, + FieldName, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} diff --git a/examples/ent-integration/ent/customer/where.go b/examples/ent-integration/ent/customer/where.go new file mode 100644 index 0000000..1ab9ac9 --- /dev/null +++ b/examples/ent-integration/ent/customer/where.go @@ -0,0 +1,183 @@ +// Code generated by ent, DO NOT EDIT. + +package customer + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.Customer { + return predicate.Customer(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.Customer { + return predicate.Customer(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.Customer { + return predicate.Customer(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.Customer { + return predicate.Customer(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.Customer { + return predicate.Customer(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.Customer { + return predicate.Customer(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.Customer { + return predicate.Customer(sql.FieldLTE(FieldID, id)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldName, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Customer { + return predicate.Customer(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Customer { + return predicate.Customer(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Customer { + return predicate.Customer(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Customer { + return predicate.Customer(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Customer { + return predicate.Customer(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Customer { + return predicate.Customer(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Customer { + return predicate.Customer(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Customer { + return predicate.Customer(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Customer { + return predicate.Customer(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Customer { + return predicate.Customer(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Customer { + return predicate.Customer(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Customer { + return predicate.Customer(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Customer { + return predicate.Customer(sql.FieldContainsFold(FieldName, v)) +} + +// HasBooks applies the HasEdge predicate on the "books" edge. +func HasBooks() predicate.Customer { + return predicate.Customer(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, BooksTable, BooksColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasBooksWith applies the HasEdge predicate on the "books" edge with a given conditions (other predicates). +func HasBooksWith(preds ...predicate.Book) predicate.Customer { + return predicate.Customer(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(BooksInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, BooksTable, BooksColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Customer) predicate.Customer { + return predicate.Customer(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for _, p := range predicates { + p(s1) + } + s.Where(s1.P()) + }) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Customer) predicate.Customer { + return predicate.Customer(func(s *sql.Selector) { + s1 := s.Clone().SetP(nil) + for i, p := range predicates { + if i > 0 { + s1.Or() + } + p(s1) + } + s.Where(s1.P()) + }) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Customer) predicate.Customer { + return predicate.Customer(func(s *sql.Selector) { + p(s.Not()) + }) +} diff --git a/examples/ent-integration/ent/customer_create.go b/examples/ent-integration/ent/customer_create.go new file mode 100644 index 0000000..ff3f214 --- /dev/null +++ b/examples/ent-integration/ent/customer_create.go @@ -0,0 +1,211 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" +) + +// CustomerCreate is the builder for creating a Customer entity. +type CustomerCreate struct { + config + mutation *CustomerMutation + hooks []Hook +} + +// SetName sets the "name" field. +func (cc *CustomerCreate) SetName(s string) *CustomerCreate { + cc.mutation.SetName(s) + return cc +} + +// AddBookIDs adds the "books" edge to the Book entity by IDs. +func (cc *CustomerCreate) AddBookIDs(ids ...int) *CustomerCreate { + cc.mutation.AddBookIDs(ids...) + return cc +} + +// AddBooks adds the "books" edges to the Book entity. +func (cc *CustomerCreate) AddBooks(b ...*Book) *CustomerCreate { + ids := make([]int, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return cc.AddBookIDs(ids...) +} + +// Mutation returns the CustomerMutation object of the builder. +func (cc *CustomerCreate) Mutation() *CustomerMutation { + return cc.mutation +} + +// Save creates the Customer in the database. +func (cc *CustomerCreate) Save(ctx context.Context) (*Customer, error) { + return withHooks[*Customer, CustomerMutation](ctx, cc.sqlSave, cc.mutation, cc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (cc *CustomerCreate) SaveX(ctx context.Context) *Customer { + v, err := cc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (cc *CustomerCreate) Exec(ctx context.Context) error { + _, err := cc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cc *CustomerCreate) ExecX(ctx context.Context) { + if err := cc.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (cc *CustomerCreate) check() error { + if _, ok := cc.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Customer.name"`)} + } + return nil +} + +func (cc *CustomerCreate) sqlSave(ctx context.Context) (*Customer, error) { + if err := cc.check(); err != nil { + return nil, err + } + _node, _spec := cc.createSpec() + if err := sqlgraph.CreateNode(ctx, cc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int(id) + cc.mutation.id = &_node.ID + cc.mutation.done = true + return _node, nil +} + +func (cc *CustomerCreate) createSpec() (*Customer, *sqlgraph.CreateSpec) { + var ( + _node = &Customer{config: cc.config} + _spec = sqlgraph.NewCreateSpec(customer.Table, sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt)) + ) + if value, ok := cc.mutation.Name(); ok { + _spec.SetField(customer.FieldName, field.TypeString, value) + _node.Name = value + } + if nodes := cc.mutation.BooksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// CustomerCreateBulk is the builder for creating many Customer entities in bulk. +type CustomerCreateBulk struct { + config + builders []*CustomerCreate +} + +// Save creates the Customer entities in the database. +func (ccb *CustomerCreateBulk) Save(ctx context.Context) ([]*Customer, error) { + specs := make([]*sqlgraph.CreateSpec, len(ccb.builders)) + nodes := make([]*Customer, len(ccb.builders)) + mutators := make([]Mutator, len(ccb.builders)) + for i := range ccb.builders { + func(i int, root context.Context) { + builder := ccb.builders[i] + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*CustomerMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + nodes[i], specs[i] = builder.createSpec() + var err error + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, ccb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, ccb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, ccb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (ccb *CustomerCreateBulk) SaveX(ctx context.Context) []*Customer { + v, err := ccb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (ccb *CustomerCreateBulk) Exec(ctx context.Context) error { + _, err := ccb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (ccb *CustomerCreateBulk) ExecX(ctx context.Context) { + if err := ccb.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/examples/ent-integration/ent/customer_delete.go b/examples/ent-integration/ent/customer_delete.go new file mode 100644 index 0000000..50dd17e --- /dev/null +++ b/examples/ent-integration/ent/customer_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// CustomerDelete is the builder for deleting a Customer entity. +type CustomerDelete struct { + config + hooks []Hook + mutation *CustomerMutation +} + +// Where appends a list predicates to the CustomerDelete builder. +func (cd *CustomerDelete) Where(ps ...predicate.Customer) *CustomerDelete { + cd.mutation.Where(ps...) + return cd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (cd *CustomerDelete) Exec(ctx context.Context) (int, error) { + return withHooks[int, CustomerMutation](ctx, cd.sqlExec, cd.mutation, cd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (cd *CustomerDelete) ExecX(ctx context.Context) int { + n, err := cd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (cd *CustomerDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(customer.Table, sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt)) + if ps := cd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, cd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + cd.mutation.done = true + return affected, err +} + +// CustomerDeleteOne is the builder for deleting a single Customer entity. +type CustomerDeleteOne struct { + cd *CustomerDelete +} + +// Where appends a list predicates to the CustomerDelete builder. +func (cdo *CustomerDeleteOne) Where(ps ...predicate.Customer) *CustomerDeleteOne { + cdo.cd.mutation.Where(ps...) + return cdo +} + +// Exec executes the deletion query. +func (cdo *CustomerDeleteOne) Exec(ctx context.Context) error { + n, err := cdo.cd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{customer.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (cdo *CustomerDeleteOne) ExecX(ctx context.Context) { + if err := cdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/examples/ent-integration/ent/customer_query.go b/examples/ent-integration/ent/customer_query.go new file mode 100644 index 0000000..5b9fff8 --- /dev/null +++ b/examples/ent-integration/ent/customer_query.go @@ -0,0 +1,606 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// CustomerQuery is the builder for querying Customer entities. +type CustomerQuery struct { + config + ctx *QueryContext + order []OrderFunc + inters []Interceptor + predicates []predicate.Customer + withBooks *BookQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the CustomerQuery builder. +func (cq *CustomerQuery) Where(ps ...predicate.Customer) *CustomerQuery { + cq.predicates = append(cq.predicates, ps...) + return cq +} + +// Limit the number of records to be returned by this query. +func (cq *CustomerQuery) Limit(limit int) *CustomerQuery { + cq.ctx.Limit = &limit + return cq +} + +// Offset to start from. +func (cq *CustomerQuery) Offset(offset int) *CustomerQuery { + cq.ctx.Offset = &offset + return cq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (cq *CustomerQuery) Unique(unique bool) *CustomerQuery { + cq.ctx.Unique = &unique + return cq +} + +// Order specifies how the records should be ordered. +func (cq *CustomerQuery) Order(o ...OrderFunc) *CustomerQuery { + cq.order = append(cq.order, o...) + return cq +} + +// QueryBooks chains the current query on the "books" edge. +func (cq *CustomerQuery) QueryBooks() *BookQuery { + query := (&BookClient{config: cq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := cq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := cq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(customer.Table, customer.FieldID, selector), + sqlgraph.To(book.Table, book.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customer.BooksTable, customer.BooksColumn), + ) + fromU = sqlgraph.SetNeighbors(cq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Customer entity from the query. +// Returns a *NotFoundError when no Customer was found. +func (cq *CustomerQuery) First(ctx context.Context) (*Customer, error) { + nodes, err := cq.Limit(1).All(setContextOp(ctx, cq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{customer.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (cq *CustomerQuery) FirstX(ctx context.Context) *Customer { + node, err := cq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Customer ID from the query. +// Returns a *NotFoundError when no Customer ID was found. +func (cq *CustomerQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = cq.Limit(1).IDs(setContextOp(ctx, cq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{customer.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (cq *CustomerQuery) FirstIDX(ctx context.Context) int { + id, err := cq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Customer entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Customer entity is found. +// Returns a *NotFoundError when no Customer entities are found. +func (cq *CustomerQuery) Only(ctx context.Context) (*Customer, error) { + nodes, err := cq.Limit(2).All(setContextOp(ctx, cq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{customer.Label} + default: + return nil, &NotSingularError{customer.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (cq *CustomerQuery) OnlyX(ctx context.Context) *Customer { + node, err := cq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Customer ID in the query. +// Returns a *NotSingularError when more than one Customer ID is found. +// Returns a *NotFoundError when no entities are found. +func (cq *CustomerQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = cq.Limit(2).IDs(setContextOp(ctx, cq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{customer.Label} + default: + err = &NotSingularError{customer.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (cq *CustomerQuery) OnlyIDX(ctx context.Context) int { + id, err := cq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Customers. +func (cq *CustomerQuery) All(ctx context.Context) ([]*Customer, error) { + ctx = setContextOp(ctx, cq.ctx, "All") + if err := cq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Customer, *CustomerQuery]() + return withInterceptors[[]*Customer](ctx, cq, qr, cq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (cq *CustomerQuery) AllX(ctx context.Context) []*Customer { + nodes, err := cq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Customer IDs. +func (cq *CustomerQuery) IDs(ctx context.Context) (ids []int, err error) { + if cq.ctx.Unique == nil && cq.path != nil { + cq.Unique(true) + } + ctx = setContextOp(ctx, cq.ctx, "IDs") + if err = cq.Select(customer.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (cq *CustomerQuery) IDsX(ctx context.Context) []int { + ids, err := cq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (cq *CustomerQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, cq.ctx, "Count") + if err := cq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, cq, querierCount[*CustomerQuery](), cq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (cq *CustomerQuery) CountX(ctx context.Context) int { + count, err := cq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (cq *CustomerQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, cq.ctx, "Exist") + switch _, err := cq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (cq *CustomerQuery) ExistX(ctx context.Context) bool { + exist, err := cq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the CustomerQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (cq *CustomerQuery) Clone() *CustomerQuery { + if cq == nil { + return nil + } + return &CustomerQuery{ + config: cq.config, + ctx: cq.ctx.Clone(), + order: append([]OrderFunc{}, cq.order...), + inters: append([]Interceptor{}, cq.inters...), + predicates: append([]predicate.Customer{}, cq.predicates...), + withBooks: cq.withBooks.Clone(), + // clone intermediate query. + sql: cq.sql.Clone(), + path: cq.path, + } +} + +// WithBooks tells the query-builder to eager-load the nodes that are connected to +// the "books" edge. The optional arguments are used to configure the query builder of the edge. +func (cq *CustomerQuery) WithBooks(opts ...func(*BookQuery)) *CustomerQuery { + query := (&BookClient{config: cq.config}).Query() + for _, opt := range opts { + opt(query) + } + cq.withBooks = query + return cq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Customer.Query(). +// GroupBy(customer.FieldName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (cq *CustomerQuery) GroupBy(field string, fields ...string) *CustomerGroupBy { + cq.ctx.Fields = append([]string{field}, fields...) + grbuild := &CustomerGroupBy{build: cq} + grbuild.flds = &cq.ctx.Fields + grbuild.label = customer.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// } +// +// client.Customer.Query(). +// Select(customer.FieldName). +// Scan(ctx, &v) +func (cq *CustomerQuery) Select(fields ...string) *CustomerSelect { + cq.ctx.Fields = append(cq.ctx.Fields, fields...) + sbuild := &CustomerSelect{CustomerQuery: cq} + sbuild.label = customer.Label + sbuild.flds, sbuild.scan = &cq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a CustomerSelect configured with the given aggregations. +func (cq *CustomerQuery) Aggregate(fns ...AggregateFunc) *CustomerSelect { + return cq.Select().Aggregate(fns...) +} + +func (cq *CustomerQuery) prepareQuery(ctx context.Context) error { + for _, inter := range cq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, cq); err != nil { + return err + } + } + } + for _, f := range cq.ctx.Fields { + if !customer.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if cq.path != nil { + prev, err := cq.path(ctx) + if err != nil { + return err + } + cq.sql = prev + } + return nil +} + +func (cq *CustomerQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Customer, error) { + var ( + nodes = []*Customer{} + _spec = cq.querySpec() + loadedTypes = [1]bool{ + cq.withBooks != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Customer).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Customer{config: cq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, cq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := cq.withBooks; query != nil { + if err := cq.loadBooks(ctx, query, nodes, + func(n *Customer) { n.Edges.Books = []*Book{} }, + func(n *Customer, e *Book) { n.Edges.Books = append(n.Edges.Books, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (cq *CustomerQuery) loadBooks(ctx context.Context, query *BookQuery, nodes []*Customer, init func(*Customer), assign func(*Customer, *Book)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*Customer) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + query.withFKs = true + query.Where(predicate.Book(func(s *sql.Selector) { + s.Where(sql.InValues(customer.BooksColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.customer_books + if fk == nil { + return fmt.Errorf(`foreign-key "customer_books" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected foreign-key "customer_books" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} + +func (cq *CustomerQuery) sqlCount(ctx context.Context) (int, error) { + _spec := cq.querySpec() + _spec.Node.Columns = cq.ctx.Fields + if len(cq.ctx.Fields) > 0 { + _spec.Unique = cq.ctx.Unique != nil && *cq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, cq.driver, _spec) +} + +func (cq *CustomerQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(customer.Table, customer.Columns, sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt)) + _spec.From = cq.sql + if unique := cq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if cq.path != nil { + _spec.Unique = true + } + if fields := cq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, customer.FieldID) + for i := range fields { + if fields[i] != customer.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := cq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := cq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := cq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := cq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (cq *CustomerQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(cq.driver.Dialect()) + t1 := builder.Table(customer.Table) + columns := cq.ctx.Fields + if len(columns) == 0 { + columns = customer.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if cq.sql != nil { + selector = cq.sql + selector.Select(selector.Columns(columns...)...) + } + if cq.ctx.Unique != nil && *cq.ctx.Unique { + selector.Distinct() + } + for _, p := range cq.predicates { + p(selector) + } + for _, p := range cq.order { + p(selector) + } + if offset := cq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := cq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// CustomerGroupBy is the group-by builder for Customer entities. +type CustomerGroupBy struct { + selector + build *CustomerQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (cgb *CustomerGroupBy) Aggregate(fns ...AggregateFunc) *CustomerGroupBy { + cgb.fns = append(cgb.fns, fns...) + return cgb +} + +// Scan applies the selector query and scans the result into the given value. +func (cgb *CustomerGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, cgb.build.ctx, "GroupBy") + if err := cgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*CustomerQuery, *CustomerGroupBy](ctx, cgb.build, cgb, cgb.build.inters, v) +} + +func (cgb *CustomerGroupBy) sqlScan(ctx context.Context, root *CustomerQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(cgb.fns)) + for _, fn := range cgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*cgb.flds)+len(cgb.fns)) + for _, f := range *cgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*cgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := cgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// CustomerSelect is the builder for selecting fields of Customer entities. +type CustomerSelect struct { + *CustomerQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (cs *CustomerSelect) Aggregate(fns ...AggregateFunc) *CustomerSelect { + cs.fns = append(cs.fns, fns...) + return cs +} + +// Scan applies the selector query and scans the result into the given value. +func (cs *CustomerSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, cs.ctx, "Select") + if err := cs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*CustomerQuery, *CustomerSelect](ctx, cs.CustomerQuery, cs, cs.inters, v) +} + +func (cs *CustomerSelect) sqlScan(ctx context.Context, root *CustomerQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(cs.fns)) + for _, fn := range cs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*cs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := cs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/examples/ent-integration/ent/customer_update.go b/examples/ent-integration/ent/customer_update.go new file mode 100644 index 0000000..938fce0 --- /dev/null +++ b/examples/ent-integration/ent/customer_update.go @@ -0,0 +1,356 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +// CustomerUpdate is the builder for updating Customer entities. +type CustomerUpdate struct { + config + hooks []Hook + mutation *CustomerMutation +} + +// Where appends a list predicates to the CustomerUpdate builder. +func (cu *CustomerUpdate) Where(ps ...predicate.Customer) *CustomerUpdate { + cu.mutation.Where(ps...) + return cu +} + +// SetName sets the "name" field. +func (cu *CustomerUpdate) SetName(s string) *CustomerUpdate { + cu.mutation.SetName(s) + return cu +} + +// AddBookIDs adds the "books" edge to the Book entity by IDs. +func (cu *CustomerUpdate) AddBookIDs(ids ...int) *CustomerUpdate { + cu.mutation.AddBookIDs(ids...) + return cu +} + +// AddBooks adds the "books" edges to the Book entity. +func (cu *CustomerUpdate) AddBooks(b ...*Book) *CustomerUpdate { + ids := make([]int, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return cu.AddBookIDs(ids...) +} + +// Mutation returns the CustomerMutation object of the builder. +func (cu *CustomerUpdate) Mutation() *CustomerMutation { + return cu.mutation +} + +// ClearBooks clears all "books" edges to the Book entity. +func (cu *CustomerUpdate) ClearBooks() *CustomerUpdate { + cu.mutation.ClearBooks() + return cu +} + +// RemoveBookIDs removes the "books" edge to Book entities by IDs. +func (cu *CustomerUpdate) RemoveBookIDs(ids ...int) *CustomerUpdate { + cu.mutation.RemoveBookIDs(ids...) + return cu +} + +// RemoveBooks removes "books" edges to Book entities. +func (cu *CustomerUpdate) RemoveBooks(b ...*Book) *CustomerUpdate { + ids := make([]int, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return cu.RemoveBookIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (cu *CustomerUpdate) Save(ctx context.Context) (int, error) { + return withHooks[int, CustomerMutation](ctx, cu.sqlSave, cu.mutation, cu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (cu *CustomerUpdate) SaveX(ctx context.Context) int { + affected, err := cu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (cu *CustomerUpdate) Exec(ctx context.Context) error { + _, err := cu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cu *CustomerUpdate) ExecX(ctx context.Context) { + if err := cu.Exec(ctx); err != nil { + panic(err) + } +} + +func (cu *CustomerUpdate) sqlSave(ctx context.Context) (n int, err error) { + _spec := sqlgraph.NewUpdateSpec(customer.Table, customer.Columns, sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt)) + if ps := cu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := cu.mutation.Name(); ok { + _spec.SetField(customer.FieldName, field.TypeString, value) + } + if cu.mutation.BooksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cu.mutation.RemovedBooksIDs(); len(nodes) > 0 && !cu.mutation.BooksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cu.mutation.BooksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if n, err = sqlgraph.UpdateNodes(ctx, cu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{customer.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + cu.mutation.done = true + return n, nil +} + +// CustomerUpdateOne is the builder for updating a single Customer entity. +type CustomerUpdateOne struct { + config + fields []string + hooks []Hook + mutation *CustomerMutation +} + +// SetName sets the "name" field. +func (cuo *CustomerUpdateOne) SetName(s string) *CustomerUpdateOne { + cuo.mutation.SetName(s) + return cuo +} + +// AddBookIDs adds the "books" edge to the Book entity by IDs. +func (cuo *CustomerUpdateOne) AddBookIDs(ids ...int) *CustomerUpdateOne { + cuo.mutation.AddBookIDs(ids...) + return cuo +} + +// AddBooks adds the "books" edges to the Book entity. +func (cuo *CustomerUpdateOne) AddBooks(b ...*Book) *CustomerUpdateOne { + ids := make([]int, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return cuo.AddBookIDs(ids...) +} + +// Mutation returns the CustomerMutation object of the builder. +func (cuo *CustomerUpdateOne) Mutation() *CustomerMutation { + return cuo.mutation +} + +// ClearBooks clears all "books" edges to the Book entity. +func (cuo *CustomerUpdateOne) ClearBooks() *CustomerUpdateOne { + cuo.mutation.ClearBooks() + return cuo +} + +// RemoveBookIDs removes the "books" edge to Book entities by IDs. +func (cuo *CustomerUpdateOne) RemoveBookIDs(ids ...int) *CustomerUpdateOne { + cuo.mutation.RemoveBookIDs(ids...) + return cuo +} + +// RemoveBooks removes "books" edges to Book entities. +func (cuo *CustomerUpdateOne) RemoveBooks(b ...*Book) *CustomerUpdateOne { + ids := make([]int, len(b)) + for i := range b { + ids[i] = b[i].ID + } + return cuo.RemoveBookIDs(ids...) +} + +// Where appends a list predicates to the CustomerUpdate builder. +func (cuo *CustomerUpdateOne) Where(ps ...predicate.Customer) *CustomerUpdateOne { + cuo.mutation.Where(ps...) + return cuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (cuo *CustomerUpdateOne) Select(field string, fields ...string) *CustomerUpdateOne { + cuo.fields = append([]string{field}, fields...) + return cuo +} + +// Save executes the query and returns the updated Customer entity. +func (cuo *CustomerUpdateOne) Save(ctx context.Context) (*Customer, error) { + return withHooks[*Customer, CustomerMutation](ctx, cuo.sqlSave, cuo.mutation, cuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (cuo *CustomerUpdateOne) SaveX(ctx context.Context) *Customer { + node, err := cuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (cuo *CustomerUpdateOne) Exec(ctx context.Context) error { + _, err := cuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (cuo *CustomerUpdateOne) ExecX(ctx context.Context) { + if err := cuo.Exec(ctx); err != nil { + panic(err) + } +} + +func (cuo *CustomerUpdateOne) sqlSave(ctx context.Context) (_node *Customer, err error) { + _spec := sqlgraph.NewUpdateSpec(customer.Table, customer.Columns, sqlgraph.NewFieldSpec(customer.FieldID, field.TypeInt)) + id, ok := cuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Customer.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := cuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, customer.FieldID) + for _, f := range fields { + if !customer.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != customer.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := cuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := cuo.mutation.Name(); ok { + _spec.SetField(customer.FieldName, field.TypeString, value) + } + if cuo.mutation.BooksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cuo.mutation.RemovedBooksIDs(); len(nodes) > 0 && !cuo.mutation.BooksCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := cuo.mutation.BooksIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customer.BooksTable, + Columns: []string{customer.BooksColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(book.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Customer{config: cuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, cuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{customer.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + cuo.mutation.done = true + return _node, nil +} diff --git a/examples/ent-integration/ent/ent.go b/examples/ent-integration/ent/ent.go new file mode 100644 index 0000000..4261632 --- /dev/null +++ b/examples/ent-integration/ent/ent.go @@ -0,0 +1,618 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "reflect" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" +) + +// ent aliases to avoid import conflicts in user's code. +type ( + Op = ent.Op + Hook = ent.Hook + Value = ent.Value + Query = ent.Query + QueryContext = ent.QueryContext + Querier = ent.Querier + QuerierFunc = ent.QuerierFunc + Interceptor = ent.Interceptor + InterceptFunc = ent.InterceptFunc + Traverser = ent.Traverser + TraverseFunc = ent.TraverseFunc + Policy = ent.Policy + Mutator = ent.Mutator + Mutation = ent.Mutation + MutateFunc = ent.MutateFunc +) + +type clientCtxKey struct{} + +// FromContext returns a Client stored inside a context, or nil if there isn't one. +func FromContext(ctx context.Context) *Client { + c, _ := ctx.Value(clientCtxKey{}).(*Client) + return c +} + +// NewContext returns a new context with the given Client attached. +func NewContext(parent context.Context, c *Client) context.Context { + return context.WithValue(parent, clientCtxKey{}, c) +} + +type txCtxKey struct{} + +// TxFromContext returns a Tx stored inside a context, or nil if there isn't one. +func TxFromContext(ctx context.Context) *Tx { + tx, _ := ctx.Value(txCtxKey{}).(*Tx) + return tx +} + +// NewTxContext returns a new context with the given Tx attached. +func NewTxContext(parent context.Context, tx *Tx) context.Context { + return context.WithValue(parent, txCtxKey{}, tx) +} + +// OrderFunc applies an ordering on the sql selector. +type OrderFunc func(*sql.Selector) + +// columnChecker returns a function indicates if the column exists in the given column. +func columnChecker(table string) func(string) error { + checks := map[string]func(string) bool{ + book.Table: book.ValidColumn, + customer.Table: customer.ValidColumn, + } + check, ok := checks[table] + if !ok { + return func(string) error { + return fmt.Errorf("unknown table %q", table) + } + } + return func(column string) error { + if !check(column) { + return fmt.Errorf("unknown column %q for table %q", column, table) + } + return nil + } +} + +// Asc applies the given fields in ASC order. +func Asc(fields ...string) OrderFunc { + return func(s *sql.Selector) { + check := columnChecker(s.TableName()) + for _, f := range fields { + if err := check(f); err != nil { + s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) + } + s.OrderBy(sql.Asc(s.C(f))) + } + } +} + +// Desc applies the given fields in DESC order. +func Desc(fields ...string) OrderFunc { + return func(s *sql.Selector) { + check := columnChecker(s.TableName()) + for _, f := range fields { + if err := check(f); err != nil { + s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) + } + s.OrderBy(sql.Desc(s.C(f))) + } + } +} + +// AggregateFunc applies an aggregation step on the group-by traversal/selector. +type AggregateFunc func(*sql.Selector) string + +// As is a pseudo aggregation function for renaming another other functions with custom names. For example: +// +// GroupBy(field1, field2). +// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")). +// Scan(ctx, &v) +func As(fn AggregateFunc, end string) AggregateFunc { + return func(s *sql.Selector) string { + return sql.As(fn(s), end) + } +} + +// Count applies the "count" aggregation function on each group. +func Count() AggregateFunc { + return func(s *sql.Selector) string { + return sql.Count("*") + } +} + +// Max applies the "max" aggregation function on the given field of each group. +func Max(field string) AggregateFunc { + return func(s *sql.Selector) string { + check := columnChecker(s.TableName()) + if err := check(field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Max(s.C(field)) + } +} + +// Mean applies the "mean" aggregation function on the given field of each group. +func Mean(field string) AggregateFunc { + return func(s *sql.Selector) string { + check := columnChecker(s.TableName()) + if err := check(field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Avg(s.C(field)) + } +} + +// Min applies the "min" aggregation function on the given field of each group. +func Min(field string) AggregateFunc { + return func(s *sql.Selector) string { + check := columnChecker(s.TableName()) + if err := check(field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Min(s.C(field)) + } +} + +// Sum applies the "sum" aggregation function on the given field of each group. +func Sum(field string) AggregateFunc { + return func(s *sql.Selector) string { + check := columnChecker(s.TableName()) + if err := check(field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Sum(s.C(field)) + } +} + +// ValidationError returns when validating a field or edge fails. +type ValidationError struct { + Name string // Field or edge name. + err error +} + +// Error implements the error interface. +func (e *ValidationError) Error() string { + return e.err.Error() +} + +// Unwrap implements the errors.Wrapper interface. +func (e *ValidationError) Unwrap() error { + return e.err +} + +// IsValidationError returns a boolean indicating whether the error is a validation error. +func IsValidationError(err error) bool { + if err == nil { + return false + } + var e *ValidationError + return errors.As(err, &e) +} + +// NotFoundError returns when trying to fetch a specific entity and it was not found in the database. +type NotFoundError struct { + label string +} + +// Error implements the error interface. +func (e *NotFoundError) Error() string { + return "ent: " + e.label + " not found" +} + +// IsNotFound returns a boolean indicating whether the error is a not found error. +func IsNotFound(err error) bool { + if err == nil { + return false + } + var e *NotFoundError + return errors.As(err, &e) +} + +// MaskNotFound masks not found error. +func MaskNotFound(err error) error { + if IsNotFound(err) { + return nil + } + return err +} + +// NotSingularError returns when trying to fetch a singular entity and more then one was found in the database. +type NotSingularError struct { + label string +} + +// Error implements the error interface. +func (e *NotSingularError) Error() string { + return "ent: " + e.label + " not singular" +} + +// IsNotSingular returns a boolean indicating whether the error is a not singular error. +func IsNotSingular(err error) bool { + if err == nil { + return false + } + var e *NotSingularError + return errors.As(err, &e) +} + +// NotLoadedError returns when trying to get a node that was not loaded by the query. +type NotLoadedError struct { + edge string +} + +// Error implements the error interface. +func (e *NotLoadedError) Error() string { + return "ent: " + e.edge + " edge was not loaded" +} + +// IsNotLoaded returns a boolean indicating whether the error is a not loaded error. +func IsNotLoaded(err error) bool { + if err == nil { + return false + } + var e *NotLoadedError + return errors.As(err, &e) +} + +// ConstraintError returns when trying to create/update one or more entities and +// one or more of their constraints failed. For example, violation of edge or +// field uniqueness. +type ConstraintError struct { + msg string + wrap error +} + +// Error implements the error interface. +func (e ConstraintError) Error() string { + return "ent: constraint failed: " + e.msg +} + +// Unwrap implements the errors.Wrapper interface. +func (e *ConstraintError) Unwrap() error { + return e.wrap +} + +// IsConstraintError returns a boolean indicating whether the error is a constraint failure. +func IsConstraintError(err error) bool { + if err == nil { + return false + } + var e *ConstraintError + return errors.As(err, &e) +} + +// selector embedded by the different Select/GroupBy builders. +type selector struct { + label string + flds *[]string + fns []AggregateFunc + scan func(context.Context, any) error +} + +// ScanX is like Scan, but panics if an error occurs. +func (s *selector) ScanX(ctx context.Context, v any) { + if err := s.scan(ctx, v); err != nil { + panic(err) + } +} + +// Strings returns list of strings from a selector. It is only allowed when selecting one field. +func (s *selector) Strings(ctx context.Context) ([]string, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Strings is not achievable when selecting more than 1 field") + } + var v []string + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// StringsX is like Strings, but panics if an error occurs. +func (s *selector) StringsX(ctx context.Context) []string { + v, err := s.Strings(ctx) + if err != nil { + panic(err) + } + return v +} + +// String returns a single string from a selector. It is only allowed when selecting one field. +func (s *selector) String(ctx context.Context) (_ string, err error) { + var v []string + if v, err = s.Strings(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Strings returned %d results when one was expected", len(v)) + } + return +} + +// StringX is like String, but panics if an error occurs. +func (s *selector) StringX(ctx context.Context) string { + v, err := s.String(ctx) + if err != nil { + panic(err) + } + return v +} + +// Ints returns list of ints from a selector. It is only allowed when selecting one field. +func (s *selector) Ints(ctx context.Context) ([]int, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Ints is not achievable when selecting more than 1 field") + } + var v []int + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// IntsX is like Ints, but panics if an error occurs. +func (s *selector) IntsX(ctx context.Context) []int { + v, err := s.Ints(ctx) + if err != nil { + panic(err) + } + return v +} + +// Int returns a single int from a selector. It is only allowed when selecting one field. +func (s *selector) Int(ctx context.Context) (_ int, err error) { + var v []int + if v, err = s.Ints(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Ints returned %d results when one was expected", len(v)) + } + return +} + +// IntX is like Int, but panics if an error occurs. +func (s *selector) IntX(ctx context.Context) int { + v, err := s.Int(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64s returns list of float64s from a selector. It is only allowed when selecting one field. +func (s *selector) Float64s(ctx context.Context) ([]float64, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Float64s is not achievable when selecting more than 1 field") + } + var v []float64 + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// Float64sX is like Float64s, but panics if an error occurs. +func (s *selector) Float64sX(ctx context.Context) []float64 { + v, err := s.Float64s(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64 returns a single float64 from a selector. It is only allowed when selecting one field. +func (s *selector) Float64(ctx context.Context) (_ float64, err error) { + var v []float64 + if v, err = s.Float64s(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Float64s returned %d results when one was expected", len(v)) + } + return +} + +// Float64X is like Float64, but panics if an error occurs. +func (s *selector) Float64X(ctx context.Context) float64 { + v, err := s.Float64(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bools returns list of bools from a selector. It is only allowed when selecting one field. +func (s *selector) Bools(ctx context.Context) ([]bool, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Bools is not achievable when selecting more than 1 field") + } + var v []bool + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// BoolsX is like Bools, but panics if an error occurs. +func (s *selector) BoolsX(ctx context.Context) []bool { + v, err := s.Bools(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bool returns a single bool from a selector. It is only allowed when selecting one field. +func (s *selector) Bool(ctx context.Context) (_ bool, err error) { + var v []bool + if v, err = s.Bools(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Bools returned %d results when one was expected", len(v)) + } + return +} + +// BoolX is like Bool, but panics if an error occurs. +func (s *selector) BoolX(ctx context.Context) bool { + v, err := s.Bool(ctx) + if err != nil { + panic(err) + } + return v +} + +// withHooks invokes the builder operation with the given hooks, if any. +func withHooks[V Value, M any, PM interface { + *M + Mutation +}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) { + if len(hooks) == 0 { + return exec(ctx) + } + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutationT, ok := any(m).(PM) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + // Set the mutation to the builder. + *mutation = *mutationT + return exec(ctx) + }) + for i := len(hooks) - 1; i >= 0; i-- { + if hooks[i] == nil { + return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = hooks[i](mut) + } + v, err := mut.Mutate(ctx, mutation) + if err != nil { + return value, err + } + nv, ok := v.(V) + if !ok { + return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation) + } + return nv, nil +} + +// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist. +func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context { + if ent.QueryFromContext(ctx) == nil { + qc.Op = op + ctx = ent.NewQueryContext(ctx, qc) + } + return ctx +} + +func querierAll[V Value, Q interface { + sqlAll(context.Context, ...queryHook) (V, error) +}]() Querier { + return QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + query, ok := q.(Q) + if !ok { + return nil, fmt.Errorf("unexpected query type %T", q) + } + return query.sqlAll(ctx) + }) +} + +func querierCount[Q interface { + sqlCount(context.Context) (int, error) +}]() Querier { + return QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + query, ok := q.(Q) + if !ok { + return nil, fmt.Errorf("unexpected query type %T", q) + } + return query.sqlCount(ctx) + }) +} + +func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) { + for i := len(inters) - 1; i >= 0; i-- { + qr = inters[i].Intercept(qr) + } + rv, err := qr.Query(ctx, q) + if err != nil { + return v, err + } + vt, ok := rv.(V) + if !ok { + return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v) + } + return vt, nil +} + +func scanWithInterceptors[Q1 ent.Query, Q2 interface { + sqlScan(context.Context, Q1, any) error +}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error { + rv := reflect.ValueOf(v) + var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + query, ok := q.(Q1) + if !ok { + return nil, fmt.Errorf("unexpected query type %T", q) + } + if err := selectOrGroup.sqlScan(ctx, query, v); err != nil { + return nil, err + } + if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() { + return rv.Elem().Interface(), nil + } + return v, nil + }) + for i := len(inters) - 1; i >= 0; i-- { + qr = inters[i].Intercept(qr) + } + vv, err := qr.Query(ctx, rootQuery) + if err != nil { + return err + } + switch rv2 := reflect.ValueOf(vv); { + case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer: + case rv.Type() == rv2.Type(): + rv.Elem().Set(rv2.Elem()) + case rv.Elem().Type() == rv2.Type(): + rv.Elem().Set(rv2) + } + return nil +} + +// queryHook describes an internal hook for the different sqlAll methods. +type queryHook func(context.Context, *sqlgraph.QuerySpec) diff --git a/examples/ent-integration/ent/enttest/enttest.go b/examples/ent-integration/ent/enttest/enttest.go new file mode 100644 index 0000000..b252b11 --- /dev/null +++ b/examples/ent-integration/ent/enttest/enttest.go @@ -0,0 +1,84 @@ +// Code generated by ent, DO NOT EDIT. + +package enttest + +import ( + "context" + + "github.com/hyuti/factory-go/examples/ent-integration/ent" + // required by schema hooks. + _ "github.com/hyuti/factory-go/examples/ent-integration/ent/runtime" + + "entgo.io/ent/dialect/sql/schema" + "github.com/hyuti/factory-go/examples/ent-integration/ent/migrate" +) + +type ( + // TestingT is the interface that is shared between + // testing.T and testing.B and used by enttest. + TestingT interface { + FailNow() + Error(...any) + } + + // Option configures client creation. + Option func(*options) + + options struct { + opts []ent.Option + migrateOpts []schema.MigrateOption + } +) + +// WithOptions forwards options to client creation. +func WithOptions(opts ...ent.Option) Option { + return func(o *options) { + o.opts = append(o.opts, opts...) + } +} + +// WithMigrateOptions forwards options to auto migration. +func WithMigrateOptions(opts ...schema.MigrateOption) Option { + return func(o *options) { + o.migrateOpts = append(o.migrateOpts, opts...) + } +} + +func newOptions(opts []Option) *options { + o := &options{} + for _, opt := range opts { + opt(o) + } + return o +} + +// Open calls ent.Open and auto-run migration. +func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Client { + o := newOptions(opts) + c, err := ent.Open(driverName, dataSourceName, o.opts...) + if err != nil { + t.Error(err) + t.FailNow() + } + migrateSchema(t, c, o) + return c +} + +// NewClient calls ent.NewClient and auto-run migration. +func NewClient(t TestingT, opts ...Option) *ent.Client { + o := newOptions(opts) + c := ent.NewClient(o.opts...) + migrateSchema(t, c, o) + return c +} +func migrateSchema(t TestingT, c *ent.Client, o *options) { + tables, err := schema.CopyTables(migrate.Tables) + if err != nil { + t.Error(err) + t.FailNow() + } + if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil { + t.Error(err) + t.FailNow() + } +} diff --git a/examples/ent-integration/ent/factory.go b/examples/ent-integration/ent/factory.go new file mode 100644 index 0000000..39d70bc --- /dev/null +++ b/examples/ent-integration/ent/factory.go @@ -0,0 +1,195 @@ +package ent + +import ( + "context" + "database/sql" + "fmt" + "reflect" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/Pallinder/go-randomdata" + "github.com/hyuti/factory-go/factory" + _ "github.com/mattn/go-sqlite3" +) + +type ( + ClientCtxType string + Opt struct { + Key string + Value any + } + IFactory[ModelType any] interface { + Create(context.Context) (ModelType, error) + CreateWithClient(context.Context, *Client) (ModelType, error) + } + + Factory[ModelType any] struct { + worker *factory.Factory + } +) + +func Zero[T any]() T { + var t T + return t +} + +const ClientCtxKey ClientCtxType = "client" + +func (s *Factory[ModelType]) Create(ctx context.Context) (ModelType, error) { + eAny, err := s.worker.CreateWithContext(ctx) + if err != nil { + return Zero[ModelType](), err + } + e, ok := eAny.(ModelType) + if !ok { + return Zero[ModelType](), fmt.Errorf("unexpected type %t", eAny) + } + return e, nil +} + +func (s *Factory[ModelType]) CreateWithClient(ctx context.Context, client *Client) (ModelType, error) { + EmbedClient(&ctx, client) + return s.Create(ctx) +} + +func getClient(ctx context.Context) (*Client, error) { + client, ok := ctx.Value(ClientCtxKey).(*Client) + if !ok || client == nil { + return nil, fmt.Errorf("cannot find client in context") + } + return client, nil +} +func convertInputToOutput[ModelInputType, ModelType any]( + ctx context.Context, + args factory.Args, + factory *factory.Factory, + saver func(context.Context, *Client, ModelInputType) (ModelType, error), + opts ...Opt, +) error { + optMap := make(map[string]any) + for _, opt := range opts { + optMap[opt.Key] = opt.Value + } + iAny, err := factory.CreateWithContextAndOption(ctx, optMap) + if err != nil { + return err + } + i, ok := iAny.(ModelInputType) + if !ok { + return fmt.Errorf("unexpected type %t", iAny) + } + client, err := getClient(ctx) + if err != nil { + return err + } + e, err := saver(ctx, client, i) + if err != nil { + return err + } + inst := args.Instance() + dst := reflect.ValueOf(inst) + src := reflect.ValueOf(e).Elem() + dst.Elem().Set(src) + return nil +} +func factoryTemplate[ModelType, ModelInputType any]( + model ModelType, + f *factory.Factory, + saver func(context.Context, *Client, ModelInputType) (ModelType, error), + opts ...Opt, +) *factory.Factory { + return factory.NewFactory( + model, + ).OnCreate(func(a factory.Args) error { + ctx := a.Context() + err := convertInputToOutput( + ctx, + a, + f, + saver, + opts..., + ) + if err != nil { + return err + } + return nil + }) +} +func EmbedClient(ctx *context.Context, v *Client) { + c := *ctx + client := c.Value(ClientCtxKey) + if client == nil { + *ctx = context.WithValue(*ctx, ClientCtxKey, v) + } +} +func OpenClient() (*Client, error) { + db, err := sql.Open("sqlite3", "file:ent?mode=memory&_fk=1") + if err != nil { + return nil, err + } + drv := entsql.OpenDB(dialect.SQLite, db) + return NewClient(Driver(drv)), nil +} + +func New() (*Client, error) { + return OpenClient() +} + +// All you have to do is define a ModelFactory, you don't need to pay attention to all stuff above. But it's nice for you if you give it a shot. +// Also this example assumes you have ent installed. if not refer to https://github.com/ent/ent to get everything inplace +// For example, here is my best practice so far. +type ( + BookInput struct { + OwnerID int + } + CustomerInput struct { + Name string + } +) + +var customerFactory = factory.NewFactory( + &CustomerInput{}, +).Attr("Name", func(a factory.Args) (any, error) { + return randomdata.FullName(randomdata.RandomGender), nil +}) +var bookFactory = factory.NewFactory( + &BookInput{}, +).SubFactory("OwnerID", CustomerFactory(), func(i any) (any, error) { + e, ok := i.(*Customer) + if !ok { + return nil, fmt.Errorf("unexpected type %t", i) + } + return e.ID, nil +}) + +func CustomerFactory(opts ...Opt) *factory.Factory { + return factoryTemplate( + new(Customer), + customerFactory, + func(ctx context.Context, client *Client, i *CustomerInput) (*Customer, error) { + return client.Customer.Create().SetName(i.Name).Save(ctx) + }, + opts..., + ) +} +func BookFactory(opts ...Opt) *factory.Factory { + return factoryTemplate( + new(Book), + bookFactory, + func(ctx context.Context, client *Client, i *BookInput) (*Book, error) { + return client.Book.Create().SetOwnerID(i.OwnerID).Save(ctx) + }, + opts..., + ) +} +func MustCustomerFactory(opts ...Opt) IFactory[*Customer] { + return &Factory[*Customer]{ + worker: CustomerFactory(opts...), + } +} +func MustBookFactory(opts ...Opt) IFactory[*Book] { + return &Factory[*Book]{ + worker: BookFactory(opts...), + } +} diff --git a/examples/ent-integration/ent/generate.go b/examples/ent-integration/ent/generate.go new file mode 100644 index 0000000..8d3fdfd --- /dev/null +++ b/examples/ent-integration/ent/generate.go @@ -0,0 +1,3 @@ +package ent + +//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema diff --git a/examples/ent-integration/ent/hook/hook.go b/examples/ent-integration/ent/hook/hook.go new file mode 100644 index 0000000..87974e9 --- /dev/null +++ b/examples/ent-integration/ent/hook/hook.go @@ -0,0 +1,211 @@ +// Code generated by ent, DO NOT EDIT. + +package hook + +import ( + "context" + "fmt" + + "github.com/hyuti/factory-go/examples/ent-integration/ent" +) + +// The BookFunc type is an adapter to allow the use of ordinary +// function as Book mutator. +type BookFunc func(context.Context, *ent.BookMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f BookFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.BookMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.BookMutation", m) +} + +// The CustomerFunc type is an adapter to allow the use of ordinary +// function as Customer mutator. +type CustomerFunc func(context.Context, *ent.CustomerMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f CustomerFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.CustomerMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CustomerMutation", m) +} + +// Condition is a hook condition function. +type Condition func(context.Context, ent.Mutation) bool + +// And groups conditions with the AND operator. +func And(first, second Condition, rest ...Condition) Condition { + return func(ctx context.Context, m ent.Mutation) bool { + if !first(ctx, m) || !second(ctx, m) { + return false + } + for _, cond := range rest { + if !cond(ctx, m) { + return false + } + } + return true + } +} + +// Or groups conditions with the OR operator. +func Or(first, second Condition, rest ...Condition) Condition { + return func(ctx context.Context, m ent.Mutation) bool { + if first(ctx, m) || second(ctx, m) { + return true + } + for _, cond := range rest { + if cond(ctx, m) { + return true + } + } + return false + } +} + +// Not negates a given condition. +func Not(cond Condition) Condition { + return func(ctx context.Context, m ent.Mutation) bool { + return !cond(ctx, m) + } +} + +// HasOp is a condition testing mutation operation. +func HasOp(op ent.Op) Condition { + return func(_ context.Context, m ent.Mutation) bool { + return m.Op().Is(op) + } +} + +// HasAddedFields is a condition validating `.AddedField` on fields. +func HasAddedFields(field string, fields ...string) Condition { + return func(_ context.Context, m ent.Mutation) bool { + if _, exists := m.AddedField(field); !exists { + return false + } + for _, field := range fields { + if _, exists := m.AddedField(field); !exists { + return false + } + } + return true + } +} + +// HasClearedFields is a condition validating `.FieldCleared` on fields. +func HasClearedFields(field string, fields ...string) Condition { + return func(_ context.Context, m ent.Mutation) bool { + if exists := m.FieldCleared(field); !exists { + return false + } + for _, field := range fields { + if exists := m.FieldCleared(field); !exists { + return false + } + } + return true + } +} + +// HasFields is a condition validating `.Field` on fields. +func HasFields(field string, fields ...string) Condition { + return func(_ context.Context, m ent.Mutation) bool { + if _, exists := m.Field(field); !exists { + return false + } + for _, field := range fields { + if _, exists := m.Field(field); !exists { + return false + } + } + return true + } +} + +// If executes the given hook under condition. +// +// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...))) +func If(hk ent.Hook, cond Condition) ent.Hook { + return func(next ent.Mutator) ent.Mutator { + return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if cond(ctx, m) { + return hk(next).Mutate(ctx, m) + } + return next.Mutate(ctx, m) + }) + } +} + +// On executes the given hook only for the given operation. +// +// hook.On(Log, ent.Delete|ent.Create) +func On(hk ent.Hook, op ent.Op) ent.Hook { + return If(hk, HasOp(op)) +} + +// Unless skips the given hook only for the given operation. +// +// hook.Unless(Log, ent.Update|ent.UpdateOne) +func Unless(hk ent.Hook, op ent.Op) ent.Hook { + return If(hk, Not(HasOp(op))) +} + +// FixedError is a hook returning a fixed error. +func FixedError(err error) ent.Hook { + return func(ent.Mutator) ent.Mutator { + return ent.MutateFunc(func(context.Context, ent.Mutation) (ent.Value, error) { + return nil, err + }) + } +} + +// Reject returns a hook that rejects all operations that match op. +// +// func (T) Hooks() []ent.Hook { +// return []ent.Hook{ +// Reject(ent.Delete|ent.Update), +// } +// } +func Reject(op ent.Op) ent.Hook { + hk := FixedError(fmt.Errorf("%s operation is not allowed", op)) + return On(hk, op) +} + +// Chain acts as a list of hooks and is effectively immutable. +// Once created, it will always hold the same set of hooks in the same order. +type Chain struct { + hooks []ent.Hook +} + +// NewChain creates a new chain of hooks. +func NewChain(hooks ...ent.Hook) Chain { + return Chain{append([]ent.Hook(nil), hooks...)} +} + +// Hook chains the list of hooks and returns the final hook. +func (c Chain) Hook() ent.Hook { + return func(mutator ent.Mutator) ent.Mutator { + for i := len(c.hooks) - 1; i >= 0; i-- { + mutator = c.hooks[i](mutator) + } + return mutator + } +} + +// Append extends a chain, adding the specified hook +// as the last ones in the mutation flow. +func (c Chain) Append(hooks ...ent.Hook) Chain { + newHooks := make([]ent.Hook, 0, len(c.hooks)+len(hooks)) + newHooks = append(newHooks, c.hooks...) + newHooks = append(newHooks, hooks...) + return Chain{newHooks} +} + +// Extend extends a chain, adding the specified chain +// as the last ones in the mutation flow. +func (c Chain) Extend(chain Chain) Chain { + return c.Append(chain.hooks...) +} diff --git a/examples/ent-integration/ent/migrate/migrate.go b/examples/ent-integration/ent/migrate/migrate.go new file mode 100644 index 0000000..1956a6b --- /dev/null +++ b/examples/ent-integration/ent/migrate/migrate.go @@ -0,0 +1,64 @@ +// Code generated by ent, DO NOT EDIT. + +package migrate + +import ( + "context" + "fmt" + "io" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql/schema" +) + +var ( + // WithGlobalUniqueID sets the universal ids options to the migration. + // If this option is enabled, ent migration will allocate a 1<<32 range + // for the ids of each entity (table). + // Note that this option cannot be applied on tables that already exist. + WithGlobalUniqueID = schema.WithGlobalUniqueID + // WithDropColumn sets the drop column option to the migration. + // If this option is enabled, ent migration will drop old columns + // that were used for both fields and edges. This defaults to false. + WithDropColumn = schema.WithDropColumn + // WithDropIndex sets the drop index option to the migration. + // If this option is enabled, ent migration will drop old indexes + // that were defined in the schema. This defaults to false. + // Note that unique constraints are defined using `UNIQUE INDEX`, + // and therefore, it's recommended to enable this option to get more + // flexibility in the schema changes. + WithDropIndex = schema.WithDropIndex + // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true. + WithForeignKeys = schema.WithForeignKeys +) + +// Schema is the API for creating, migrating and dropping a schema. +type Schema struct { + drv dialect.Driver +} + +// NewSchema creates a new schema client. +func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} } + +// Create creates all schema resources. +func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { + return Create(ctx, s, Tables, opts...) +} + +// Create creates all table resources using the given schema driver. +func Create(ctx context.Context, s *Schema, tables []*schema.Table, opts ...schema.MigrateOption) error { + migrate, err := schema.NewMigrate(s.drv, opts...) + if err != nil { + return fmt.Errorf("ent/migrate: %w", err) + } + return migrate.Create(ctx, tables...) +} + +// WriteTo writes the schema changes to w instead of running them against the database. +// +// if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil { +// log.Fatal(err) +// } +func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error { + return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv}}, Tables, opts...) +} diff --git a/examples/ent-integration/ent/migrate/schema.go b/examples/ent-integration/ent/migrate/schema.go new file mode 100644 index 0000000..08c2bfc --- /dev/null +++ b/examples/ent-integration/ent/migrate/schema.go @@ -0,0 +1,50 @@ +// Code generated by ent, DO NOT EDIT. + +package migrate + +import ( + "entgo.io/ent/dialect/sql/schema" + "entgo.io/ent/schema/field" +) + +var ( + // BooksColumns holds the columns for the "books" table. + BooksColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "customer_books", Type: field.TypeInt, Nullable: true}, + } + // BooksTable holds the schema information for the "books" table. + BooksTable = &schema.Table{ + Name: "books", + Columns: BooksColumns, + PrimaryKey: []*schema.Column{BooksColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "books_customers_books", + Columns: []*schema.Column{BooksColumns[1]}, + RefColumns: []*schema.Column{CustomersColumns[0]}, + OnDelete: schema.SetNull, + }, + }, + } + // CustomersColumns holds the columns for the "customers" table. + CustomersColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "name", Type: field.TypeString}, + } + // CustomersTable holds the schema information for the "customers" table. + CustomersTable = &schema.Table{ + Name: "customers", + Columns: CustomersColumns, + PrimaryKey: []*schema.Column{CustomersColumns[0]}, + } + // Tables holds all the tables in the schema. + Tables = []*schema.Table{ + BooksTable, + CustomersTable, + } +) + +func init() { + BooksTable.ForeignKeys[0].RefTable = CustomersTable +} diff --git a/examples/ent-integration/ent/mutation.go b/examples/ent-integration/ent/mutation.go new file mode 100644 index 0000000..ae40e1b --- /dev/null +++ b/examples/ent-integration/ent/mutation.go @@ -0,0 +1,779 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "sync" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/hyuti/factory-go/examples/ent-integration/ent/book" + "github.com/hyuti/factory-go/examples/ent-integration/ent/customer" + "github.com/hyuti/factory-go/examples/ent-integration/ent/predicate" +) + +const ( + // Operation types. + OpCreate = ent.OpCreate + OpDelete = ent.OpDelete + OpDeleteOne = ent.OpDeleteOne + OpUpdate = ent.OpUpdate + OpUpdateOne = ent.OpUpdateOne + + // Node types. + TypeBook = "Book" + TypeCustomer = "Customer" +) + +// BookMutation represents an operation that mutates the Book nodes in the graph. +type BookMutation struct { + config + op Op + typ string + id *int + clearedFields map[string]struct{} + owner *int + clearedowner bool + done bool + oldValue func(context.Context) (*Book, error) + predicates []predicate.Book +} + +var _ ent.Mutation = (*BookMutation)(nil) + +// bookOption allows management of the mutation configuration using functional options. +type bookOption func(*BookMutation) + +// newBookMutation creates new mutation for the Book entity. +func newBookMutation(c config, op Op, opts ...bookOption) *BookMutation { + m := &BookMutation{ + config: c, + op: op, + typ: TypeBook, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withBookID sets the ID field of the mutation. +func withBookID(id int) bookOption { + return func(m *BookMutation) { + var ( + err error + once sync.Once + value *Book + ) + m.oldValue = func(ctx context.Context) (*Book, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Book.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withBook sets the old Book of the mutation. +func withBook(node *Book) bookOption { + return func(m *BookMutation) { + m.oldValue = func(context.Context) (*Book, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m BookMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m BookMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *BookMutation) ID() (id int, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *BookMutation) IDs(ctx context.Context) ([]int, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Book.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetOwnerID sets the "owner" edge to the Customer entity by id. +func (m *BookMutation) SetOwnerID(id int) { + m.owner = &id +} + +// ClearOwner clears the "owner" edge to the Customer entity. +func (m *BookMutation) ClearOwner() { + m.clearedowner = true +} + +// OwnerCleared reports if the "owner" edge to the Customer entity was cleared. +func (m *BookMutation) OwnerCleared() bool { + return m.clearedowner +} + +// OwnerID returns the "owner" edge ID in the mutation. +func (m *BookMutation) OwnerID() (id int, exists bool) { + if m.owner != nil { + return *m.owner, true + } + return +} + +// OwnerIDs returns the "owner" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// OwnerID instead. It exists only for internal usage by the builders. +func (m *BookMutation) OwnerIDs() (ids []int) { + if id := m.owner; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetOwner resets all changes to the "owner" edge. +func (m *BookMutation) ResetOwner() { + m.owner = nil + m.clearedowner = false +} + +// Where appends a list predicates to the BookMutation builder. +func (m *BookMutation) Where(ps ...predicate.Book) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the BookMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *BookMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Book, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *BookMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *BookMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Book). +func (m *BookMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *BookMutation) Fields() []string { + fields := make([]string, 0, 0) + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *BookMutation) Field(name string) (ent.Value, bool) { + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *BookMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, fmt.Errorf("unknown Book field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BookMutation) SetField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Book field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *BookMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *BookMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BookMutation) AddField(name string, value ent.Value) error { + return fmt.Errorf("unknown Book numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *BookMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *BookMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *BookMutation) ClearField(name string) error { + return fmt.Errorf("unknown Book nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *BookMutation) ResetField(name string) error { + return fmt.Errorf("unknown Book field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *BookMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.owner != nil { + edges = append(edges, book.EdgeOwner) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *BookMutation) AddedIDs(name string) []ent.Value { + switch name { + case book.EdgeOwner: + if id := m.owner; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *BookMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *BookMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *BookMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedowner { + edges = append(edges, book.EdgeOwner) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *BookMutation) EdgeCleared(name string) bool { + switch name { + case book.EdgeOwner: + return m.clearedowner + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *BookMutation) ClearEdge(name string) error { + switch name { + case book.EdgeOwner: + m.ClearOwner() + return nil + } + return fmt.Errorf("unknown Book unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *BookMutation) ResetEdge(name string) error { + switch name { + case book.EdgeOwner: + m.ResetOwner() + return nil + } + return fmt.Errorf("unknown Book edge %s", name) +} + +// CustomerMutation represents an operation that mutates the Customer nodes in the graph. +type CustomerMutation struct { + config + op Op + typ string + id *int + name *string + clearedFields map[string]struct{} + books map[int]struct{} + removedbooks map[int]struct{} + clearedbooks bool + done bool + oldValue func(context.Context) (*Customer, error) + predicates []predicate.Customer +} + +var _ ent.Mutation = (*CustomerMutation)(nil) + +// customerOption allows management of the mutation configuration using functional options. +type customerOption func(*CustomerMutation) + +// newCustomerMutation creates new mutation for the Customer entity. +func newCustomerMutation(c config, op Op, opts ...customerOption) *CustomerMutation { + m := &CustomerMutation{ + config: c, + op: op, + typ: TypeCustomer, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withCustomerID sets the ID field of the mutation. +func withCustomerID(id int) customerOption { + return func(m *CustomerMutation) { + var ( + err error + once sync.Once + value *Customer + ) + m.oldValue = func(ctx context.Context) (*Customer, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Customer.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withCustomer sets the old Customer of the mutation. +func withCustomer(node *Customer) customerOption { + return func(m *CustomerMutation) { + m.oldValue = func(context.Context) (*Customer, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m CustomerMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m CustomerMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *CustomerMutation) ID() (id int, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *CustomerMutation) IDs(ctx context.Context) ([]int, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Customer.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetName sets the "name" field. +func (m *CustomerMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *CustomerMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Customer entity. +// If the Customer object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CustomerMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *CustomerMutation) ResetName() { + m.name = nil +} + +// AddBookIDs adds the "books" edge to the Book entity by ids. +func (m *CustomerMutation) AddBookIDs(ids ...int) { + if m.books == nil { + m.books = make(map[int]struct{}) + } + for i := range ids { + m.books[ids[i]] = struct{}{} + } +} + +// ClearBooks clears the "books" edge to the Book entity. +func (m *CustomerMutation) ClearBooks() { + m.clearedbooks = true +} + +// BooksCleared reports if the "books" edge to the Book entity was cleared. +func (m *CustomerMutation) BooksCleared() bool { + return m.clearedbooks +} + +// RemoveBookIDs removes the "books" edge to the Book entity by IDs. +func (m *CustomerMutation) RemoveBookIDs(ids ...int) { + if m.removedbooks == nil { + m.removedbooks = make(map[int]struct{}) + } + for i := range ids { + delete(m.books, ids[i]) + m.removedbooks[ids[i]] = struct{}{} + } +} + +// RemovedBooks returns the removed IDs of the "books" edge to the Book entity. +func (m *CustomerMutation) RemovedBooksIDs() (ids []int) { + for id := range m.removedbooks { + ids = append(ids, id) + } + return +} + +// BooksIDs returns the "books" edge IDs in the mutation. +func (m *CustomerMutation) BooksIDs() (ids []int) { + for id := range m.books { + ids = append(ids, id) + } + return +} + +// ResetBooks resets all changes to the "books" edge. +func (m *CustomerMutation) ResetBooks() { + m.books = nil + m.clearedbooks = false + m.removedbooks = nil +} + +// Where appends a list predicates to the CustomerMutation builder. +func (m *CustomerMutation) Where(ps ...predicate.Customer) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the CustomerMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *CustomerMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Customer, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *CustomerMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *CustomerMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Customer). +func (m *CustomerMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *CustomerMutation) Fields() []string { + fields := make([]string, 0, 1) + if m.name != nil { + fields = append(fields, customer.FieldName) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *CustomerMutation) Field(name string) (ent.Value, bool) { + switch name { + case customer.FieldName: + return m.Name() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *CustomerMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case customer.FieldName: + return m.OldName(ctx) + } + return nil, fmt.Errorf("unknown Customer field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *CustomerMutation) SetField(name string, value ent.Value) error { + switch name { + case customer.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + } + return fmt.Errorf("unknown Customer field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *CustomerMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *CustomerMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *CustomerMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Customer numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *CustomerMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *CustomerMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *CustomerMutation) ClearField(name string) error { + return fmt.Errorf("unknown Customer nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *CustomerMutation) ResetField(name string) error { + switch name { + case customer.FieldName: + m.ResetName() + return nil + } + return fmt.Errorf("unknown Customer field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *CustomerMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.books != nil { + edges = append(edges, customer.EdgeBooks) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *CustomerMutation) AddedIDs(name string) []ent.Value { + switch name { + case customer.EdgeBooks: + ids := make([]ent.Value, 0, len(m.books)) + for id := range m.books { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *CustomerMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + if m.removedbooks != nil { + edges = append(edges, customer.EdgeBooks) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *CustomerMutation) RemovedIDs(name string) []ent.Value { + switch name { + case customer.EdgeBooks: + ids := make([]ent.Value, 0, len(m.removedbooks)) + for id := range m.removedbooks { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *CustomerMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedbooks { + edges = append(edges, customer.EdgeBooks) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *CustomerMutation) EdgeCleared(name string) bool { + switch name { + case customer.EdgeBooks: + return m.clearedbooks + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *CustomerMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Customer unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *CustomerMutation) ResetEdge(name string) error { + switch name { + case customer.EdgeBooks: + m.ResetBooks() + return nil + } + return fmt.Errorf("unknown Customer edge %s", name) +} diff --git a/examples/ent-integration/ent/predicate/predicate.go b/examples/ent-integration/ent/predicate/predicate.go new file mode 100644 index 0000000..5e83391 --- /dev/null +++ b/examples/ent-integration/ent/predicate/predicate.go @@ -0,0 +1,13 @@ +// Code generated by ent, DO NOT EDIT. + +package predicate + +import ( + "entgo.io/ent/dialect/sql" +) + +// Book is the predicate function for book builders. +type Book func(*sql.Selector) + +// Customer is the predicate function for customer builders. +type Customer func(*sql.Selector) diff --git a/examples/ent-integration/ent/runtime.go b/examples/ent-integration/ent/runtime.go new file mode 100644 index 0000000..793d053 --- /dev/null +++ b/examples/ent-integration/ent/runtime.go @@ -0,0 +1,9 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +// The init function reads all schema descriptors with runtime code +// (default values, validators, hooks and policies) and stitches it +// to their package variables. +func init() { +} diff --git a/examples/ent-integration/ent/runtime/runtime.go b/examples/ent-integration/ent/runtime/runtime.go new file mode 100644 index 0000000..d5aabcd --- /dev/null +++ b/examples/ent-integration/ent/runtime/runtime.go @@ -0,0 +1,10 @@ +// Code generated by ent, DO NOT EDIT. + +package runtime + +// The schema-stitching logic is generated in github.com/hyuti/factory-go/examples/ent-integration/ent/runtime.go + +const ( + Version = "v0.11.10" // Version of ent codegen. + Sum = "h1:iqn32ybY5HRW3xSAyMNdNKpZhKgMf1Zunsej9yPKUI8=" // Sum of ent codegen. +) diff --git a/examples/ent-integration/ent/schema/book.go b/examples/ent-integration/ent/schema/book.go new file mode 100644 index 0000000..06db598 --- /dev/null +++ b/examples/ent-integration/ent/schema/book.go @@ -0,0 +1,23 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" +) + +// Book holds the schema definition for the Book entity. +type Book struct { + ent.Schema +} + +// Fields of the Book. +func (Book) Fields() []ent.Field { + return nil +} + +// Edges of the Book. +func (Book) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("owner", Customer.Type).Ref("books").Unique(), + } +} diff --git a/examples/ent-integration/ent/schema/customer.go b/examples/ent-integration/ent/schema/customer.go new file mode 100644 index 0000000..bcc1d67 --- /dev/null +++ b/examples/ent-integration/ent/schema/customer.go @@ -0,0 +1,26 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +// Customer holds the schema definition for the Customer entity. +type Customer struct { + ent.Schema +} + +// Fields of the Customer. +func (Customer) Fields() []ent.Field { + return []ent.Field{ + field.String("name"), + } +} + +// Edges of the Customer. +func (Customer) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("books", Book.Type), + } +} diff --git a/examples/ent-integration/ent/tx.go b/examples/ent-integration/ent/tx.go new file mode 100644 index 0000000..900ade2 --- /dev/null +++ b/examples/ent-integration/ent/tx.go @@ -0,0 +1,213 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "sync" + + "entgo.io/ent/dialect" +) + +// Tx is a transactional client that is created by calling Client.Tx(). +type Tx struct { + config + // Book is the client for interacting with the Book builders. + Book *BookClient + // Customer is the client for interacting with the Customer builders. + Customer *CustomerClient + + // lazily loaded. + client *Client + clientOnce sync.Once + // ctx lives for the life of the transaction. It is + // the same context used by the underlying connection. + ctx context.Context +} + +type ( + // Committer is the interface that wraps the Commit method. + Committer interface { + Commit(context.Context, *Tx) error + } + + // The CommitFunc type is an adapter to allow the use of ordinary + // function as a Committer. If f is a function with the appropriate + // signature, CommitFunc(f) is a Committer that calls f. + CommitFunc func(context.Context, *Tx) error + + // CommitHook defines the "commit middleware". A function that gets a Committer + // and returns a Committer. For example: + // + // hook := func(next ent.Committer) ent.Committer { + // return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error { + // // Do some stuff before. + // if err := next.Commit(ctx, tx); err != nil { + // return err + // } + // // Do some stuff after. + // return nil + // }) + // } + // + CommitHook func(Committer) Committer +) + +// Commit calls f(ctx, m). +func (f CommitFunc) Commit(ctx context.Context, tx *Tx) error { + return f(ctx, tx) +} + +// Commit commits the transaction. +func (tx *Tx) Commit() error { + txDriver := tx.config.driver.(*txDriver) + var fn Committer = CommitFunc(func(context.Context, *Tx) error { + return txDriver.tx.Commit() + }) + txDriver.mu.Lock() + hooks := append([]CommitHook(nil), txDriver.onCommit...) + txDriver.mu.Unlock() + for i := len(hooks) - 1; i >= 0; i-- { + fn = hooks[i](fn) + } + return fn.Commit(tx.ctx, tx) +} + +// OnCommit adds a hook to call on commit. +func (tx *Tx) OnCommit(f CommitHook) { + txDriver := tx.config.driver.(*txDriver) + txDriver.mu.Lock() + txDriver.onCommit = append(txDriver.onCommit, f) + txDriver.mu.Unlock() +} + +type ( + // Rollbacker is the interface that wraps the Rollback method. + Rollbacker interface { + Rollback(context.Context, *Tx) error + } + + // The RollbackFunc type is an adapter to allow the use of ordinary + // function as a Rollbacker. If f is a function with the appropriate + // signature, RollbackFunc(f) is a Rollbacker that calls f. + RollbackFunc func(context.Context, *Tx) error + + // RollbackHook defines the "rollback middleware". A function that gets a Rollbacker + // and returns a Rollbacker. For example: + // + // hook := func(next ent.Rollbacker) ent.Rollbacker { + // return ent.RollbackFunc(func(ctx context.Context, tx *ent.Tx) error { + // // Do some stuff before. + // if err := next.Rollback(ctx, tx); err != nil { + // return err + // } + // // Do some stuff after. + // return nil + // }) + // } + // + RollbackHook func(Rollbacker) Rollbacker +) + +// Rollback calls f(ctx, m). +func (f RollbackFunc) Rollback(ctx context.Context, tx *Tx) error { + return f(ctx, tx) +} + +// Rollback rollbacks the transaction. +func (tx *Tx) Rollback() error { + txDriver := tx.config.driver.(*txDriver) + var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error { + return txDriver.tx.Rollback() + }) + txDriver.mu.Lock() + hooks := append([]RollbackHook(nil), txDriver.onRollback...) + txDriver.mu.Unlock() + for i := len(hooks) - 1; i >= 0; i-- { + fn = hooks[i](fn) + } + return fn.Rollback(tx.ctx, tx) +} + +// OnRollback adds a hook to call on rollback. +func (tx *Tx) OnRollback(f RollbackHook) { + txDriver := tx.config.driver.(*txDriver) + txDriver.mu.Lock() + txDriver.onRollback = append(txDriver.onRollback, f) + txDriver.mu.Unlock() +} + +// Client returns a Client that binds to current transaction. +func (tx *Tx) Client() *Client { + tx.clientOnce.Do(func() { + tx.client = &Client{config: tx.config} + tx.client.init() + }) + return tx.client +} + +func (tx *Tx) init() { + tx.Book = NewBookClient(tx.config) + tx.Customer = NewCustomerClient(tx.config) +} + +// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. +// The idea is to support transactions without adding any extra code to the builders. +// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance. +// Commit and Rollback are nop for the internal builders and the user must call one +// of them in order to commit or rollback the transaction. +// +// If a closed transaction is embedded in one of the generated entities, and the entity +// applies a query, for example: Book.QueryXXX(), the query will be executed +// through the driver which created this transaction. +// +// Note that txDriver is not goroutine safe. +type txDriver struct { + // the driver we started the transaction from. + drv dialect.Driver + // tx is the underlying transaction. + tx dialect.Tx + // completion hooks. + mu sync.Mutex + onCommit []CommitHook + onRollback []RollbackHook +} + +// newTx creates a new transactional driver. +func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) { + tx, err := drv.Tx(ctx) + if err != nil { + return nil, err + } + return &txDriver{tx: tx, drv: drv}, nil +} + +// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls +// from the internal builders. Should be called only by the internal builders. +func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil } + +// Dialect returns the dialect of the driver we started the transaction from. +func (tx *txDriver) Dialect() string { return tx.drv.Dialect() } + +// Close is a nop close. +func (*txDriver) Close() error { return nil } + +// Commit is a nop commit for the internal builders. +// User must call `Tx.Commit` in order to commit the transaction. +func (*txDriver) Commit() error { return nil } + +// Rollback is a nop rollback for the internal builders. +// User must call `Tx.Rollback` in order to rollback the transaction. +func (*txDriver) Rollback() error { return nil } + +// Exec calls tx.Exec. +func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error { + return tx.tx.Exec(ctx, query, args, v) +} + +// Query calls tx.Query. +func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error { + return tx.tx.Query(ctx, query, args, v) +} + +var _ dialect.Driver = (*txDriver)(nil) diff --git a/examples/ent-integration/go.mod b/examples/ent-integration/go.mod new file mode 100644 index 0000000..e67cf06 --- /dev/null +++ b/examples/ent-integration/go.mod @@ -0,0 +1,24 @@ +module github.com/hyuti/factory-go/examples/ent-integration + +go 1.19 + +require ( + entgo.io/ent v0.11.10 + github.com/Pallinder/go-randomdata v1.2.0 + github.com/hyuti/factory-go v0.0.0-20230118112605-a647dcd033b1 + github.com/mattn/go-sqlite3 v1.14.16 +) + +require ( + ariga.io/atlas v0.9.2-0.20230303073438-03a4779a6338 // indirect + github.com/agext/levenshtein v1.2.1 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/go-openapi/inflect v0.19.0 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/hcl/v2 v2.13.0 // indirect + github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect + github.com/zclconf/go-cty v1.8.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/text v0.5.0 // indirect +) diff --git a/examples/ent-integration/go.sum b/examples/ent-integration/go.sum new file mode 100644 index 0000000..a1a883c --- /dev/null +++ b/examples/ent-integration/go.sum @@ -0,0 +1,224 @@ +ariga.io/atlas v0.9.2-0.20230303073438-03a4779a6338 h1:8kmSV3mbQKn0niZ/EdE11uhFvFKiW1VlaqVBIYOyahM= +ariga.io/atlas v0.9.2-0.20230303073438-03a4779a6338/go.mod h1:T230JFcENj4ZZzMkZrXFDSkv+2kXkUgpJ5FQQ5hMcKU= +entgo.io/ent v0.11.10 h1:iqn32ybY5HRW3xSAyMNdNKpZhKgMf1Zunsej9yPKUI8= +entgo.io/ent v0.11.10/go.mod h1:mzTZ0trE+jCQw/fnzijbm5Mck/l8Gbg7gC/+L1COyzM= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= +github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= +github.com/go-pg/pg v8.0.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= +github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hyuti/factory-go v0.0.0-20230118112605-a647dcd033b1 h1:g6gkvRkyJwuWG1brG5vJqdA23BEYuzwZLZxAgCU/bVg= +github.com/hyuti/factory-go v0.0.0-20230118112605-a647dcd033b1/go.mod h1:jE0F6H+rRNtsJU61x1Rxg1pFxg9lqN9oGmebS9DEiS4= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= +github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= diff --git a/examples/ent-integration/main.go b/examples/ent-integration/main.go new file mode 100644 index 0000000..759a923 --- /dev/null +++ b/examples/ent-integration/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + "fmt" + "log" + + "github.com/hyuti/factory-go/examples/ent-integration/ent" +) + +func main() { + c, _ := ent.New() + defer c.Close() + ctx := context.Background() + + if err := c.Schema.Create(ctx); err != nil { + log.Fatalf("failed creating schema resources: %v", err) + } + + customer, _ := ent.MustCustomerFactory(ent.Opt{Key: "Name", Value: "foobar"}).CreateWithClient(ctx, c) + fmt.Println("Customer.ID: ", customer.ID, " Customer.Name: ", customer.Name) + // Output: + // Customer.ID: 1 Customer.Name: foobar + + bookWithOwner, _ := ent.MustBookFactory(ent.Opt{Key: "OwnerID", Value: customer.ID}).CreateWithClient(ctx, c) + fmt.Println("Book.ID: ", bookWithOwner.ID) + fmt.Println("\tOwner.ID: ", bookWithOwner.QueryOwner().FirstIDX(ctx), " Owner.Name: ", bookWithOwner.QueryOwner().FirstX(ctx).Name) + // Output: + // Book.ID: 1 + // Owner.ID: 1 Owner.Name: foobar + + book, _ := ent.MustBookFactory().CreateWithClient(ctx, c) + fmt.Println("Book.ID: ", book.ID) + fmt.Println("\tOwner.ID: ", book.QueryOwner().FirstIDX(ctx), " Owner.Name: ", book.QueryOwner().FirstX(ctx).Name) + // Output: + // Book.ID: 2 + // Owner.ID: 2 Owner.Name: random-name +} diff --git a/examples/factory_with_randomdata.go b/examples/factory-with-random-data/main.go similarity index 67% rename from examples/factory_with_randomdata.go rename to examples/factory-with-random-data/main.go index 4308057..d1dd5a9 100644 --- a/examples/factory_with_randomdata.go +++ b/examples/factory-with-random-data/main.go @@ -2,8 +2,9 @@ package main import ( "fmt" + "github.com/Pallinder/go-randomdata" - "github.com/bluele/factory-go/factory" + "github.com/hyuti/factory-go/factory" ) type User struct { @@ -14,11 +15,11 @@ type User struct { var UserFactory = factory.NewFactory( &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { return randomdata.FullName(randomdata.RandomGender), nil -}).Attr("Location", func(args factory.Args) (interface{}, error) { +}).Attr("Location", func(args factory.Args) (any, error) { return randomdata.City(), nil }) diff --git a/examples/file-factory/main.go b/examples/file-factory/main.go new file mode 100644 index 0000000..37ffe59 --- /dev/null +++ b/examples/file-factory/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "os" + + "github.com/hyuti/factory-go/factory" +) + +type User struct { + Img string +} + +var UserFactory = factory.NewFactory( + &User{}, +).Png("Img", func(f *os.File) (any, error) { + return f, nil +}) + +func main() { + for i := 0; i < 3; i++ { + user := UserFactory.MustCreate().(*User) + fmt.Println("img:", user.Img) + } + defer UserFactory.Clean() +} diff --git a/examples/go_pg_integration.go b/examples/go-pg-integration/main.go similarity index 84% rename from examples/go_pg_integration.go rename to examples/go-pg-integration/main.go index 36eb0f8..40bacf1 100644 --- a/examples/go_pg_integration.go +++ b/examples/go-pg-integration/main.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/bluele/factory-go/factory" "github.com/go-pg/pg" "github.com/go-pg/pg/orm" + "github.com/hyuti/factory-go/factory" ) type Group struct { @@ -22,9 +22,9 @@ type User struct { var UserFactory = factory.NewFactory( &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { user := args.Instance().(*User) return fmt.Sprintf("user-%d", user.ID), nil }).OnCreate(func(args factory.Args) error { @@ -34,9 +34,9 @@ var UserFactory = factory.NewFactory( var GroupFactory = factory.NewFactory( &Group{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { group := args.Instance().(*Group) return fmt.Sprintf("group-%d", group.ID), nil }).OnCreate(func(args factory.Args) error { @@ -45,7 +45,7 @@ var GroupFactory = factory.NewFactory( }) func createTestSchema(db *pg.DB) error { - tables := []interface{}{ + tables := []any{ &Group{}, &User{}, } diff --git a/examples/gorm_integration.go b/examples/gorm-integration/main.go similarity index 82% rename from examples/gorm_integration.go rename to examples/gorm-integration/main.go index 82f4c57..e266aa9 100644 --- a/examples/gorm_integration.go +++ b/examples/gorm-integration/main.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/bluele/factory-go/factory" + "github.com/hyuti/factory-go/factory" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" ) @@ -22,9 +22,9 @@ type User struct { var UserFactory = factory.NewFactory( &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { user := args.Instance().(*User) return fmt.Sprintf("user-%d", user.ID), nil }).OnCreate(func(args factory.Args) error { @@ -34,9 +34,9 @@ var UserFactory = factory.NewFactory( var GroupFactory = factory.NewFactory( &Group{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { group := args.Instance().(*Group) return fmt.Sprintf("group-%d", group.ID), nil }).OnCreate(func(args factory.Args) error { diff --git a/examples/input-output-different-type/main.go b/examples/input-output-different-type/main.go new file mode 100644 index 0000000..ae7ebff --- /dev/null +++ b/examples/input-output-different-type/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + "reflect" + + "github.com/Pallinder/go-randomdata" + "github.com/hyuti/factory-go/factory" +) + +// This example illustrates on how to use factory in a case where you have different type of input and output +type ( + CustomerInput struct { + Name string + } + Customer struct { + ID string + Name string + } +) + +func CustomerRepository(i *CustomerInput) *Customer { + return &Customer{ + ID: "foo", + Name: i.Name, + } +} + +var customerFactory = factory.NewFactory( + &CustomerInput{}, +).Attr("Name", func(a factory.Args) (any, error) { + return randomdata.FullName(randomdata.RandomGender), nil +}) + +func CustomerFactory(opts map[string]any) *factory.Factory { + return factory.NewFactory( + &Customer{}, + ).OnCreate(func(a factory.Args) error { + ctx := a.Context() + iAny, err := customerFactory.CreateWithContextAndOption(ctx, opts) + if err != nil { + return err + } + i, ok := iAny.(*CustomerInput) + if !ok { + return fmt.Errorf("unexpected type %t", iAny) + } + e := CustomerRepository(i) + inst := a.Instance() + dst := reflect.ValueOf(inst) + src := reflect.ValueOf(e).Elem() + dst.Elem().Set(src) + return nil + }) +} + +func main() { + customerAny, err := CustomerFactory(nil).Create() + if err != nil { + fmt.Println(err) + } else { + customer, ok := customerAny.(*Customer) + if !ok { + fmt.Printf("unexpected type %t\n", customerAny) + } else { + fmt.Printf("Name: %s, ID: %s\n", customer.Name, customer.ID) + } + } +} diff --git a/examples/ordering-attr-with-formatter/main.go b/examples/ordering-attr-with-formatter/main.go new file mode 100644 index 0000000..2e35c49 --- /dev/null +++ b/examples/ordering-attr-with-formatter/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + + "github.com/hyuti/factory-go/factory" +) + +type ( + Customer struct { + ID int + Name string + } + Order struct { + ID int + CustomerName string + CustomerID int + } +) + +func GetCustomerByID(id int) *Customer { + return &Customer{ + ID: id, + Name: "foo", + } +} + +var CustomerFactory = factory.NewFactory( + &Customer{ID: 1}, +).Attr("Name", func(a factory.Args) (any, error) { + return "foo", nil +}) + +var OrderFactory = factory.NewFactory( + &Order{}, + +// define CustomerID before CustomerName so that CustomerID field is generated before CustomerName +// user a formatter to retrieve ID from Customer after Customer is created by CustomerFactory +).SubFactory("CustomerID", CustomerFactory, func(i any) (any, error) { + e, ok := i.(*Customer) + if !ok { + return nil, fmt.Errorf("unexpected type %t", i) + } + return e.ID, nil +}).Attr("CustomerName", func(a factory.Args) (any, error) { + inst, ok := a.Instance().(*Order) + if !ok { + return nil, fmt.Errorf("unexpected type %t", a.Instance()) + } + e := GetCustomerByID(inst.CustomerID) + return e.Name, nil +}) + +func main() { + order := OrderFactory.MustCreate().(*Order) + fmt.Println("ID:", order.ID, " CustomerName:", order.CustomerName, " CustomerID:", order.CustomerID) +} diff --git a/examples/recursive_factory.go b/examples/recursive-factory/main.go similarity index 82% rename from examples/recursive_factory.go rename to examples/recursive-factory/main.go index c037f1d..8ec6b7e 100644 --- a/examples/recursive_factory.go +++ b/examples/recursive-factory/main.go @@ -2,8 +2,9 @@ package main import ( "fmt" + "github.com/Pallinder/go-randomdata" - "github.com/bluele/factory-go/factory" + "github.com/hyuti/factory-go/factory" ) type User struct { @@ -17,9 +18,9 @@ var UserFactory = factory.NewFactory( ) func init() { - UserFactory.SeqInt("ID", func(n int) (interface{}, error) { + UserFactory.SeqInt("ID", func(n int) (any, error) { return n, nil - }).Attr("Name", func(args factory.Args) (interface{}, error) { + }).Attr("Name", func(args factory.Args) (any, error) { return randomdata.FullName(randomdata.RandomGender), nil }).SubRecursiveFactory("CloseFriend", UserFactory, func() int { return 2 }) // recursive depth is always 2 } diff --git a/examples/refer_to_parent_factory.go b/examples/refer-to-parent-factory/main.go similarity index 72% rename from examples/refer_to_parent_factory.go rename to examples/refer-to-parent-factory/main.go index e48de24..738df23 100644 --- a/examples/refer_to_parent_factory.go +++ b/examples/refer-to-parent-factory/main.go @@ -2,7 +2,8 @@ package main import ( "fmt" - "github.com/bluele/factory-go/factory" + + "github.com/hyuti/factory-go/factory" ) type User struct { @@ -19,12 +20,12 @@ type Group struct { var UserFactory = factory.NewFactory( &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { user := args.Instance().(*User) return fmt.Sprintf("user-%d", user.ID), nil -}).Attr("Group", func(args factory.Args) (interface{}, error) { +}).Attr("Group", func(args factory.Args) (any, error) { if parent := args.Parent(); parent != nil { // if args have parent, use it. return parent.Instance(), nil @@ -34,9 +35,9 @@ var UserFactory = factory.NewFactory( var GroupFactory = factory.NewFactory( &Group{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return 2 - n%2, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { group := args.Instance().(*Group) return fmt.Sprintf("group-%d", group.ID), nil }).SubSliceFactory("Users", UserFactory, func() int { return 3 }) diff --git a/examples/simple_factory.go b/examples/simple-factory/main.go similarity index 75% rename from examples/simple_factory.go rename to examples/simple-factory/main.go index 9b5e84d..dd0e4e8 100644 --- a/examples/simple_factory.go +++ b/examples/simple-factory/main.go @@ -2,7 +2,8 @@ package main import ( "fmt" - "github.com/bluele/factory-go/factory" + + "github.com/hyuti/factory-go/factory" ) type User struct { @@ -14,9 +15,9 @@ type User struct { // 'Location: "Tokyo"' is default value. var UserFactory = factory.NewFactory( &User{Location: "Tokyo"}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { user := args.Instance().(*User) return fmt.Sprintf("user-%d", user.ID), nil }) diff --git a/examples/subfactory_slice.go b/examples/subfactory-slice/main.go similarity index 73% rename from examples/subfactory_slice.go rename to examples/subfactory-slice/main.go index e6b889e..d8ee53a 100644 --- a/examples/subfactory_slice.go +++ b/examples/subfactory-slice/main.go @@ -2,7 +2,8 @@ package main import ( "fmt" - "github.com/bluele/factory-go/factory" + + "github.com/hyuti/factory-go/factory" ) type Post struct { @@ -18,18 +19,18 @@ type User struct { var PostFactory = factory.NewFactory( &Post{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Content", func(args factory.Args) (interface{}, error) { +}).Attr("Content", func(args factory.Args) (any, error) { post := args.Instance().(*Post) return fmt.Sprintf("post-%d", post.ID), nil }) var UserFactory = factory.NewFactory( &User{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { user := args.Instance().(*User) return fmt.Sprintf("user-%d", user.ID), nil }).SubSliceFactory("Posts", PostFactory, func() int { return 3 }) diff --git a/examples/subfactory.go b/examples/subfactory/main.go similarity index 74% rename from examples/subfactory.go rename to examples/subfactory/main.go index 4b14392..70adf19 100644 --- a/examples/subfactory.go +++ b/examples/subfactory/main.go @@ -2,7 +2,8 @@ package main import ( "fmt" - "github.com/bluele/factory-go/factory" + + "github.com/hyuti/factory-go/factory" ) type Group struct { @@ -19,9 +20,9 @@ type User struct { var GroupFactory = factory.NewFactory( &Group{}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return 2 - n%2, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { group := args.Instance().(*Group) return fmt.Sprintf("group-%d", group.ID), nil }) @@ -29,9 +30,9 @@ var GroupFactory = factory.NewFactory( // 'Location: "Tokyo"' is default value. var UserFactory = factory.NewFactory( &User{Location: "Tokyo"}, -).SeqInt("ID", func(n int) (interface{}, error) { +).SeqInt("ID", func(n int) (any, error) { return n, nil -}).Attr("Name", func(args factory.Args) (interface{}, error) { +}).Attr("Name", func(args factory.Args) (any, error) { user := args.Instance().(*User) return fmt.Sprintf("user-%d", user.ID), nil }).SubFactory("Group", GroupFactory) diff --git a/factory/factory.go b/factory/factory.go index 5ddce81..e7904ff 100644 --- a/factory/factory.go +++ b/factory/factory.go @@ -3,6 +3,8 @@ package factory import ( "context" "errors" + "fmt" + "os" "reflect" "strconv" "sync/atomic" @@ -11,207 +13,142 @@ import ( var ( TagName = "factory" emptyValue = reflect.Value{} + defaultDir = "temp" ) -type Factory struct { - model interface{} - numField int - rt reflect.Type - rv *reflect.Value - attrGens []*attrGenerator - nameIndexMap map[string]int // pair for attribute name and field index. - isPtr bool - onCreate func(Args) error -} - +//go:generate mockgen -source=factory.go -destination=factory_mocks.go -package=factory type Args interface { - Instance() interface{} + Instance() any Parent() Args Context() context.Context pipeline(int) *pipeline } -type argsStruct struct { - ctx context.Context - rv *reflect.Value - pl *pipeline -} - -// Instance returns a object to which the generator declared just before is applied -func (args *argsStruct) Instance() interface{} { - return args.rv.Interface() -} - -// Parent returns a parent argument if current factory is a subfactory of parent -func (args *argsStruct) Parent() Args { - if args.pl == nil { - return nil - } - return args.pl.parent -} - -func (args *argsStruct) pipeline(num int) *pipeline { - if args.pl == nil { - return newPipeline(num) - } - return args.pl -} - -func (args *argsStruct) Context() context.Context { - return args.ctx -} - -func (args *argsStruct) UpdateContext(ctx context.Context) { - args.ctx = ctx -} - -type Stacks []*int64 - -func (st *Stacks) Size(idx int) int64 { - return *(*st)[idx] -} - -// Set method is not goroutine safe. -func (st *Stacks) Set(idx, val int) { - var ini int64 = 0 - (*st)[idx] = &ini - atomic.StoreInt64((*st)[idx], int64(val)) -} - -func (st *Stacks) Push(idx, delta int) { - atomic.AddInt64((*st)[idx], int64(delta)) -} - -func (st *Stacks) Pop(idx, delta int) { - atomic.AddInt64((*st)[idx], -int64(delta)) -} - -func (st *Stacks) Next(idx int) bool { - st.Pop(idx, 1) - return *(*st)[idx] >= 0 -} - -func (st *Stacks) Has(idx int) bool { - return (*st)[idx] != nil -} - -type pipeline struct { - stacks Stacks - parent Args -} - -func newPipeline(size int) *pipeline { - return &pipeline{stacks: make(Stacks, size)} -} - -func (pl *pipeline) Next(args Args) *pipeline { - npl := &pipeline{} - npl.parent = args - npl.stacks = make(Stacks, len(pl.stacks)) - for i, sptr := range pl.stacks { - if sptr != nil { - stack := *sptr - npl.stacks[i] = &stack - } +type ( + Formatter func(any) (any, error) + Generator func(Args) (any, error) + Factory struct { + numField int + curIdx int + isPtr bool + model any + rt reflect.Type + rv *reflect.Value + attrGens []*attrGenerator + orderingAttrGens []*attrGenerator + nameIndexMap map[string]int // pair for attribute name and field index. + onCreate func(Args) error + filesCreated []string } - return npl -} +) // NewFactory returns a new factory for specified model class // Each generator is applied in the order in which they are declared -func NewFactory(model interface{}) *Factory { +func NewFactory(model any) *Factory { fa := &Factory{} fa.model = model fa.nameIndexMap = make(map[string]int) + fa.filesCreated = make([]string, 0) fa.init() return fa } -type attrGenerator struct { - genFunc func(Args) (interface{}, error) - key string - value interface{} - isNil bool -} - -func (fa *Factory) init() { - rt := reflect.TypeOf(fa.model) - rv := reflect.ValueOf(fa.model) - - fa.isPtr = rt.Kind() == reflect.Ptr - - if fa.isPtr { - rt = rt.Elem() - rv = rv.Elem() +func (fa *Factory) wrapWithFormatter(gen Generator, formatters ...Formatter) Generator { + return func(a Args) (any, error) { + ret, err := gen(a) + if err != nil { + return nil, err + } + for _, f := range formatters { + ret, err = f(ret) + if err != nil { + return nil, err + } + } + return ret, nil } +} - fa.numField = rv.NumField() - - for i := 0; i < fa.numField; i++ { - tf := rt.Field(i) - vf := rv.Field(i) - ag := &attrGenerator{} +func (fa *Factory) Attr(name string, gen Generator, formatters ...Formatter) *Factory { + return fa.fillAttrGen(nil, name, fa.wrapWithFormatter(gen, formatters...)) +} - if !vf.CanSet() || (tf.Type.Kind() == reflect.Ptr && vf.IsNil()) { - ag.isNil = true - } else { - ag.value = vf.Interface() - } +// Png function is a shortcut for creating png files. No file name is required. +// For specifying file name, see File function +func (fa *Factory) Png(name string, gen func(*os.File) (any, error), formatters ...Formatter) *Factory { + return fa.File(name, "png", gen, formatters...) +} - attrName := getAttrName(tf, TagName) - ag.key = attrName - fa.nameIndexMap[attrName] = i - fa.attrGens = append(fa.attrGens, ag) - } +// Jpg function is a shortcut for creating jpg files. No file name is required. +// For specifying file name, see File function +func (fa *Factory) Jpg(name string, gen func(*os.File) (any, error), formatters ...Formatter) *Factory { + return fa.File(name, "jpg", gen, formatters...) +} - fa.rt = rt - fa.rv = &rv +// Default directory containing temporary files is "dir" directory at the same level of directory executing commands. +// ext is extension eg "txt", "bat", "xlsx", etc +func (fa *Factory) File(name, ext string, gen func(*os.File) (any, error), formatters ...Formatter) *Factory { + return fa.FileWithDir(name, ext, defaultDir, gen, formatters...) } -func (fa *Factory) modelName() string { - return fa.rt.Name() +// Remove all temporary files if existed +func (fa *Factory) Clean() { + for _, n := range fa.filesCreated { + os.Remove(n) + } + // TODO: this implementation needs a more optimal memory alternative because it leaves lots of memory unused after assigning to nil + fa.filesCreated = nil } -func (fa *Factory) Attr(name string, gen func(Args) (interface{}, error)) *Factory { - idx := fa.checkIdx(name) - fa.attrGens[idx].genFunc = gen - return fa +// You should call Clean function in defer after invoking FileWithDir function (See Clean) to clean all unused files. +// Otherwise, files will not be removed +func (fa *Factory) FileWithDir(name, ext, dir string, gen func(*os.File) (any, error), formatters ...Formatter) *Factory { + file := fmt.Sprintf("*.%s", ext) + genFunc := func(krgs Args) (any, error) { + f, err := os.CreateTemp(dir, file) + if err != nil { + return nil, err + } + _, err = gen(f) + if err != nil { + return nil, err + } + fa.filesCreated = append(fa.filesCreated, f.Name()) + return f.Name(), nil + } + return fa.fillAttrGen(nil, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SeqInt(name string, gen func(int) (interface{}, error)) *Factory { - idx := fa.checkIdx(name) - var seq int64 = 0 - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { +func (fa *Factory) SeqInt(name string, gen func(int) (any, error), formatters ...Formatter) *Factory { + seq := int64(0) + genFunc := func(krgs Args) (any, error) { new := atomic.AddInt64(&seq, 1) return gen(int(new)) } - return fa + return fa.fillAttrGen(nil, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SeqInt64(name string, gen func(int64) (interface{}, error)) *Factory { - idx := fa.checkIdx(name) - var seq int64 = 0 - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { +func (fa *Factory) SeqInt64(name string, gen func(int64) (any, error), formatters ...Formatter) *Factory { + seq := int64(0) + genFunc := func(args Args) (any, error) { new := atomic.AddInt64(&seq, 1) return gen(new) } - return fa + return fa.fillAttrGen(nil, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SeqString(name string, gen func(string) (interface{}, error)) *Factory { - idx := fa.checkIdx(name) - var seq int64 = 0 - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { +func (fa *Factory) SeqString(name string, gen func(string) (any, error), formatters ...Formatter) *Factory { + seq := int64(0) + genFunc := func(args Args) (any, error) { new := atomic.AddInt64(&seq, 1) return gen(strconv.FormatInt(new, 10)) } - return fa + return fa.fillAttrGen(nil, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SubFactory(name string, sub *Factory) *Factory { - idx := fa.checkIdx(name) - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { +func (fa *Factory) SubFactory(name string, sub *Factory, formatters ...Formatter) *Factory { + genFunc := func(args Args) (any, error) { pipeline := args.pipeline(fa.numField) ret, err := sub.create(args.Context(), nil, pipeline.Next(args)) if err != nil { @@ -219,13 +156,13 @@ func (fa *Factory) SubFactory(name string, sub *Factory) *Factory { } return ret, nil } - return fa + return fa.fillAttrGen(nil, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SubSliceFactory(name string, sub *Factory, getSize func() int) *Factory { +func (fa *Factory) SubSliceFactory(name string, sub *Factory, getSize func() int, formatters ...Formatter) *Factory { idx := fa.checkIdx(name) tp := fa.rt.Field(idx).Type - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { + genFunc := func(args Args) (any, error) { size := getSize() pipeline := args.pipeline(fa.numField) sv := reflect.MakeSlice(tp, size, size) @@ -238,12 +175,12 @@ func (fa *Factory) SubSliceFactory(name string, sub *Factory, getSize func() int } return sv.Interface(), nil } - return fa + return fa.fillAttrGen(&idx, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SubRecursiveFactory(name string, sub *Factory, getLimit func() int) *Factory { +func (fa *Factory) SubRecursiveFactory(name string, sub *Factory, getLimit func() int, formatters ...Formatter) *Factory { idx := fa.checkIdx(name) - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { + genFunc := func(args Args) (any, error) { pl := args.pipeline(fa.numField) if !pl.stacks.Has(idx) { pl.stacks.Set(idx, getLimit()) @@ -257,13 +194,13 @@ func (fa *Factory) SubRecursiveFactory(name string, sub *Factory, getLimit func( } return nil, nil } - return fa + return fa.fillAttrGen(&idx, name, fa.wrapWithFormatter(genFunc, formatters...)) } -func (fa *Factory) SubRecursiveSliceFactory(name string, sub *Factory, getSize, getLimit func() int) *Factory { +func (fa *Factory) SubRecursiveSliceFactory(name string, sub *Factory, getSize, getLimit func() int, formatters ...Formatter) *Factory { idx := fa.checkIdx(name) tp := fa.rt.Field(idx).Type - fa.attrGens[idx].genFunc = func(args Args) (interface{}, error) { + genFunc := func(args Args) (any, error) { pl := args.pipeline(fa.numField) if !pl.stacks.Has(idx) { pl.stacks.Set(idx, getLimit()) @@ -282,7 +219,7 @@ func (fa *Factory) SubRecursiveSliceFactory(name string, sub *Factory, getSize, } return nil, nil } - return fa + return fa.fillAttrGen(&idx, name, fa.wrapWithFormatter(genFunc, formatters...)) } // OnCreate registers a callback on object creation. @@ -292,39 +229,30 @@ func (fa *Factory) OnCreate(cb func(Args) error) *Factory { return fa } -func (fa *Factory) checkIdx(name string) int { - idx, ok := fa.nameIndexMap[name] - if !ok { - panic("No such attribute name: " + name) - } - return idx -} - -func (fa *Factory) Create() (interface{}, error) { +func (fa *Factory) Create() (any, error) { return fa.CreateWithOption(nil) } -func (fa *Factory) CreateWithOption(opt map[string]interface{}) (interface{}, error) { +func (fa *Factory) CreateWithOption(opt map[string]any) (any, error) { return fa.create(context.Background(), opt, nil) } -func (fa *Factory) CreateWithContext(ctx context.Context) (interface{}, error) { +func (fa *Factory) CreateWithContext(ctx context.Context) (any, error) { return fa.create(ctx, nil, nil) } -func (fa *Factory) CreateWithContextAndOption(ctx context.Context, opt map[string]interface{}) (interface{}, error) { +func (fa *Factory) CreateWithContextAndOption(ctx context.Context, opt map[string]any) (any, error) { return fa.create(ctx, opt, nil) } -func (fa *Factory) MustCreate() interface{} { - return fa.MustCreateWithOption(nil) +func (fa *Factory) CreateX() any { + return fa.CreateXWithOption(nil) } - -func (fa *Factory) MustCreateWithOption(opt map[string]interface{}) interface{} { - return fa.MustCreateWithContextAndOption(context.Background(), opt) +func (fa *Factory) CreateXWithOption(opt map[string]any) any { + return fa.CreateXWithContextAndOption(context.Background(), opt) } -func (fa *Factory) MustCreateWithContextAndOption(ctx context.Context, opt map[string]interface{}) interface{} { +func (fa *Factory) CreateXWithContextAndOption(ctx context.Context, opt map[string]any) any { inst, err := fa.CreateWithContextAndOption(ctx, opt) if err != nil { panic(err) @@ -332,12 +260,30 @@ func (fa *Factory) MustCreateWithContextAndOption(ctx context.Context, opt map[s return inst } +// MustCreate will be deprecated in future releases. +// Please use CreateX instead. +func (fa *Factory) MustCreate() any { + return fa.CreateX() +} + +// MustCreateWithOption will be deprecated in future releases. +// Please use CreateXWithOption instead. +func (fa *Factory) MustCreateWithOption(opt map[string]any) any { + return fa.CreateXWithContextAndOption(context.Background(), opt) +} + +// MustCreateWithContextAndOption will be deprecated in future releases. +// Please use CreateXWithContextAndOption instead. +func (fa *Factory) MustCreateWithContextAndOption(ctx context.Context, opt map[string]any) any { + return fa.CreateXWithContextAndOption(ctx, opt) +} + /* Bind values of a new objects to a pointer to struct. ptr: a pointer to struct */ -func (fa *Factory) Construct(ptr interface{}) error { +func (fa *Factory) Construct(ptr any) error { return fa.ConstructWithOption(ptr, nil) } @@ -347,7 +293,7 @@ Bind values of a new objects to a pointer to struct with option. ptr: a pointer to struct opt: attibute values */ -func (fa *Factory) ConstructWithOption(ptr interface{}, opt map[string]interface{}) error { +func (fa *Factory) ConstructWithOption(ptr any, opt map[string]any) error { return fa.ConstructWithContextAndOption(context.Background(), ptr, opt) } @@ -358,10 +304,10 @@ ctx: context object ptr: a pointer to struct opt: attibute values */ -func (fa *Factory) ConstructWithContextAndOption(ctx context.Context, ptr interface{}, opt map[string]interface{}) error { +func (fa *Factory) ConstructWithContextAndOption(ctx context.Context, ptr any, opt map[string]any) error { pt := reflect.TypeOf(ptr) if pt.Kind() != reflect.Ptr { - return errors.New("ptr should be pointer type.") + return errors.New("ptr should be pointer type") } pt = pt.Elem() if pt.Name() != fa.modelName() { @@ -373,7 +319,82 @@ func (fa *Factory) ConstructWithContextAndOption(ctx context.Context, ptr interf return err } -func (fa *Factory) build(ctx context.Context, inst *reflect.Value, tp reflect.Type, opt map[string]interface{}, pl *pipeline) (interface{}, error) { +func (fa *Factory) init() { + rt := reflect.TypeOf(fa.model) + rv := reflect.ValueOf(fa.model) + + fa.isPtr = rt.Kind() == reflect.Ptr + + if fa.isPtr { + rt = rt.Elem() + rv = rv.Elem() + } + + fa.numField = rv.NumField() + fa.orderingAttrGens = make([]*attrGenerator, fa.numField) + + for i := 0; i < fa.numField; i++ { + tf := rt.Field(i) + vf := rv.Field(i) + ag := &attrGenerator{} + + if !vf.CanSet() || (tf.Type.Kind() == reflect.Ptr && vf.IsNil()) { + ag.isNil = true + } else { + ag.value = vf.Interface() + } + + attrName := getAttrName(tf, TagName) + ag.key = attrName + ag.isFilled = false + fa.nameIndexMap[attrName] = i + fa.attrGens = append(fa.attrGens, ag) + } + + fa.rt = rt + fa.rv = &rv +} + +func (fa *Factory) modelName() string { + return fa.rt.Name() +} + +func (fa *Factory) fillAttrGen(idx *int, name string, gen func(Args) (any, error)) *Factory { + if idx == nil { + i := fa.checkIdx(name) + idx = &i + } + fa.attrGens[*idx].genFunc = gen + fa.attrGens[*idx].isFilled = true + orderingIdx := fa.getOrderingIdx() + fa.orderingAttrGens[orderingIdx] = fa.attrGens[*idx] + return fa +} + +func (fa *Factory) checkIdx(name string) int { + idx, ok := fa.nameIndexMap[name] + if !ok { + panic("No such attribute name: " + name) + } + return idx +} +func (fa *Factory) getOrderingIdx() int { + idx := fa.curIdx + if fa.curIdx < fa.numField-1 { + fa.curIdx += 1 + } + return idx +} + +func (fa *Factory) fillMissingAttr(_ context.Context) { + for _, attr := range fa.attrGens { + if !attr.isFilled { + fa.fillAttrGen(nil, attr.key, attr.genFunc) + } + } +} + +func (fa *Factory) build(ctx context.Context, inst *reflect.Value, tp reflect.Type, opt map[string]any, pl *pipeline) (any, error) { args := &argsStruct{} args.pl = pl args.ctx = ctx @@ -384,22 +405,23 @@ func (fa *Factory) build(ctx context.Context, inst *reflect.Value, tp reflect.Ty args.rv = inst } - for i := 0; i < fa.numField; i++ { - if v, ok := opt[fa.attrGens[i].key]; ok { - inst.Field(i).Set(reflect.ValueOf(v)) + fa.fillMissingAttr(ctx) + + for _, attr := range fa.orderingAttrGens { + if v, ok := opt[attr.key]; ok { + inst.FieldByName(attr.key).Set(reflect.ValueOf(v)) } else { - ag := fa.attrGens[i] - if ag.genFunc == nil { - if !ag.isNil { - inst.Field(i).Set(reflect.ValueOf(ag.value)) + if attr.genFunc == nil { + if !attr.isNil { + inst.FieldByName(attr.key).Set(reflect.ValueOf(attr.value)) } } else { - v, err := ag.genFunc(args) + v, err := attr.genFunc(args) if err != nil { return nil, err } if v != nil { - inst.Field(i).Set(reflect.ValueOf(v)) + inst.FieldByName(attr.key).Set(reflect.ValueOf(v)) } } } @@ -421,7 +443,7 @@ func (fa *Factory) build(ctx context.Context, inst *reflect.Value, tp reflect.Ty return inst.Interface(), nil } -func (fa *Factory) create(ctx context.Context, opt map[string]interface{}, pl *pipeline) (interface{}, error) { +func (fa *Factory) create(ctx context.Context, opt map[string]any, pl *pipeline) (any, error) { inst := reflect.New(fa.rt).Elem() return fa.build(ctx, &inst, fa.rt, opt, pl) } diff --git a/factory/factory_mocks.go b/factory/factory_mocks.go new file mode 100644 index 0000000..738f98f --- /dev/null +++ b/factory/factory_mocks.go @@ -0,0 +1,91 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: factory.go + +// Package factory is a generated GoMock package. +package factory + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockArgs is a mock of Args interface. +type MockArgs struct { + ctrl *gomock.Controller + recorder *MockArgsMockRecorder +} + +// MockArgsMockRecorder is the mock recorder for MockArgs. +type MockArgsMockRecorder struct { + mock *MockArgs +} + +// NewMockArgs creates a new mock instance. +func NewMockArgs(ctrl *gomock.Controller) *MockArgs { + mock := &MockArgs{ctrl: ctrl} + mock.recorder = &MockArgsMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockArgs) EXPECT() *MockArgsMockRecorder { + return m.recorder +} + +// Context mocks base method. +func (m *MockArgs) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context. +func (mr *MockArgsMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockArgs)(nil).Context)) +} + +// Instance mocks base method. +func (m *MockArgs) Instance() any { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Instance") + ret0, _ := ret[0].(any) + return ret0 +} + +// Instance indicates an expected call of Instance. +func (mr *MockArgsMockRecorder) Instance() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Instance", reflect.TypeOf((*MockArgs)(nil).Instance)) +} + +// Parent mocks base method. +func (m *MockArgs) Parent() Args { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Parent") + ret0, _ := ret[0].(Args) + return ret0 +} + +// Parent indicates an expected call of Parent. +func (mr *MockArgsMockRecorder) Parent() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parent", reflect.TypeOf((*MockArgs)(nil).Parent)) +} + +// pipeline mocks base method. +func (m *MockArgs) pipeline(arg0 int) *pipeline { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "pipeline", arg0) + ret0, _ := ret[0].(*pipeline) + return ret0 +} + +// pipeline indicates an expected call of pipeline. +func (mr *MockArgsMockRecorder) pipeline(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "pipeline", reflect.TypeOf((*MockArgs)(nil).pipeline), arg0) +} diff --git a/factory/factory_test.go b/factory/factory_test.go index 2fb2db1..9b7db81 100644 --- a/factory/factory_test.go +++ b/factory/factory_test.go @@ -2,24 +2,29 @@ package factory import ( "context" + "errors" + "fmt" + "os" "sync" "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" ) func TestFactory(t *testing.T) { type User struct { - ID int - Name string - Location string - unexported string + ID int + Name string + Location string } userFactory := NewFactory(&User{Location: "Tokyo"}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - Attr("Name", func(args Args) (interface{}, error) { - return "bluele", nil + Attr("Name", func(args Args) (any, error) { + return "hyuti", nil }) iuser, err := userFactory.Create() @@ -35,8 +40,8 @@ func TestFactory(t *testing.T) { t.Error("user.ID should be 1.") return } - if user.Name != "bluele" { - t.Error(`user.Name should be "bluele".`) + if user.Name != "hyuti" { + t.Error(`user.Name should be "hyuti".`) return } if user.Location != "Tokyo" { @@ -51,10 +56,10 @@ func TestMapAttrFactory(t *testing.T) { Ext map[string]string } var userFactory = NewFactory(&User{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - Attr("Ext", func(args Args) (interface{}, error) { + Attr("Ext", func(args Args) (any, error) { return map[string]string{"test": "ok"}, nil }) user := &User{} @@ -83,16 +88,16 @@ func TestSubFactory(t *testing.T) { } groupFactory := NewFactory(&Group{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }) userFactory := NewFactory(&User{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - Attr("Name", func(args Args) (interface{}, error) { - return "bluele", nil + Attr("Name", func(args Args) (any, error) { + return "hyuti", nil }). SubFactory("Group", groupFactory) @@ -128,16 +133,16 @@ func TestSubSliceFactory(t *testing.T) { } groupFactory := NewFactory(&Group{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }) userFactory := NewFactory(&User{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - Attr("Name", func(args Args) (interface{}, error) { - return "bluele", nil + Attr("Name", func(args Args) (any, error) { + return "hyuti", nil }). SubSliceFactory("Groups", groupFactory, func() int { return 3 }) @@ -178,11 +183,11 @@ func TestSubRecursiveFactory(t *testing.T) { var userFactory = NewFactory(&User{}) userFactory. - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - Attr("Name", func(args Args) (interface{}, error) { - return "bluele", nil + Attr("Name", func(args Args) (any, error) { + return "hyuti", nil }). SubRecursiveFactory("Friend", userFactory, func() int { return 2 }) @@ -215,11 +220,11 @@ func TestFactoryConstruction(t *testing.T) { } var userFactory = NewFactory(&User{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - Attr("Name", func(args Args) (interface{}, error) { - return "bluele", nil + Attr("Name", func(args Args) (any, error) { + return "hyuti", nil }) var user *User @@ -237,7 +242,7 @@ func TestFactoryConstruction(t *testing.T) { } user = &User{} - if err := userFactory.ConstructWithOption(user, map[string]interface{}{"Name": "jun"}); err != nil { + if err := userFactory.ConstructWithOption(user, map[string]any{"Name": "jun"}); err != nil { t.Error(err) return } @@ -257,7 +262,7 @@ func TestFactoryWhenCallArgsParent(t *testing.T) { var userFactory = NewFactory(&User{}) userFactory. - Attr("Name", func(args Args) (interface{}, error) { + Attr("Name", func(args Args) (any, error) { if parent := args.Parent(); parent != nil { if pUser, ok := parent.Instance().(*User); ok { return pUser.GroupUUID, nil @@ -286,9 +291,9 @@ func TestFactoryWithOptions(t *testing.T) { ) var userFactory = NewFactory(&User{}) - user := userFactory.MustCreateWithOption(map[string]interface{}{ + user := userFactory.MustCreateWithOption(map[string]any{ "ID": 1, - "Name": "bluele", + "Name": "hyuti", "Group1.Name": "programmer", "Group2.Name": "web", }).(*User) @@ -297,8 +302,8 @@ func TestFactoryWithOptions(t *testing.T) { t.Errorf("user.ID should be 1, not %v", user.ID) } - if user.Name != "bluele" { - t.Errorf("user.Name should be bluele, not %v", user.Name) + if user.Name != "hyuti" { + t.Errorf("user.Name should be hyuti, not %v", user.Name) } if user.Group1.Name != "programmer" { @@ -322,17 +327,17 @@ func TestFactoryMuctCreateWithContextAndOptions(t *testing.T) { var userFactory = NewFactory(&User{}) t.Run("with valid options", func(t *testing.T) { - user := userFactory.MustCreateWithContextAndOption(context.Background(), map[string]interface{}{ + user := userFactory.MustCreateWithContextAndOption(context.Background(), map[string]any{ "ID": 1, - "Name": "bluele", + "Name": "hyuti", }).(*User) if user.ID != 1 { t.Errorf("user.ID should be 1, not %v", user.ID) } - if user.Name != "bluele" { - t.Errorf("user.Name should be bluele, not %v", user.Name) + if user.Name != "hyuti" { + t.Errorf("user.Name should be hyuti, not %v", user.Name) } }) @@ -343,29 +348,29 @@ func TestFactoryMuctCreateWithContextAndOptions(t *testing.T) { } }() - userFactory.MustCreateWithContextAndOption(context.Background(), map[string]interface{}{ + userFactory.MustCreateWithContextAndOption(context.Background(), map[string]any{ "ID": 1, "Name": 3, }) }) t.Run("with filled context", func(t *testing.T) { - userFactory := NewFactory(&User{}).Attr("Name", func(args Args) (interface{}, error) { + userFactory := NewFactory(&User{}).Attr("Name", func(args Args) (any, error) { return args.Context().Value(nameField), nil }) - ctx := context.WithValue(context.Background(), nameField, "bluele from ctx") - user := userFactory.MustCreateWithContextAndOption(ctx, map[string]interface{}{ + ctx := context.WithValue(context.Background(), nameField, "hyuti from ctx") + user := userFactory.MustCreateWithContextAndOption(ctx, map[string]any{ "ID": 1, }).(*User) - if user.Name != "bluele from ctx" { - t.Errorf("user.Name should be bluele from ctx, not %v", user.Name) + if user.Name != "hyuti from ctx" { + t.Errorf("user.Name should be hyuti from ctx, not %v", user.Name) } }) t.Run("with nil context", func(t *testing.T) { - userFactory := NewFactory(&User{}).Attr("Name", func(args Args) (interface{}, error) { + userFactory := NewFactory(&User{}).Attr("Name", func(args Args) (any, error) { return args.Context().Value(nameField), nil }) @@ -375,7 +380,7 @@ func TestFactoryMuctCreateWithContextAndOptions(t *testing.T) { } }() - userFactory.MustCreateWithContextAndOption(nil, map[string]interface{}{ + userFactory.MustCreateWithContextAndOption(nil, map[string]any{ "ID": 1, }) }) @@ -388,10 +393,10 @@ func TestFactorySeqConcurrency(t *testing.T) { } var userFactory = NewFactory(&User{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }). - SeqString("Name", func(s string) (interface{}, error) { + SeqString("Name", func(s string) (any, error) { return "user-" + s, nil }) @@ -439,7 +444,7 @@ func TestFactorySeqIntStartsAt1(t *testing.T) { } var userFactory = NewFactory(&User{}). - SeqInt("ID", func(n int) (interface{}, error) { + SeqInt("ID", func(n int) (any, error) { return n, nil }) @@ -459,7 +464,7 @@ func TestFactorySeqInt64StartsAt1(t *testing.T) { } var userFactory = NewFactory(&User{}). - SeqInt64("ID", func(n int64) (interface{}, error) { + SeqInt64("ID", func(n int64) (any, error) { return n, nil }) @@ -479,7 +484,7 @@ func TestFactorySeqStringStartsAt1(t *testing.T) { } var userFactory = NewFactory(&User{}). - SeqString("Name", func(s string) (interface{}, error) { + SeqString("Name", func(s string) (any, error) { return s, nil }) @@ -492,3 +497,259 @@ func TestFactorySeqStringStartsAt1(t *testing.T) { t.Errorf("the starting number for SeqString was %s, not 1", name) } } + +// this func tests create, not Create function which is exported one. +func TestCreate(t *testing.T) { + t.Parallel() + tests := []struct { + name string + setUp func(*testing.T, context.Context) + expect func(*testing.T, context.Context) + }{ + { + name: "client not defines any attr gen", + setUp: func(t *testing.T, ctx context.Context) {}, + expect: func(t *testing.T, ctx context.Context) { + type Foo struct { + Bar int + } + fooFactory := NewFactory(&Foo{}) + require.Len(t, fooFactory.orderingAttrGens, fooFactory.numField) + fooAny, err := fooFactory.create(ctx, nil, nil) + foo, _ := fooAny.(*Foo) + for _, attr := range fooFactory.orderingAttrGens { + require.True(t, attr.isFilled) + } + require.Nil(t, err) + require.Empty(t, foo.Bar) + }, + }, + { + name: "client defines ordering attr gen", + setUp: func(t *testing.T, ctx context.Context) {}, + expect: func(t *testing.T, ctx context.Context) { + type Foo struct { + FooBar string + Bar int + } + fooFactory := NewFactory(&Foo{}). + Attr("Bar", func(a Args) (any, error) { + return 0, nil + }). + Attr("FooBar", func(a Args) (any, error) { + return "foobar", nil + }) + _, err := fooFactory.create(ctx, nil, nil) + require.Nil(t, err) + require.Equal(t, fooFactory.orderingAttrGens[0].key, "Bar") + require.Equal(t, fooFactory.orderingAttrGens[1].key, "FooBar") + }, + }, + { + name: "client defines partial ordering attr gen", + setUp: func(t *testing.T, ctx context.Context) {}, + expect: func(t *testing.T, ctx context.Context) { + type Foo struct { + FooBar string + Bar int + BarFoo int + } + fooFactory := NewFactory(&Foo{}). + Attr("Bar", func(a Args) (any, error) { + return 0, nil + }). + Attr("FooBar", func(a Args) (any, error) { + return "foobar", nil + }) + _, err := fooFactory.create(ctx, nil, nil) + require.Nil(t, err) + require.Equal(t, fooFactory.orderingAttrGens[0].key, "Bar") + require.Equal(t, fooFactory.orderingAttrGens[1].key, "FooBar") + require.Equal(t, fooFactory.orderingAttrGens[2].key, "BarFoo") + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + tt.setUp(t, ctx) + tt.expect(t, ctx) + }) + } +} + +func TestFormatter(t *testing.T) { + t.Parallel() + tests := []struct { + name string + setUp func(*testing.T, context.Context) + expect func(*testing.T, context.Context) + }{ + { + name: "subfactory with formatters", + setUp: func(t *testing.T, ctx context.Context) {}, + expect: func(t *testing.T, ctx context.Context) { + type Bar struct { + ID int + } + type Foo struct { + BarID int + } + barFactory := NewFactory(&Bar{ID: 1}) + fooFactory := NewFactory(&Foo{}). + SubFactory("BarID", barFactory, func(i any) (any, error) { + inst, ok := i.(*Bar) + if !ok { + return nil, fmt.Errorf("unexpected type %t", i) + } + return inst.ID, nil + }) + fooAny, err := fooFactory.Create() + foo, ok := fooAny.(*Foo) + require.True(t, ok) + require.Nil(t, err) + require.Equal(t, foo.BarID, 1) + }, + }, + { + name: "non-subfactory with formatters", + setUp: func(t *testing.T, ctx context.Context) {}, + expect: func(t *testing.T, ctx context.Context) { + type Bar struct { + Foo string + } + barFactory := NewFactory(&Bar{}). + Attr("Foo", func(a Args) (any, error) { + return "foo", nil + }, func(i any) (any, error) { + inst, ok := i.(string) + if !ok { + return nil, fmt.Errorf("unexpected type %t", i) + } + return fmt.Sprintf("%sbar", inst), nil + }) + barAny, err := barFactory.Create() + bar, ok := barAny.(*Bar) + require.True(t, ok) + require.Nil(t, err) + require.Equal(t, bar.Foo, "foobar") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + tt.setUp(t, ctx) + tt.expect(t, ctx) + }) + } +} + +func TestFileWithDir(t *testing.T) { + t.Parallel() + type kitTest struct { + gen func(*os.File) (any, error) + ctrl *gomock.Controller + args *MockArgs + } + tests := [...]struct { + name string + setUp func(*testing.T) *kitTest + expect func(*testing.T, *kitTest) + tearDown func(*testing.T, *kitTest) + }{ + { + name: "create unsuccess", + setUp: func(t *testing.T) *kitTest { + gen := func(f *os.File) (any, error) { + return f, errors.New("foo") + } + ctrl := gomock.NewController(t) + args := NewMockArgs(ctrl) + return &kitTest{ + gen: gen, + ctrl: ctrl, + args: args, + } + }, + expect: func(t *testing.T, kt *kitTest) { + type foobar struct { + foo string + } + f := NewFactory(&foobar{}) + f = f.FileWithDir("foo", "txt", "temp", kt.gen) + _, err := f.orderingAttrGens[0].genFunc(kt.args) + require.NotNil(t, err) + }, + tearDown: func(t *testing.T, kt *kitTest) { + kt.ctrl.Finish() + }, + }, + { + name: "create success", + setUp: func(t *testing.T) *kitTest { + gen := func(f *os.File) (any, error) { + return f, nil + } + ctrl := gomock.NewController(t) + args := NewMockArgs(ctrl) + return &kitTest{ + gen: gen, + ctrl: ctrl, + args: args, + } + }, + expect: func(t *testing.T, kt *kitTest) { + type foobar struct { + foo string + } + f := NewFactory(&foobar{}) + f = f.FileWithDir("foo", "txt", "temp", kt.gen) + fn, err := f.orderingAttrGens[0].genFunc(kt.args) + require.Nil(t, err) + require.Contains(t, fn, ".txt") + }, + tearDown: func(t *testing.T, kt *kitTest) { + kt.ctrl.Finish() + }, + }, + { + name: "clean", + setUp: func(t *testing.T) *kitTest { + gen := func(f *os.File) (any, error) { + return f, nil + } + ctrl := gomock.NewController(t) + args := NewMockArgs(ctrl) + return &kitTest{ + gen: gen, + ctrl: ctrl, + args: args, + } + }, + expect: func(t *testing.T, kt *kitTest) { + type foobar struct { + foo string + } + f := NewFactory(&foobar{}) + f = f.FileWithDir("foo", "txt", "temp", kt.gen) + fnAny, _ := f.orderingAttrGens[0].genFunc(kt.args) + fn, _ := fnAny.(string) + f.Clean() + err := os.Remove(fn) + require.NotNil(t, err) + }, + tearDown: func(t *testing.T, kt *kitTest) { + kt.ctrl.Finish() + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kt := tt.setUp(t) + tt.expect(t, kt) + tt.tearDown(t, kt) + }) + } +} diff --git a/factory/tools.go b/factory/tools.go new file mode 100644 index 0000000..908ca60 --- /dev/null +++ b/factory/tools.go @@ -0,0 +1,100 @@ +package factory + +import ( + "context" + "reflect" + "sync/atomic" +) + +type ( + Stacks []*int64 + pipeline struct { + stacks Stacks + parent Args + } + attrGenerator struct { + genFunc func(Args) (any, error) + key string + value any + isNil bool + isFilled bool + } + argsStruct struct { + ctx context.Context + rv *reflect.Value + pl *pipeline + } +) + +func newPipeline(size int) *pipeline { + return &pipeline{stacks: make(Stacks, size)} +} + +// Instance returns a object to which the generator declared just before is applied +func (args *argsStruct) Instance() any { + return args.rv.Interface() +} + +// Parent returns a parent argument if current factory is a subfactory of parent +func (args *argsStruct) Parent() Args { + if args.pl == nil { + return nil + } + return args.pl.parent +} + +func (args *argsStruct) Context() context.Context { + return args.ctx +} + +func (args *argsStruct) UpdateContext(ctx context.Context) { + args.ctx = ctx +} + +func (st *Stacks) Size(idx int) int64 { + return *(*st)[idx] +} + +// Set method is not goroutine safe. +func (st *Stacks) Set(idx, val int) { + var ini int64 = 0 + (*st)[idx] = &ini + atomic.StoreInt64((*st)[idx], int64(val)) +} + +func (st *Stacks) Push(idx, delta int) { + atomic.AddInt64((*st)[idx], int64(delta)) +} + +func (st *Stacks) Pop(idx, delta int) { + atomic.AddInt64((*st)[idx], -int64(delta)) +} + +func (st *Stacks) Next(idx int) bool { + st.Pop(idx, 1) + return *(*st)[idx] >= 0 +} + +func (st *Stacks) Has(idx int) bool { + return (*st)[idx] != nil +} + +func (pl *pipeline) Next(args Args) *pipeline { + npl := &pipeline{} + npl.parent = args + npl.stacks = make(Stacks, len(pl.stacks)) + for i, sptr := range pl.stacks { + if sptr != nil { + stack := *sptr + npl.stacks[i] = &stack + } + } + return npl +} + +func (args *argsStruct) pipeline(num int) *pipeline { + if args.pl == nil { + return newPipeline(num) + } + return args.pl +} diff --git a/factory/utils.go b/factory/utils.go index 9d14269..497a61d 100644 --- a/factory/utils.go +++ b/factory/utils.go @@ -13,7 +13,7 @@ func getAttrName(sf reflect.StructField, tagName string) string { return sf.Name } -func setValueWithAttrPath(inst *reflect.Value, tp reflect.Type, attr string, v interface{}) bool { +func setValueWithAttrPath(inst *reflect.Value, tp reflect.Type, attr string, v any) bool { attrs := strings.Split(attr, ".") if len(attrs) <= 1 { return false diff --git a/go.mod b/go.mod index 0e7c084..cdfc25b 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,24 @@ -module github.com/bluele/factory-go +module github.com/hyuti/factory-go -go 1.15 +go 1.19 + +require ( + github.com/Pallinder/go-randomdata v1.2.0 + github.com/go-pg/pg v8.0.7+incompatible + github.com/golang/mock v1.6.0 + github.com/jinzhu/gorm v1.9.16 + github.com/stretchr/testify v1.8.2 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/mattn/go-sqlite3 v1.14.0 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect + github.com/onsi/gomega v1.27.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect + golang.org/x/text v0.8.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + mellium.im/sasl v0.3.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6b46649 --- /dev/null +++ b/go.sum @@ -0,0 +1,135 @@ +github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-pg/pg v8.0.7+incompatible h1:ty/sXL1OZLo+47KK9N8llRcmbA9tZasqbQ/OO4ld53g= +github.com/go-pg/pg v8.0.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= +mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= diff --git a/go.work b/go.work new file mode 100644 index 0000000..0d1f062 --- /dev/null +++ b/go.work @@ -0,0 +1,4 @@ +go 1.19 + +use ./examples/ent-integration +use ./ diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..1a4f204 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,17 @@ +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=