├── LICENSE ├── README.md ├── cmd ├── client │ └── main.go └── server │ └── main.go ├── ent ├── category.go ├── category │ ├── category.go │ └── where.go ├── category_create.go ├── category_delete.go ├── category_query.go ├── category_update.go ├── client.go ├── config.go ├── context.go ├── ent.go ├── enttest │ └── enttest.go ├── generate.go ├── hook │ └── hook.go ├── migrate │ ├── migrate.go │ └── schema.go ├── mutation.go ├── predicate │ └── predicate.go ├── proto │ └── entpb │ │ ├── entpb.pb.go │ │ ├── entpb.proto │ │ ├── entpb_grpc.pb.go │ │ ├── entpb_user_service.go │ │ └── generate.go ├── runtime.go ├── runtime │ └── runtime.go ├── schema │ ├── category.go │ └── user.go ├── tx.go ├── user.go ├── user │ ├── user.go │ └── where.go ├── user_create.go ├── user_delete.go ├── user_query.go └── user_update.go ├── go.mod ├── go.sum ├── pb_test.go └── service_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ent-grpc-example 2 | an example repo showing entproto's code-generation capabilities 3 | -------------------------------------------------------------------------------- /cmd/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "time" 9 | 10 | "github.com/rotemtam/ent-grpc-example/ent/proto/entpb" 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/credentials/insecure" 13 | "google.golang.org/grpc/status" 14 | "google.golang.org/protobuf/types/known/wrapperspb" 15 | ) 16 | 17 | func main() { 18 | rand.Seed(time.Now().UnixNano()) 19 | 20 | // Open a connection to the server. 21 | conn, err := grpc.Dial(":5000", grpc.WithTransportCredentials(insecure.NewCredentials())) 22 | if err != nil { 23 | log.Fatalf("failed connecting to server: %s", err) 24 | } 25 | defer conn.Close() 26 | 27 | // Create a User service Client on the connection. 28 | client := entpb.NewUserServiceClient(conn) 29 | 30 | // Ask the server to create a random User. 31 | ctx := context.Background() 32 | user := randomUser() 33 | created, err := client.Create(ctx, &entpb.CreateUserRequest{ 34 | User: user, 35 | }) 36 | if err != nil { 37 | se, _ := status.FromError(err) 38 | log.Fatalf("failed creating user: status=%s message=%s", se.Code(), se.Message()) 39 | } 40 | log.Printf("user created with id: %d", created.Id) 41 | 42 | // On a separate RPC invocation, retrieve the user we saved previously. 43 | get, err := client.Get(ctx, &entpb.GetUserRequest{ 44 | Id: created.Id, 45 | }) 46 | if err != nil { 47 | se, _ := status.FromError(err) 48 | log.Fatalf("failed retrieving user: status=%s message=%s", se.Code(), se.Message()) 49 | } 50 | log.Printf("retrieved user with id=%d: %v", get.Id, get) 51 | } 52 | 53 | func randomUser() *entpb.User { 54 | return &entpb.User{ 55 | Name: fmt.Sprintf("user_%d", rand.Int()), 56 | EmailAddress: fmt.Sprintf("user_%d@example.com", rand.Int()), 57 | Alias: wrapperspb.String(fmt.Sprintf("John Doe")), 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | 8 | _ "github.com/mattn/go-sqlite3" 9 | "github.com/rotemtam/ent-grpc-example/ent" 10 | "github.com/rotemtam/ent-grpc-example/ent/proto/entpb" 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | func main() { 15 | // Initialize an ent client. 16 | client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") 17 | if err != nil { 18 | log.Fatalf("failed opening connection to sqlite: %v", err) 19 | } 20 | defer client.Close() 21 | 22 | // Run the migration tool (creating tables, etc). 23 | if err := client.Schema.Create(context.Background()); err != nil { 24 | log.Fatalf("failed creating schema resources: %v", err) 25 | } 26 | 27 | // Initialize the generated User service. 28 | svc := entpb.NewUserService(client) 29 | 30 | // Create a new gRPC server (you can wire multiple services to a single server). 31 | server := grpc.NewServer() 32 | 33 | // Register the User service with the server. 34 | entpb.RegisterUserServiceServer(server, svc) 35 | 36 | // Open port 5000 for listening to traffic. 37 | lis, err := net.Listen("tcp", ":5000") 38 | if err != nil { 39 | log.Fatalf("failed listening: %s", err) 40 | } 41 | 42 | // Listen for traffic indefinitely. 43 | if err := server.Serve(lis); err != nil { 44 | log.Fatalf("server ended: %s", err) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ent/category.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "entgo.io/ent/dialect/sql" 10 | "github.com/rotemtam/ent-grpc-example/ent/category" 11 | "github.com/rotemtam/ent-grpc-example/ent/user" 12 | ) 13 | 14 | // Category is the model entity for the Category schema. 15 | type Category struct { 16 | config `json:"-"` 17 | // ID of the ent. 18 | ID int `json:"id,omitempty"` 19 | // Name holds the value of the "name" field. 20 | Name string `json:"name,omitempty"` 21 | // Edges holds the relations/edges for other nodes in the graph. 22 | // The values are being populated by the CategoryQuery when eager-loading is set. 23 | Edges CategoryEdges `json:"edges"` 24 | category_admin *int 25 | } 26 | 27 | // CategoryEdges holds the relations/edges for other nodes in the graph. 28 | type CategoryEdges struct { 29 | // Admin holds the value of the admin edge. 30 | Admin *User `json:"admin,omitempty"` 31 | // loadedTypes holds the information for reporting if a 32 | // type was loaded (or requested) in eager-loading or not. 33 | loadedTypes [1]bool 34 | } 35 | 36 | // AdminOrErr returns the Admin value or an error if the edge 37 | // was not loaded in eager-loading, or loaded but was not found. 38 | func (e CategoryEdges) AdminOrErr() (*User, error) { 39 | if e.loadedTypes[0] { 40 | if e.Admin == nil { 41 | // Edge was loaded but was not found. 42 | return nil, &NotFoundError{label: user.Label} 43 | } 44 | return e.Admin, nil 45 | } 46 | return nil, &NotLoadedError{edge: "admin"} 47 | } 48 | 49 | // scanValues returns the types for scanning values from sql.Rows. 50 | func (*Category) scanValues(columns []string) ([]any, error) { 51 | values := make([]any, len(columns)) 52 | for i := range columns { 53 | switch columns[i] { 54 | case category.FieldID: 55 | values[i] = new(sql.NullInt64) 56 | case category.FieldName: 57 | values[i] = new(sql.NullString) 58 | case category.ForeignKeys[0]: // category_admin 59 | values[i] = new(sql.NullInt64) 60 | default: 61 | return nil, fmt.Errorf("unexpected column %q for type Category", columns[i]) 62 | } 63 | } 64 | return values, nil 65 | } 66 | 67 | // assignValues assigns the values that were returned from sql.Rows (after scanning) 68 | // to the Category fields. 69 | func (c *Category) assignValues(columns []string, values []any) error { 70 | if m, n := len(values), len(columns); m < n { 71 | return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) 72 | } 73 | for i := range columns { 74 | switch columns[i] { 75 | case category.FieldID: 76 | value, ok := values[i].(*sql.NullInt64) 77 | if !ok { 78 | return fmt.Errorf("unexpected type %T for field id", value) 79 | } 80 | c.ID = int(value.Int64) 81 | case category.FieldName: 82 | if value, ok := values[i].(*sql.NullString); !ok { 83 | return fmt.Errorf("unexpected type %T for field name", values[i]) 84 | } else if value.Valid { 85 | c.Name = value.String 86 | } 87 | case category.ForeignKeys[0]: 88 | if value, ok := values[i].(*sql.NullInt64); !ok { 89 | return fmt.Errorf("unexpected type %T for edge-field category_admin", value) 90 | } else if value.Valid { 91 | c.category_admin = new(int) 92 | *c.category_admin = int(value.Int64) 93 | } 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | // QueryAdmin queries the "admin" edge of the Category entity. 100 | func (c *Category) QueryAdmin() *UserQuery { 101 | return NewCategoryClient(c.config).QueryAdmin(c) 102 | } 103 | 104 | // Update returns a builder for updating this Category. 105 | // Note that you need to call Category.Unwrap() before calling this method if this Category 106 | // was returned from a transaction, and the transaction was committed or rolled back. 107 | func (c *Category) Update() *CategoryUpdateOne { 108 | return NewCategoryClient(c.config).UpdateOne(c) 109 | } 110 | 111 | // Unwrap unwraps the Category entity that was returned from a transaction after it was closed, 112 | // so that all future queries will be executed through the driver which created the transaction. 113 | func (c *Category) Unwrap() *Category { 114 | _tx, ok := c.config.driver.(*txDriver) 115 | if !ok { 116 | panic("ent: Category is not a transactional entity") 117 | } 118 | c.config.driver = _tx.drv 119 | return c 120 | } 121 | 122 | // String implements the fmt.Stringer. 123 | func (c *Category) String() string { 124 | var builder strings.Builder 125 | builder.WriteString("Category(") 126 | builder.WriteString(fmt.Sprintf("id=%v, ", c.ID)) 127 | builder.WriteString("name=") 128 | builder.WriteString(c.Name) 129 | builder.WriteByte(')') 130 | return builder.String() 131 | } 132 | 133 | // Categories is a parsable slice of Category. 134 | type Categories []*Category 135 | 136 | func (c Categories) config(cfg config) { 137 | for _i := range c { 138 | c[_i].config = cfg 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /ent/category/category.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package category 4 | 5 | const ( 6 | // Label holds the string label denoting the category type in the database. 7 | Label = "category" 8 | // FieldID holds the string denoting the id field in the database. 9 | FieldID = "id" 10 | // FieldName holds the string denoting the name field in the database. 11 | FieldName = "name" 12 | // EdgeAdmin holds the string denoting the admin edge name in mutations. 13 | EdgeAdmin = "admin" 14 | // Table holds the table name of the category in the database. 15 | Table = "categories" 16 | // AdminTable is the table that holds the admin relation/edge. 17 | AdminTable = "categories" 18 | // AdminInverseTable is the table name for the User entity. 19 | // It exists in this package in order to avoid circular dependency with the "user" package. 20 | AdminInverseTable = "users" 21 | // AdminColumn is the table column denoting the admin relation/edge. 22 | AdminColumn = "category_admin" 23 | ) 24 | 25 | // Columns holds all SQL columns for category fields. 26 | var Columns = []string{ 27 | FieldID, 28 | FieldName, 29 | } 30 | 31 | // ForeignKeys holds the SQL foreign-keys that are owned by the "categories" 32 | // table and are not defined as standalone fields in the schema. 33 | var ForeignKeys = []string{ 34 | "category_admin", 35 | } 36 | 37 | // ValidColumn reports if the column name is valid (part of the table columns). 38 | func ValidColumn(column string) bool { 39 | for i := range Columns { 40 | if column == Columns[i] { 41 | return true 42 | } 43 | } 44 | for i := range ForeignKeys { 45 | if column == ForeignKeys[i] { 46 | return true 47 | } 48 | } 49 | return false 50 | } 51 | -------------------------------------------------------------------------------- /ent/category/where.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package category 4 | 5 | import ( 6 | "entgo.io/ent/dialect/sql" 7 | "entgo.io/ent/dialect/sql/sqlgraph" 8 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 9 | ) 10 | 11 | // ID filters vertices based on their ID field. 12 | func ID(id int) predicate.Category { 13 | return predicate.Category(sql.FieldEQ(FieldID, id)) 14 | } 15 | 16 | // IDEQ applies the EQ predicate on the ID field. 17 | func IDEQ(id int) predicate.Category { 18 | return predicate.Category(sql.FieldEQ(FieldID, id)) 19 | } 20 | 21 | // IDNEQ applies the NEQ predicate on the ID field. 22 | func IDNEQ(id int) predicate.Category { 23 | return predicate.Category(sql.FieldNEQ(FieldID, id)) 24 | } 25 | 26 | // IDIn applies the In predicate on the ID field. 27 | func IDIn(ids ...int) predicate.Category { 28 | return predicate.Category(sql.FieldIn(FieldID, ids...)) 29 | } 30 | 31 | // IDNotIn applies the NotIn predicate on the ID field. 32 | func IDNotIn(ids ...int) predicate.Category { 33 | return predicate.Category(sql.FieldNotIn(FieldID, ids...)) 34 | } 35 | 36 | // IDGT applies the GT predicate on the ID field. 37 | func IDGT(id int) predicate.Category { 38 | return predicate.Category(sql.FieldGT(FieldID, id)) 39 | } 40 | 41 | // IDGTE applies the GTE predicate on the ID field. 42 | func IDGTE(id int) predicate.Category { 43 | return predicate.Category(sql.FieldGTE(FieldID, id)) 44 | } 45 | 46 | // IDLT applies the LT predicate on the ID field. 47 | func IDLT(id int) predicate.Category { 48 | return predicate.Category(sql.FieldLT(FieldID, id)) 49 | } 50 | 51 | // IDLTE applies the LTE predicate on the ID field. 52 | func IDLTE(id int) predicate.Category { 53 | return predicate.Category(sql.FieldLTE(FieldID, id)) 54 | } 55 | 56 | // Name applies equality check predicate on the "name" field. It's identical to NameEQ. 57 | func Name(v string) predicate.Category { 58 | return predicate.Category(sql.FieldEQ(FieldName, v)) 59 | } 60 | 61 | // NameEQ applies the EQ predicate on the "name" field. 62 | func NameEQ(v string) predicate.Category { 63 | return predicate.Category(sql.FieldEQ(FieldName, v)) 64 | } 65 | 66 | // NameNEQ applies the NEQ predicate on the "name" field. 67 | func NameNEQ(v string) predicate.Category { 68 | return predicate.Category(sql.FieldNEQ(FieldName, v)) 69 | } 70 | 71 | // NameIn applies the In predicate on the "name" field. 72 | func NameIn(vs ...string) predicate.Category { 73 | return predicate.Category(sql.FieldIn(FieldName, vs...)) 74 | } 75 | 76 | // NameNotIn applies the NotIn predicate on the "name" field. 77 | func NameNotIn(vs ...string) predicate.Category { 78 | return predicate.Category(sql.FieldNotIn(FieldName, vs...)) 79 | } 80 | 81 | // NameGT applies the GT predicate on the "name" field. 82 | func NameGT(v string) predicate.Category { 83 | return predicate.Category(sql.FieldGT(FieldName, v)) 84 | } 85 | 86 | // NameGTE applies the GTE predicate on the "name" field. 87 | func NameGTE(v string) predicate.Category { 88 | return predicate.Category(sql.FieldGTE(FieldName, v)) 89 | } 90 | 91 | // NameLT applies the LT predicate on the "name" field. 92 | func NameLT(v string) predicate.Category { 93 | return predicate.Category(sql.FieldLT(FieldName, v)) 94 | } 95 | 96 | // NameLTE applies the LTE predicate on the "name" field. 97 | func NameLTE(v string) predicate.Category { 98 | return predicate.Category(sql.FieldLTE(FieldName, v)) 99 | } 100 | 101 | // NameContains applies the Contains predicate on the "name" field. 102 | func NameContains(v string) predicate.Category { 103 | return predicate.Category(sql.FieldContains(FieldName, v)) 104 | } 105 | 106 | // NameHasPrefix applies the HasPrefix predicate on the "name" field. 107 | func NameHasPrefix(v string) predicate.Category { 108 | return predicate.Category(sql.FieldHasPrefix(FieldName, v)) 109 | } 110 | 111 | // NameHasSuffix applies the HasSuffix predicate on the "name" field. 112 | func NameHasSuffix(v string) predicate.Category { 113 | return predicate.Category(sql.FieldHasSuffix(FieldName, v)) 114 | } 115 | 116 | // NameEqualFold applies the EqualFold predicate on the "name" field. 117 | func NameEqualFold(v string) predicate.Category { 118 | return predicate.Category(sql.FieldEqualFold(FieldName, v)) 119 | } 120 | 121 | // NameContainsFold applies the ContainsFold predicate on the "name" field. 122 | func NameContainsFold(v string) predicate.Category { 123 | return predicate.Category(sql.FieldContainsFold(FieldName, v)) 124 | } 125 | 126 | // HasAdmin applies the HasEdge predicate on the "admin" edge. 127 | func HasAdmin() predicate.Category { 128 | return predicate.Category(func(s *sql.Selector) { 129 | step := sqlgraph.NewStep( 130 | sqlgraph.From(Table, FieldID), 131 | sqlgraph.Edge(sqlgraph.M2O, false, AdminTable, AdminColumn), 132 | ) 133 | sqlgraph.HasNeighbors(s, step) 134 | }) 135 | } 136 | 137 | // HasAdminWith applies the HasEdge predicate on the "admin" edge with a given conditions (other predicates). 138 | func HasAdminWith(preds ...predicate.User) predicate.Category { 139 | return predicate.Category(func(s *sql.Selector) { 140 | step := sqlgraph.NewStep( 141 | sqlgraph.From(Table, FieldID), 142 | sqlgraph.To(AdminInverseTable, FieldID), 143 | sqlgraph.Edge(sqlgraph.M2O, false, AdminTable, AdminColumn), 144 | ) 145 | sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { 146 | for _, p := range preds { 147 | p(s) 148 | } 149 | }) 150 | }) 151 | } 152 | 153 | // And groups predicates with the AND operator between them. 154 | func And(predicates ...predicate.Category) predicate.Category { 155 | return predicate.Category(func(s *sql.Selector) { 156 | s1 := s.Clone().SetP(nil) 157 | for _, p := range predicates { 158 | p(s1) 159 | } 160 | s.Where(s1.P()) 161 | }) 162 | } 163 | 164 | // Or groups predicates with the OR operator between them. 165 | func Or(predicates ...predicate.Category) predicate.Category { 166 | return predicate.Category(func(s *sql.Selector) { 167 | s1 := s.Clone().SetP(nil) 168 | for i, p := range predicates { 169 | if i > 0 { 170 | s1.Or() 171 | } 172 | p(s1) 173 | } 174 | s.Where(s1.P()) 175 | }) 176 | } 177 | 178 | // Not applies the not operator on the given predicate. 179 | func Not(p predicate.Category) predicate.Category { 180 | return predicate.Category(func(s *sql.Selector) { 181 | p(s.Not()) 182 | }) 183 | } 184 | -------------------------------------------------------------------------------- /ent/category_create.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | 10 | "entgo.io/ent/dialect/sql/sqlgraph" 11 | "entgo.io/ent/schema/field" 12 | "github.com/rotemtam/ent-grpc-example/ent/category" 13 | "github.com/rotemtam/ent-grpc-example/ent/user" 14 | ) 15 | 16 | // CategoryCreate is the builder for creating a Category entity. 17 | type CategoryCreate struct { 18 | config 19 | mutation *CategoryMutation 20 | hooks []Hook 21 | } 22 | 23 | // SetName sets the "name" field. 24 | func (cc *CategoryCreate) SetName(s string) *CategoryCreate { 25 | cc.mutation.SetName(s) 26 | return cc 27 | } 28 | 29 | // SetAdminID sets the "admin" edge to the User entity by ID. 30 | func (cc *CategoryCreate) SetAdminID(id int) *CategoryCreate { 31 | cc.mutation.SetAdminID(id) 32 | return cc 33 | } 34 | 35 | // SetNillableAdminID sets the "admin" edge to the User entity by ID if the given value is not nil. 36 | func (cc *CategoryCreate) SetNillableAdminID(id *int) *CategoryCreate { 37 | if id != nil { 38 | cc = cc.SetAdminID(*id) 39 | } 40 | return cc 41 | } 42 | 43 | // SetAdmin sets the "admin" edge to the User entity. 44 | func (cc *CategoryCreate) SetAdmin(u *User) *CategoryCreate { 45 | return cc.SetAdminID(u.ID) 46 | } 47 | 48 | // Mutation returns the CategoryMutation object of the builder. 49 | func (cc *CategoryCreate) Mutation() *CategoryMutation { 50 | return cc.mutation 51 | } 52 | 53 | // Save creates the Category in the database. 54 | func (cc *CategoryCreate) Save(ctx context.Context) (*Category, error) { 55 | return withHooks[*Category, CategoryMutation](ctx, cc.sqlSave, cc.mutation, cc.hooks) 56 | } 57 | 58 | // SaveX calls Save and panics if Save returns an error. 59 | func (cc *CategoryCreate) SaveX(ctx context.Context) *Category { 60 | v, err := cc.Save(ctx) 61 | if err != nil { 62 | panic(err) 63 | } 64 | return v 65 | } 66 | 67 | // Exec executes the query. 68 | func (cc *CategoryCreate) Exec(ctx context.Context) error { 69 | _, err := cc.Save(ctx) 70 | return err 71 | } 72 | 73 | // ExecX is like Exec, but panics if an error occurs. 74 | func (cc *CategoryCreate) ExecX(ctx context.Context) { 75 | if err := cc.Exec(ctx); err != nil { 76 | panic(err) 77 | } 78 | } 79 | 80 | // check runs all checks and user-defined validators on the builder. 81 | func (cc *CategoryCreate) check() error { 82 | if _, ok := cc.mutation.Name(); !ok { 83 | return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Category.name"`)} 84 | } 85 | return nil 86 | } 87 | 88 | func (cc *CategoryCreate) sqlSave(ctx context.Context) (*Category, error) { 89 | if err := cc.check(); err != nil { 90 | return nil, err 91 | } 92 | _node, _spec := cc.createSpec() 93 | if err := sqlgraph.CreateNode(ctx, cc.driver, _spec); err != nil { 94 | if sqlgraph.IsConstraintError(err) { 95 | err = &ConstraintError{msg: err.Error(), wrap: err} 96 | } 97 | return nil, err 98 | } 99 | id := _spec.ID.Value.(int64) 100 | _node.ID = int(id) 101 | cc.mutation.id = &_node.ID 102 | cc.mutation.done = true 103 | return _node, nil 104 | } 105 | 106 | func (cc *CategoryCreate) createSpec() (*Category, *sqlgraph.CreateSpec) { 107 | var ( 108 | _node = &Category{config: cc.config} 109 | _spec = &sqlgraph.CreateSpec{ 110 | Table: category.Table, 111 | ID: &sqlgraph.FieldSpec{ 112 | Type: field.TypeInt, 113 | Column: category.FieldID, 114 | }, 115 | } 116 | ) 117 | if value, ok := cc.mutation.Name(); ok { 118 | _spec.SetField(category.FieldName, field.TypeString, value) 119 | _node.Name = value 120 | } 121 | if nodes := cc.mutation.AdminIDs(); len(nodes) > 0 { 122 | edge := &sqlgraph.EdgeSpec{ 123 | Rel: sqlgraph.M2O, 124 | Inverse: false, 125 | Table: category.AdminTable, 126 | Columns: []string{category.AdminColumn}, 127 | Bidi: false, 128 | Target: &sqlgraph.EdgeTarget{ 129 | IDSpec: &sqlgraph.FieldSpec{ 130 | Type: field.TypeInt, 131 | Column: user.FieldID, 132 | }, 133 | }, 134 | } 135 | for _, k := range nodes { 136 | edge.Target.Nodes = append(edge.Target.Nodes, k) 137 | } 138 | _node.category_admin = &nodes[0] 139 | _spec.Edges = append(_spec.Edges, edge) 140 | } 141 | return _node, _spec 142 | } 143 | 144 | // CategoryCreateBulk is the builder for creating many Category entities in bulk. 145 | type CategoryCreateBulk struct { 146 | config 147 | builders []*CategoryCreate 148 | } 149 | 150 | // Save creates the Category entities in the database. 151 | func (ccb *CategoryCreateBulk) Save(ctx context.Context) ([]*Category, error) { 152 | specs := make([]*sqlgraph.CreateSpec, len(ccb.builders)) 153 | nodes := make([]*Category, len(ccb.builders)) 154 | mutators := make([]Mutator, len(ccb.builders)) 155 | for i := range ccb.builders { 156 | func(i int, root context.Context) { 157 | builder := ccb.builders[i] 158 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 159 | mutation, ok := m.(*CategoryMutation) 160 | if !ok { 161 | return nil, fmt.Errorf("unexpected mutation type %T", m) 162 | } 163 | if err := builder.check(); err != nil { 164 | return nil, err 165 | } 166 | builder.mutation = mutation 167 | nodes[i], specs[i] = builder.createSpec() 168 | var err error 169 | if i < len(mutators)-1 { 170 | _, err = mutators[i+1].Mutate(root, ccb.builders[i+1].mutation) 171 | } else { 172 | spec := &sqlgraph.BatchCreateSpec{Nodes: specs} 173 | // Invoke the actual operation on the latest mutation in the chain. 174 | if err = sqlgraph.BatchCreate(ctx, ccb.driver, spec); err != nil { 175 | if sqlgraph.IsConstraintError(err) { 176 | err = &ConstraintError{msg: err.Error(), wrap: err} 177 | } 178 | } 179 | } 180 | if err != nil { 181 | return nil, err 182 | } 183 | mutation.id = &nodes[i].ID 184 | if specs[i].ID.Value != nil { 185 | id := specs[i].ID.Value.(int64) 186 | nodes[i].ID = int(id) 187 | } 188 | mutation.done = true 189 | return nodes[i], nil 190 | }) 191 | for i := len(builder.hooks) - 1; i >= 0; i-- { 192 | mut = builder.hooks[i](mut) 193 | } 194 | mutators[i] = mut 195 | }(i, ctx) 196 | } 197 | if len(mutators) > 0 { 198 | if _, err := mutators[0].Mutate(ctx, ccb.builders[0].mutation); err != nil { 199 | return nil, err 200 | } 201 | } 202 | return nodes, nil 203 | } 204 | 205 | // SaveX is like Save, but panics if an error occurs. 206 | func (ccb *CategoryCreateBulk) SaveX(ctx context.Context) []*Category { 207 | v, err := ccb.Save(ctx) 208 | if err != nil { 209 | panic(err) 210 | } 211 | return v 212 | } 213 | 214 | // Exec executes the query. 215 | func (ccb *CategoryCreateBulk) Exec(ctx context.Context) error { 216 | _, err := ccb.Save(ctx) 217 | return err 218 | } 219 | 220 | // ExecX is like Exec, but panics if an error occurs. 221 | func (ccb *CategoryCreateBulk) ExecX(ctx context.Context) { 222 | if err := ccb.Exec(ctx); err != nil { 223 | panic(err) 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /ent/category_delete.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | 8 | "entgo.io/ent/dialect/sql" 9 | "entgo.io/ent/dialect/sql/sqlgraph" 10 | "entgo.io/ent/schema/field" 11 | "github.com/rotemtam/ent-grpc-example/ent/category" 12 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 13 | ) 14 | 15 | // CategoryDelete is the builder for deleting a Category entity. 16 | type CategoryDelete struct { 17 | config 18 | hooks []Hook 19 | mutation *CategoryMutation 20 | } 21 | 22 | // Where appends a list predicates to the CategoryDelete builder. 23 | func (cd *CategoryDelete) Where(ps ...predicate.Category) *CategoryDelete { 24 | cd.mutation.Where(ps...) 25 | return cd 26 | } 27 | 28 | // Exec executes the deletion query and returns how many vertices were deleted. 29 | func (cd *CategoryDelete) Exec(ctx context.Context) (int, error) { 30 | return withHooks[int, CategoryMutation](ctx, cd.sqlExec, cd.mutation, cd.hooks) 31 | } 32 | 33 | // ExecX is like Exec, but panics if an error occurs. 34 | func (cd *CategoryDelete) ExecX(ctx context.Context) int { 35 | n, err := cd.Exec(ctx) 36 | if err != nil { 37 | panic(err) 38 | } 39 | return n 40 | } 41 | 42 | func (cd *CategoryDelete) sqlExec(ctx context.Context) (int, error) { 43 | _spec := &sqlgraph.DeleteSpec{ 44 | Node: &sqlgraph.NodeSpec{ 45 | Table: category.Table, 46 | ID: &sqlgraph.FieldSpec{ 47 | Type: field.TypeInt, 48 | Column: category.FieldID, 49 | }, 50 | }, 51 | } 52 | if ps := cd.mutation.predicates; len(ps) > 0 { 53 | _spec.Predicate = func(selector *sql.Selector) { 54 | for i := range ps { 55 | ps[i](selector) 56 | } 57 | } 58 | } 59 | affected, err := sqlgraph.DeleteNodes(ctx, cd.driver, _spec) 60 | if err != nil && sqlgraph.IsConstraintError(err) { 61 | err = &ConstraintError{msg: err.Error(), wrap: err} 62 | } 63 | cd.mutation.done = true 64 | return affected, err 65 | } 66 | 67 | // CategoryDeleteOne is the builder for deleting a single Category entity. 68 | type CategoryDeleteOne struct { 69 | cd *CategoryDelete 70 | } 71 | 72 | // Exec executes the deletion query. 73 | func (cdo *CategoryDeleteOne) Exec(ctx context.Context) error { 74 | n, err := cdo.cd.Exec(ctx) 75 | switch { 76 | case err != nil: 77 | return err 78 | case n == 0: 79 | return &NotFoundError{category.Label} 80 | default: 81 | return nil 82 | } 83 | } 84 | 85 | // ExecX is like Exec, but panics if an error occurs. 86 | func (cdo *CategoryDeleteOne) ExecX(ctx context.Context) { 87 | cdo.cd.ExecX(ctx) 88 | } 89 | -------------------------------------------------------------------------------- /ent/category_update.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | 10 | "entgo.io/ent/dialect/sql" 11 | "entgo.io/ent/dialect/sql/sqlgraph" 12 | "entgo.io/ent/schema/field" 13 | "github.com/rotemtam/ent-grpc-example/ent/category" 14 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 15 | "github.com/rotemtam/ent-grpc-example/ent/user" 16 | ) 17 | 18 | // CategoryUpdate is the builder for updating Category entities. 19 | type CategoryUpdate struct { 20 | config 21 | hooks []Hook 22 | mutation *CategoryMutation 23 | } 24 | 25 | // Where appends a list predicates to the CategoryUpdate builder. 26 | func (cu *CategoryUpdate) Where(ps ...predicate.Category) *CategoryUpdate { 27 | cu.mutation.Where(ps...) 28 | return cu 29 | } 30 | 31 | // SetName sets the "name" field. 32 | func (cu *CategoryUpdate) SetName(s string) *CategoryUpdate { 33 | cu.mutation.SetName(s) 34 | return cu 35 | } 36 | 37 | // SetAdminID sets the "admin" edge to the User entity by ID. 38 | func (cu *CategoryUpdate) SetAdminID(id int) *CategoryUpdate { 39 | cu.mutation.SetAdminID(id) 40 | return cu 41 | } 42 | 43 | // SetNillableAdminID sets the "admin" edge to the User entity by ID if the given value is not nil. 44 | func (cu *CategoryUpdate) SetNillableAdminID(id *int) *CategoryUpdate { 45 | if id != nil { 46 | cu = cu.SetAdminID(*id) 47 | } 48 | return cu 49 | } 50 | 51 | // SetAdmin sets the "admin" edge to the User entity. 52 | func (cu *CategoryUpdate) SetAdmin(u *User) *CategoryUpdate { 53 | return cu.SetAdminID(u.ID) 54 | } 55 | 56 | // Mutation returns the CategoryMutation object of the builder. 57 | func (cu *CategoryUpdate) Mutation() *CategoryMutation { 58 | return cu.mutation 59 | } 60 | 61 | // ClearAdmin clears the "admin" edge to the User entity. 62 | func (cu *CategoryUpdate) ClearAdmin() *CategoryUpdate { 63 | cu.mutation.ClearAdmin() 64 | return cu 65 | } 66 | 67 | // Save executes the query and returns the number of nodes affected by the update operation. 68 | func (cu *CategoryUpdate) Save(ctx context.Context) (int, error) { 69 | return withHooks[int, CategoryMutation](ctx, cu.sqlSave, cu.mutation, cu.hooks) 70 | } 71 | 72 | // SaveX is like Save, but panics if an error occurs. 73 | func (cu *CategoryUpdate) SaveX(ctx context.Context) int { 74 | affected, err := cu.Save(ctx) 75 | if err != nil { 76 | panic(err) 77 | } 78 | return affected 79 | } 80 | 81 | // Exec executes the query. 82 | func (cu *CategoryUpdate) Exec(ctx context.Context) error { 83 | _, err := cu.Save(ctx) 84 | return err 85 | } 86 | 87 | // ExecX is like Exec, but panics if an error occurs. 88 | func (cu *CategoryUpdate) ExecX(ctx context.Context) { 89 | if err := cu.Exec(ctx); err != nil { 90 | panic(err) 91 | } 92 | } 93 | 94 | func (cu *CategoryUpdate) sqlSave(ctx context.Context) (n int, err error) { 95 | _spec := &sqlgraph.UpdateSpec{ 96 | Node: &sqlgraph.NodeSpec{ 97 | Table: category.Table, 98 | Columns: category.Columns, 99 | ID: &sqlgraph.FieldSpec{ 100 | Type: field.TypeInt, 101 | Column: category.FieldID, 102 | }, 103 | }, 104 | } 105 | if ps := cu.mutation.predicates; len(ps) > 0 { 106 | _spec.Predicate = func(selector *sql.Selector) { 107 | for i := range ps { 108 | ps[i](selector) 109 | } 110 | } 111 | } 112 | if value, ok := cu.mutation.Name(); ok { 113 | _spec.SetField(category.FieldName, field.TypeString, value) 114 | } 115 | if cu.mutation.AdminCleared() { 116 | edge := &sqlgraph.EdgeSpec{ 117 | Rel: sqlgraph.M2O, 118 | Inverse: false, 119 | Table: category.AdminTable, 120 | Columns: []string{category.AdminColumn}, 121 | Bidi: false, 122 | Target: &sqlgraph.EdgeTarget{ 123 | IDSpec: &sqlgraph.FieldSpec{ 124 | Type: field.TypeInt, 125 | Column: user.FieldID, 126 | }, 127 | }, 128 | } 129 | _spec.Edges.Clear = append(_spec.Edges.Clear, edge) 130 | } 131 | if nodes := cu.mutation.AdminIDs(); len(nodes) > 0 { 132 | edge := &sqlgraph.EdgeSpec{ 133 | Rel: sqlgraph.M2O, 134 | Inverse: false, 135 | Table: category.AdminTable, 136 | Columns: []string{category.AdminColumn}, 137 | Bidi: false, 138 | Target: &sqlgraph.EdgeTarget{ 139 | IDSpec: &sqlgraph.FieldSpec{ 140 | Type: field.TypeInt, 141 | Column: user.FieldID, 142 | }, 143 | }, 144 | } 145 | for _, k := range nodes { 146 | edge.Target.Nodes = append(edge.Target.Nodes, k) 147 | } 148 | _spec.Edges.Add = append(_spec.Edges.Add, edge) 149 | } 150 | if n, err = sqlgraph.UpdateNodes(ctx, cu.driver, _spec); err != nil { 151 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 152 | err = &NotFoundError{category.Label} 153 | } else if sqlgraph.IsConstraintError(err) { 154 | err = &ConstraintError{msg: err.Error(), wrap: err} 155 | } 156 | return 0, err 157 | } 158 | cu.mutation.done = true 159 | return n, nil 160 | } 161 | 162 | // CategoryUpdateOne is the builder for updating a single Category entity. 163 | type CategoryUpdateOne struct { 164 | config 165 | fields []string 166 | hooks []Hook 167 | mutation *CategoryMutation 168 | } 169 | 170 | // SetName sets the "name" field. 171 | func (cuo *CategoryUpdateOne) SetName(s string) *CategoryUpdateOne { 172 | cuo.mutation.SetName(s) 173 | return cuo 174 | } 175 | 176 | // SetAdminID sets the "admin" edge to the User entity by ID. 177 | func (cuo *CategoryUpdateOne) SetAdminID(id int) *CategoryUpdateOne { 178 | cuo.mutation.SetAdminID(id) 179 | return cuo 180 | } 181 | 182 | // SetNillableAdminID sets the "admin" edge to the User entity by ID if the given value is not nil. 183 | func (cuo *CategoryUpdateOne) SetNillableAdminID(id *int) *CategoryUpdateOne { 184 | if id != nil { 185 | cuo = cuo.SetAdminID(*id) 186 | } 187 | return cuo 188 | } 189 | 190 | // SetAdmin sets the "admin" edge to the User entity. 191 | func (cuo *CategoryUpdateOne) SetAdmin(u *User) *CategoryUpdateOne { 192 | return cuo.SetAdminID(u.ID) 193 | } 194 | 195 | // Mutation returns the CategoryMutation object of the builder. 196 | func (cuo *CategoryUpdateOne) Mutation() *CategoryMutation { 197 | return cuo.mutation 198 | } 199 | 200 | // ClearAdmin clears the "admin" edge to the User entity. 201 | func (cuo *CategoryUpdateOne) ClearAdmin() *CategoryUpdateOne { 202 | cuo.mutation.ClearAdmin() 203 | return cuo 204 | } 205 | 206 | // Select allows selecting one or more fields (columns) of the returned entity. 207 | // The default is selecting all fields defined in the entity schema. 208 | func (cuo *CategoryUpdateOne) Select(field string, fields ...string) *CategoryUpdateOne { 209 | cuo.fields = append([]string{field}, fields...) 210 | return cuo 211 | } 212 | 213 | // Save executes the query and returns the updated Category entity. 214 | func (cuo *CategoryUpdateOne) Save(ctx context.Context) (*Category, error) { 215 | return withHooks[*Category, CategoryMutation](ctx, cuo.sqlSave, cuo.mutation, cuo.hooks) 216 | } 217 | 218 | // SaveX is like Save, but panics if an error occurs. 219 | func (cuo *CategoryUpdateOne) SaveX(ctx context.Context) *Category { 220 | node, err := cuo.Save(ctx) 221 | if err != nil { 222 | panic(err) 223 | } 224 | return node 225 | } 226 | 227 | // Exec executes the query on the entity. 228 | func (cuo *CategoryUpdateOne) Exec(ctx context.Context) error { 229 | _, err := cuo.Save(ctx) 230 | return err 231 | } 232 | 233 | // ExecX is like Exec, but panics if an error occurs. 234 | func (cuo *CategoryUpdateOne) ExecX(ctx context.Context) { 235 | if err := cuo.Exec(ctx); err != nil { 236 | panic(err) 237 | } 238 | } 239 | 240 | func (cuo *CategoryUpdateOne) sqlSave(ctx context.Context) (_node *Category, err error) { 241 | _spec := &sqlgraph.UpdateSpec{ 242 | Node: &sqlgraph.NodeSpec{ 243 | Table: category.Table, 244 | Columns: category.Columns, 245 | ID: &sqlgraph.FieldSpec{ 246 | Type: field.TypeInt, 247 | Column: category.FieldID, 248 | }, 249 | }, 250 | } 251 | id, ok := cuo.mutation.ID() 252 | if !ok { 253 | return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Category.id" for update`)} 254 | } 255 | _spec.Node.ID.Value = id 256 | if fields := cuo.fields; len(fields) > 0 { 257 | _spec.Node.Columns = make([]string, 0, len(fields)) 258 | _spec.Node.Columns = append(_spec.Node.Columns, category.FieldID) 259 | for _, f := range fields { 260 | if !category.ValidColumn(f) { 261 | return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} 262 | } 263 | if f != category.FieldID { 264 | _spec.Node.Columns = append(_spec.Node.Columns, f) 265 | } 266 | } 267 | } 268 | if ps := cuo.mutation.predicates; len(ps) > 0 { 269 | _spec.Predicate = func(selector *sql.Selector) { 270 | for i := range ps { 271 | ps[i](selector) 272 | } 273 | } 274 | } 275 | if value, ok := cuo.mutation.Name(); ok { 276 | _spec.SetField(category.FieldName, field.TypeString, value) 277 | } 278 | if cuo.mutation.AdminCleared() { 279 | edge := &sqlgraph.EdgeSpec{ 280 | Rel: sqlgraph.M2O, 281 | Inverse: false, 282 | Table: category.AdminTable, 283 | Columns: []string{category.AdminColumn}, 284 | Bidi: false, 285 | Target: &sqlgraph.EdgeTarget{ 286 | IDSpec: &sqlgraph.FieldSpec{ 287 | Type: field.TypeInt, 288 | Column: user.FieldID, 289 | }, 290 | }, 291 | } 292 | _spec.Edges.Clear = append(_spec.Edges.Clear, edge) 293 | } 294 | if nodes := cuo.mutation.AdminIDs(); len(nodes) > 0 { 295 | edge := &sqlgraph.EdgeSpec{ 296 | Rel: sqlgraph.M2O, 297 | Inverse: false, 298 | Table: category.AdminTable, 299 | Columns: []string{category.AdminColumn}, 300 | Bidi: false, 301 | Target: &sqlgraph.EdgeTarget{ 302 | IDSpec: &sqlgraph.FieldSpec{ 303 | Type: field.TypeInt, 304 | Column: user.FieldID, 305 | }, 306 | }, 307 | } 308 | for _, k := range nodes { 309 | edge.Target.Nodes = append(edge.Target.Nodes, k) 310 | } 311 | _spec.Edges.Add = append(_spec.Edges.Add, edge) 312 | } 313 | _node = &Category{config: cuo.config} 314 | _spec.Assign = _node.assignValues 315 | _spec.ScanValues = _node.scanValues 316 | if err = sqlgraph.UpdateNode(ctx, cuo.driver, _spec); err != nil { 317 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 318 | err = &NotFoundError{category.Label} 319 | } else if sqlgraph.IsConstraintError(err) { 320 | err = &ConstraintError{msg: err.Error(), wrap: err} 321 | } 322 | return nil, err 323 | } 324 | cuo.mutation.done = true 325 | return _node, nil 326 | } 327 | -------------------------------------------------------------------------------- /ent/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | "log" 10 | 11 | "github.com/rotemtam/ent-grpc-example/ent/migrate" 12 | 13 | "github.com/rotemtam/ent-grpc-example/ent/category" 14 | "github.com/rotemtam/ent-grpc-example/ent/user" 15 | 16 | "entgo.io/ent/dialect" 17 | "entgo.io/ent/dialect/sql" 18 | "entgo.io/ent/dialect/sql/sqlgraph" 19 | ) 20 | 21 | // Client is the client that holds all ent builders. 22 | type Client struct { 23 | config 24 | // Schema is the client for creating, migrating and dropping schema. 25 | Schema *migrate.Schema 26 | // Category is the client for interacting with the Category builders. 27 | Category *CategoryClient 28 | // User is the client for interacting with the User builders. 29 | User *UserClient 30 | } 31 | 32 | // NewClient creates a new client configured with the given options. 33 | func NewClient(opts ...Option) *Client { 34 | cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}} 35 | cfg.options(opts...) 36 | client := &Client{config: cfg} 37 | client.init() 38 | return client 39 | } 40 | 41 | func (c *Client) init() { 42 | c.Schema = migrate.NewSchema(c.driver) 43 | c.Category = NewCategoryClient(c.config) 44 | c.User = NewUserClient(c.config) 45 | } 46 | 47 | // Open opens a database/sql.DB specified by the driver name and 48 | // the data source name, and returns a new client attached to it. 49 | // Optional parameters can be added for configuring the client. 50 | func Open(driverName, dataSourceName string, options ...Option) (*Client, error) { 51 | switch driverName { 52 | case dialect.MySQL, dialect.Postgres, dialect.SQLite: 53 | drv, err := sql.Open(driverName, dataSourceName) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return NewClient(append(options, Driver(drv))...), nil 58 | default: 59 | return nil, fmt.Errorf("unsupported driver: %q", driverName) 60 | } 61 | } 62 | 63 | // Tx returns a new transactional client. The provided context 64 | // is used until the transaction is committed or rolled back. 65 | func (c *Client) Tx(ctx context.Context) (*Tx, error) { 66 | if _, ok := c.driver.(*txDriver); ok { 67 | return nil, errors.New("ent: cannot start a transaction within a transaction") 68 | } 69 | tx, err := newTx(ctx, c.driver) 70 | if err != nil { 71 | return nil, fmt.Errorf("ent: starting a transaction: %w", err) 72 | } 73 | cfg := c.config 74 | cfg.driver = tx 75 | return &Tx{ 76 | ctx: ctx, 77 | config: cfg, 78 | Category: NewCategoryClient(cfg), 79 | User: NewUserClient(cfg), 80 | }, nil 81 | } 82 | 83 | // BeginTx returns a transactional client with specified options. 84 | func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { 85 | if _, ok := c.driver.(*txDriver); ok { 86 | return nil, errors.New("ent: cannot start a transaction within a transaction") 87 | } 88 | tx, err := c.driver.(interface { 89 | BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) 90 | }).BeginTx(ctx, opts) 91 | if err != nil { 92 | return nil, fmt.Errorf("ent: starting a transaction: %w", err) 93 | } 94 | cfg := c.config 95 | cfg.driver = &txDriver{tx: tx, drv: c.driver} 96 | return &Tx{ 97 | ctx: ctx, 98 | config: cfg, 99 | Category: NewCategoryClient(cfg), 100 | User: NewUserClient(cfg), 101 | }, nil 102 | } 103 | 104 | // Debug returns a new debug-client. It's used to get verbose logging on specific operations. 105 | // 106 | // client.Debug(). 107 | // Category. 108 | // Query(). 109 | // Count(ctx) 110 | func (c *Client) Debug() *Client { 111 | if c.debug { 112 | return c 113 | } 114 | cfg := c.config 115 | cfg.driver = dialect.Debug(c.driver, c.log) 116 | client := &Client{config: cfg} 117 | client.init() 118 | return client 119 | } 120 | 121 | // Close closes the database connection and prevents new queries from starting. 122 | func (c *Client) Close() error { 123 | return c.driver.Close() 124 | } 125 | 126 | // Use adds the mutation hooks to all the entity clients. 127 | // In order to add hooks to a specific client, call: `client.Node.Use(...)`. 128 | func (c *Client) Use(hooks ...Hook) { 129 | c.Category.Use(hooks...) 130 | c.User.Use(hooks...) 131 | } 132 | 133 | // Intercept adds the query interceptors to all the entity clients. 134 | // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. 135 | func (c *Client) Intercept(interceptors ...Interceptor) { 136 | c.Category.Intercept(interceptors...) 137 | c.User.Intercept(interceptors...) 138 | } 139 | 140 | // Mutate implements the ent.Mutator interface. 141 | func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { 142 | switch m := m.(type) { 143 | case *CategoryMutation: 144 | return c.Category.mutate(ctx, m) 145 | case *UserMutation: 146 | return c.User.mutate(ctx, m) 147 | default: 148 | return nil, fmt.Errorf("ent: unknown mutation type %T", m) 149 | } 150 | } 151 | 152 | // CategoryClient is a client for the Category schema. 153 | type CategoryClient struct { 154 | config 155 | } 156 | 157 | // NewCategoryClient returns a client for the Category from the given config. 158 | func NewCategoryClient(c config) *CategoryClient { 159 | return &CategoryClient{config: c} 160 | } 161 | 162 | // Use adds a list of mutation hooks to the hooks stack. 163 | // A call to `Use(f, g, h)` equals to `category.Hooks(f(g(h())))`. 164 | func (c *CategoryClient) Use(hooks ...Hook) { 165 | c.hooks.Category = append(c.hooks.Category, hooks...) 166 | } 167 | 168 | // Use adds a list of query interceptors to the interceptors stack. 169 | // A call to `Intercept(f, g, h)` equals to `category.Intercept(f(g(h())))`. 170 | func (c *CategoryClient) Intercept(interceptors ...Interceptor) { 171 | c.inters.Category = append(c.inters.Category, interceptors...) 172 | } 173 | 174 | // Create returns a builder for creating a Category entity. 175 | func (c *CategoryClient) Create() *CategoryCreate { 176 | mutation := newCategoryMutation(c.config, OpCreate) 177 | return &CategoryCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} 178 | } 179 | 180 | // CreateBulk returns a builder for creating a bulk of Category entities. 181 | func (c *CategoryClient) CreateBulk(builders ...*CategoryCreate) *CategoryCreateBulk { 182 | return &CategoryCreateBulk{config: c.config, builders: builders} 183 | } 184 | 185 | // Update returns an update builder for Category. 186 | func (c *CategoryClient) Update() *CategoryUpdate { 187 | mutation := newCategoryMutation(c.config, OpUpdate) 188 | return &CategoryUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} 189 | } 190 | 191 | // UpdateOne returns an update builder for the given entity. 192 | func (c *CategoryClient) UpdateOne(ca *Category) *CategoryUpdateOne { 193 | mutation := newCategoryMutation(c.config, OpUpdateOne, withCategory(ca)) 194 | return &CategoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 195 | } 196 | 197 | // UpdateOneID returns an update builder for the given id. 198 | func (c *CategoryClient) UpdateOneID(id int) *CategoryUpdateOne { 199 | mutation := newCategoryMutation(c.config, OpUpdateOne, withCategoryID(id)) 200 | return &CategoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 201 | } 202 | 203 | // Delete returns a delete builder for Category. 204 | func (c *CategoryClient) Delete() *CategoryDelete { 205 | mutation := newCategoryMutation(c.config, OpDelete) 206 | return &CategoryDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} 207 | } 208 | 209 | // DeleteOne returns a builder for deleting the given entity. 210 | func (c *CategoryClient) DeleteOne(ca *Category) *CategoryDeleteOne { 211 | return c.DeleteOneID(ca.ID) 212 | } 213 | 214 | // DeleteOneID returns a builder for deleting the given entity by its id. 215 | func (c *CategoryClient) DeleteOneID(id int) *CategoryDeleteOne { 216 | builder := c.Delete().Where(category.ID(id)) 217 | builder.mutation.id = &id 218 | builder.mutation.op = OpDeleteOne 219 | return &CategoryDeleteOne{builder} 220 | } 221 | 222 | // Query returns a query builder for Category. 223 | func (c *CategoryClient) Query() *CategoryQuery { 224 | return &CategoryQuery{ 225 | config: c.config, 226 | ctx: &QueryContext{Type: TypeCategory}, 227 | inters: c.Interceptors(), 228 | } 229 | } 230 | 231 | // Get returns a Category entity by its id. 232 | func (c *CategoryClient) Get(ctx context.Context, id int) (*Category, error) { 233 | return c.Query().Where(category.ID(id)).Only(ctx) 234 | } 235 | 236 | // GetX is like Get, but panics if an error occurs. 237 | func (c *CategoryClient) GetX(ctx context.Context, id int) *Category { 238 | obj, err := c.Get(ctx, id) 239 | if err != nil { 240 | panic(err) 241 | } 242 | return obj 243 | } 244 | 245 | // QueryAdmin queries the admin edge of a Category. 246 | func (c *CategoryClient) QueryAdmin(ca *Category) *UserQuery { 247 | query := (&UserClient{config: c.config}).Query() 248 | query.path = func(context.Context) (fromV *sql.Selector, _ error) { 249 | id := ca.ID 250 | step := sqlgraph.NewStep( 251 | sqlgraph.From(category.Table, category.FieldID, id), 252 | sqlgraph.To(user.Table, user.FieldID), 253 | sqlgraph.Edge(sqlgraph.M2O, false, category.AdminTable, category.AdminColumn), 254 | ) 255 | fromV = sqlgraph.Neighbors(ca.driver.Dialect(), step) 256 | return fromV, nil 257 | } 258 | return query 259 | } 260 | 261 | // Hooks returns the client hooks. 262 | func (c *CategoryClient) Hooks() []Hook { 263 | return c.hooks.Category 264 | } 265 | 266 | // Interceptors returns the client interceptors. 267 | func (c *CategoryClient) Interceptors() []Interceptor { 268 | return c.inters.Category 269 | } 270 | 271 | func (c *CategoryClient) mutate(ctx context.Context, m *CategoryMutation) (Value, error) { 272 | switch m.Op() { 273 | case OpCreate: 274 | return (&CategoryCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 275 | case OpUpdate: 276 | return (&CategoryUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 277 | case OpUpdateOne: 278 | return (&CategoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 279 | case OpDelete, OpDeleteOne: 280 | return (&CategoryDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) 281 | default: 282 | return nil, fmt.Errorf("ent: unknown Category mutation op: %q", m.Op()) 283 | } 284 | } 285 | 286 | // UserClient is a client for the User schema. 287 | type UserClient struct { 288 | config 289 | } 290 | 291 | // NewUserClient returns a client for the User from the given config. 292 | func NewUserClient(c config) *UserClient { 293 | return &UserClient{config: c} 294 | } 295 | 296 | // Use adds a list of mutation hooks to the hooks stack. 297 | // A call to `Use(f, g, h)` equals to `user.Hooks(f(g(h())))`. 298 | func (c *UserClient) Use(hooks ...Hook) { 299 | c.hooks.User = append(c.hooks.User, hooks...) 300 | } 301 | 302 | // Use adds a list of query interceptors to the interceptors stack. 303 | // A call to `Intercept(f, g, h)` equals to `user.Intercept(f(g(h())))`. 304 | func (c *UserClient) Intercept(interceptors ...Interceptor) { 305 | c.inters.User = append(c.inters.User, interceptors...) 306 | } 307 | 308 | // Create returns a builder for creating a User entity. 309 | func (c *UserClient) Create() *UserCreate { 310 | mutation := newUserMutation(c.config, OpCreate) 311 | return &UserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} 312 | } 313 | 314 | // CreateBulk returns a builder for creating a bulk of User entities. 315 | func (c *UserClient) CreateBulk(builders ...*UserCreate) *UserCreateBulk { 316 | return &UserCreateBulk{config: c.config, builders: builders} 317 | } 318 | 319 | // Update returns an update builder for User. 320 | func (c *UserClient) Update() *UserUpdate { 321 | mutation := newUserMutation(c.config, OpUpdate) 322 | return &UserUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} 323 | } 324 | 325 | // UpdateOne returns an update builder for the given entity. 326 | func (c *UserClient) UpdateOne(u *User) *UserUpdateOne { 327 | mutation := newUserMutation(c.config, OpUpdateOne, withUser(u)) 328 | return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 329 | } 330 | 331 | // UpdateOneID returns an update builder for the given id. 332 | func (c *UserClient) UpdateOneID(id int) *UserUpdateOne { 333 | mutation := newUserMutation(c.config, OpUpdateOne, withUserID(id)) 334 | return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 335 | } 336 | 337 | // Delete returns a delete builder for User. 338 | func (c *UserClient) Delete() *UserDelete { 339 | mutation := newUserMutation(c.config, OpDelete) 340 | return &UserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} 341 | } 342 | 343 | // DeleteOne returns a builder for deleting the given entity. 344 | func (c *UserClient) DeleteOne(u *User) *UserDeleteOne { 345 | return c.DeleteOneID(u.ID) 346 | } 347 | 348 | // DeleteOneID returns a builder for deleting the given entity by its id. 349 | func (c *UserClient) DeleteOneID(id int) *UserDeleteOne { 350 | builder := c.Delete().Where(user.ID(id)) 351 | builder.mutation.id = &id 352 | builder.mutation.op = OpDeleteOne 353 | return &UserDeleteOne{builder} 354 | } 355 | 356 | // Query returns a query builder for User. 357 | func (c *UserClient) Query() *UserQuery { 358 | return &UserQuery{ 359 | config: c.config, 360 | ctx: &QueryContext{Type: TypeUser}, 361 | inters: c.Interceptors(), 362 | } 363 | } 364 | 365 | // Get returns a User entity by its id. 366 | func (c *UserClient) Get(ctx context.Context, id int) (*User, error) { 367 | return c.Query().Where(user.ID(id)).Only(ctx) 368 | } 369 | 370 | // GetX is like Get, but panics if an error occurs. 371 | func (c *UserClient) GetX(ctx context.Context, id int) *User { 372 | obj, err := c.Get(ctx, id) 373 | if err != nil { 374 | panic(err) 375 | } 376 | return obj 377 | } 378 | 379 | // QueryAdministered queries the administered edge of a User. 380 | func (c *UserClient) QueryAdministered(u *User) *CategoryQuery { 381 | query := (&CategoryClient{config: c.config}).Query() 382 | query.path = func(context.Context) (fromV *sql.Selector, _ error) { 383 | id := u.ID 384 | step := sqlgraph.NewStep( 385 | sqlgraph.From(user.Table, user.FieldID, id), 386 | sqlgraph.To(category.Table, category.FieldID), 387 | sqlgraph.Edge(sqlgraph.O2M, true, user.AdministeredTable, user.AdministeredColumn), 388 | ) 389 | fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) 390 | return fromV, nil 391 | } 392 | return query 393 | } 394 | 395 | // Hooks returns the client hooks. 396 | func (c *UserClient) Hooks() []Hook { 397 | return c.hooks.User 398 | } 399 | 400 | // Interceptors returns the client interceptors. 401 | func (c *UserClient) Interceptors() []Interceptor { 402 | return c.inters.User 403 | } 404 | 405 | func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) { 406 | switch m.Op() { 407 | case OpCreate: 408 | return (&UserCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 409 | case OpUpdate: 410 | return (&UserUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 411 | case OpUpdateOne: 412 | return (&UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 413 | case OpDelete, OpDeleteOne: 414 | return (&UserDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) 415 | default: 416 | return nil, fmt.Errorf("ent: unknown User mutation op: %q", m.Op()) 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /ent/config.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "entgo.io/ent" 7 | "entgo.io/ent/dialect" 8 | ) 9 | 10 | // Option function to configure the client. 11 | type Option func(*config) 12 | 13 | // Config is the configuration for the client and its builder. 14 | type config struct { 15 | // driver used for executing database requests. 16 | driver dialect.Driver 17 | // debug enable a debug logging. 18 | debug bool 19 | // log used for logging on debug mode. 20 | log func(...any) 21 | // hooks to execute on mutations. 22 | hooks *hooks 23 | // interceptors to execute on queries. 24 | inters *inters 25 | } 26 | 27 | // hooks and interceptors per client, for fast access. 28 | type ( 29 | hooks struct { 30 | Category []ent.Hook 31 | User []ent.Hook 32 | } 33 | inters struct { 34 | Category []ent.Interceptor 35 | User []ent.Interceptor 36 | } 37 | ) 38 | 39 | // Options applies the options on the config object. 40 | func (c *config) options(opts ...Option) { 41 | for _, opt := range opts { 42 | opt(c) 43 | } 44 | if c.debug { 45 | c.driver = dialect.Debug(c.driver, c.log) 46 | } 47 | } 48 | 49 | // Debug enables debug logging on the ent.Driver. 50 | func Debug() Option { 51 | return func(c *config) { 52 | c.debug = true 53 | } 54 | } 55 | 56 | // Log sets the logging function for debug mode. 57 | func Log(fn func(...any)) Option { 58 | return func(c *config) { 59 | c.log = fn 60 | } 61 | } 62 | 63 | // Driver configures the client driver. 64 | func Driver(driver dialect.Driver) Option { 65 | return func(c *config) { 66 | c.driver = driver 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ent/context.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | type clientCtxKey struct{} 10 | 11 | // FromContext returns a Client stored inside a context, or nil if there isn't one. 12 | func FromContext(ctx context.Context) *Client { 13 | c, _ := ctx.Value(clientCtxKey{}).(*Client) 14 | return c 15 | } 16 | 17 | // NewContext returns a new context with the given Client attached. 18 | func NewContext(parent context.Context, c *Client) context.Context { 19 | return context.WithValue(parent, clientCtxKey{}, c) 20 | } 21 | 22 | type txCtxKey struct{} 23 | 24 | // TxFromContext returns a Tx stored inside a context, or nil if there isn't one. 25 | func TxFromContext(ctx context.Context) *Tx { 26 | tx, _ := ctx.Value(txCtxKey{}).(*Tx) 27 | return tx 28 | } 29 | 30 | // NewTxContext returns a new context with the given Tx attached. 31 | func NewTxContext(parent context.Context, tx *Tx) context.Context { 32 | return context.WithValue(parent, txCtxKey{}, tx) 33 | } 34 | -------------------------------------------------------------------------------- /ent/ent.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | "reflect" 10 | 11 | "entgo.io/ent" 12 | "entgo.io/ent/dialect/sql" 13 | "entgo.io/ent/dialect/sql/sqlgraph" 14 | "github.com/rotemtam/ent-grpc-example/ent/category" 15 | "github.com/rotemtam/ent-grpc-example/ent/user" 16 | ) 17 | 18 | // ent aliases to avoid import conflicts in user's code. 19 | type ( 20 | Op = ent.Op 21 | Hook = ent.Hook 22 | Value = ent.Value 23 | Query = ent.Query 24 | QueryContext = ent.QueryContext 25 | Querier = ent.Querier 26 | QuerierFunc = ent.QuerierFunc 27 | Interceptor = ent.Interceptor 28 | InterceptFunc = ent.InterceptFunc 29 | Traverser = ent.Traverser 30 | TraverseFunc = ent.TraverseFunc 31 | Policy = ent.Policy 32 | Mutator = ent.Mutator 33 | Mutation = ent.Mutation 34 | MutateFunc = ent.MutateFunc 35 | ) 36 | 37 | // OrderFunc applies an ordering on the sql selector. 38 | type OrderFunc func(*sql.Selector) 39 | 40 | // columnChecker returns a function indicates if the column exists in the given column. 41 | func columnChecker(table string) func(string) error { 42 | checks := map[string]func(string) bool{ 43 | category.Table: category.ValidColumn, 44 | user.Table: user.ValidColumn, 45 | } 46 | check, ok := checks[table] 47 | if !ok { 48 | return func(string) error { 49 | return fmt.Errorf("unknown table %q", table) 50 | } 51 | } 52 | return func(column string) error { 53 | if !check(column) { 54 | return fmt.Errorf("unknown column %q for table %q", column, table) 55 | } 56 | return nil 57 | } 58 | } 59 | 60 | // Asc applies the given fields in ASC order. 61 | func Asc(fields ...string) OrderFunc { 62 | return func(s *sql.Selector) { 63 | check := columnChecker(s.TableName()) 64 | for _, f := range fields { 65 | if err := check(f); err != nil { 66 | s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) 67 | } 68 | s.OrderBy(sql.Asc(s.C(f))) 69 | } 70 | } 71 | } 72 | 73 | // Desc applies the given fields in DESC order. 74 | func Desc(fields ...string) OrderFunc { 75 | return func(s *sql.Selector) { 76 | check := columnChecker(s.TableName()) 77 | for _, f := range fields { 78 | if err := check(f); err != nil { 79 | s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) 80 | } 81 | s.OrderBy(sql.Desc(s.C(f))) 82 | } 83 | } 84 | } 85 | 86 | // AggregateFunc applies an aggregation step on the group-by traversal/selector. 87 | type AggregateFunc func(*sql.Selector) string 88 | 89 | // As is a pseudo aggregation function for renaming another other functions with custom names. For example: 90 | // 91 | // GroupBy(field1, field2). 92 | // Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")). 93 | // Scan(ctx, &v) 94 | func As(fn AggregateFunc, end string) AggregateFunc { 95 | return func(s *sql.Selector) string { 96 | return sql.As(fn(s), end) 97 | } 98 | } 99 | 100 | // Count applies the "count" aggregation function on each group. 101 | func Count() AggregateFunc { 102 | return func(s *sql.Selector) string { 103 | return sql.Count("*") 104 | } 105 | } 106 | 107 | // Max applies the "max" aggregation function on the given field of each group. 108 | func Max(field string) AggregateFunc { 109 | return func(s *sql.Selector) string { 110 | check := columnChecker(s.TableName()) 111 | if err := check(field); err != nil { 112 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 113 | return "" 114 | } 115 | return sql.Max(s.C(field)) 116 | } 117 | } 118 | 119 | // Mean applies the "mean" aggregation function on the given field of each group. 120 | func Mean(field string) AggregateFunc { 121 | return func(s *sql.Selector) string { 122 | check := columnChecker(s.TableName()) 123 | if err := check(field); err != nil { 124 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 125 | return "" 126 | } 127 | return sql.Avg(s.C(field)) 128 | } 129 | } 130 | 131 | // Min applies the "min" aggregation function on the given field of each group. 132 | func Min(field string) AggregateFunc { 133 | return func(s *sql.Selector) string { 134 | check := columnChecker(s.TableName()) 135 | if err := check(field); err != nil { 136 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 137 | return "" 138 | } 139 | return sql.Min(s.C(field)) 140 | } 141 | } 142 | 143 | // Sum applies the "sum" aggregation function on the given field of each group. 144 | func Sum(field string) AggregateFunc { 145 | return func(s *sql.Selector) string { 146 | check := columnChecker(s.TableName()) 147 | if err := check(field); err != nil { 148 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 149 | return "" 150 | } 151 | return sql.Sum(s.C(field)) 152 | } 153 | } 154 | 155 | // ValidationError returns when validating a field or edge fails. 156 | type ValidationError struct { 157 | Name string // Field or edge name. 158 | err error 159 | } 160 | 161 | // Error implements the error interface. 162 | func (e *ValidationError) Error() string { 163 | return e.err.Error() 164 | } 165 | 166 | // Unwrap implements the errors.Wrapper interface. 167 | func (e *ValidationError) Unwrap() error { 168 | return e.err 169 | } 170 | 171 | // IsValidationError returns a boolean indicating whether the error is a validation error. 172 | func IsValidationError(err error) bool { 173 | if err == nil { 174 | return false 175 | } 176 | var e *ValidationError 177 | return errors.As(err, &e) 178 | } 179 | 180 | // NotFoundError returns when trying to fetch a specific entity and it was not found in the database. 181 | type NotFoundError struct { 182 | label string 183 | } 184 | 185 | // Error implements the error interface. 186 | func (e *NotFoundError) Error() string { 187 | return "ent: " + e.label + " not found" 188 | } 189 | 190 | // IsNotFound returns a boolean indicating whether the error is a not found error. 191 | func IsNotFound(err error) bool { 192 | if err == nil { 193 | return false 194 | } 195 | var e *NotFoundError 196 | return errors.As(err, &e) 197 | } 198 | 199 | // MaskNotFound masks not found error. 200 | func MaskNotFound(err error) error { 201 | if IsNotFound(err) { 202 | return nil 203 | } 204 | return err 205 | } 206 | 207 | // NotSingularError returns when trying to fetch a singular entity and more then one was found in the database. 208 | type NotSingularError struct { 209 | label string 210 | } 211 | 212 | // Error implements the error interface. 213 | func (e *NotSingularError) Error() string { 214 | return "ent: " + e.label + " not singular" 215 | } 216 | 217 | // IsNotSingular returns a boolean indicating whether the error is a not singular error. 218 | func IsNotSingular(err error) bool { 219 | if err == nil { 220 | return false 221 | } 222 | var e *NotSingularError 223 | return errors.As(err, &e) 224 | } 225 | 226 | // NotLoadedError returns when trying to get a node that was not loaded by the query. 227 | type NotLoadedError struct { 228 | edge string 229 | } 230 | 231 | // Error implements the error interface. 232 | func (e *NotLoadedError) Error() string { 233 | return "ent: " + e.edge + " edge was not loaded" 234 | } 235 | 236 | // IsNotLoaded returns a boolean indicating whether the error is a not loaded error. 237 | func IsNotLoaded(err error) bool { 238 | if err == nil { 239 | return false 240 | } 241 | var e *NotLoadedError 242 | return errors.As(err, &e) 243 | } 244 | 245 | // ConstraintError returns when trying to create/update one or more entities and 246 | // one or more of their constraints failed. For example, violation of edge or 247 | // field uniqueness. 248 | type ConstraintError struct { 249 | msg string 250 | wrap error 251 | } 252 | 253 | // Error implements the error interface. 254 | func (e ConstraintError) Error() string { 255 | return "ent: constraint failed: " + e.msg 256 | } 257 | 258 | // Unwrap implements the errors.Wrapper interface. 259 | func (e *ConstraintError) Unwrap() error { 260 | return e.wrap 261 | } 262 | 263 | // IsConstraintError returns a boolean indicating whether the error is a constraint failure. 264 | func IsConstraintError(err error) bool { 265 | if err == nil { 266 | return false 267 | } 268 | var e *ConstraintError 269 | return errors.As(err, &e) 270 | } 271 | 272 | // selector embedded by the different Select/GroupBy builders. 273 | type selector struct { 274 | label string 275 | flds *[]string 276 | fns []AggregateFunc 277 | scan func(context.Context, any) error 278 | } 279 | 280 | // ScanX is like Scan, but panics if an error occurs. 281 | func (s *selector) ScanX(ctx context.Context, v any) { 282 | if err := s.scan(ctx, v); err != nil { 283 | panic(err) 284 | } 285 | } 286 | 287 | // Strings returns list of strings from a selector. It is only allowed when selecting one field. 288 | func (s *selector) Strings(ctx context.Context) ([]string, error) { 289 | if len(*s.flds) > 1 { 290 | return nil, errors.New("ent: Strings is not achievable when selecting more than 1 field") 291 | } 292 | var v []string 293 | if err := s.scan(ctx, &v); err != nil { 294 | return nil, err 295 | } 296 | return v, nil 297 | } 298 | 299 | // StringsX is like Strings, but panics if an error occurs. 300 | func (s *selector) StringsX(ctx context.Context) []string { 301 | v, err := s.Strings(ctx) 302 | if err != nil { 303 | panic(err) 304 | } 305 | return v 306 | } 307 | 308 | // String returns a single string from a selector. It is only allowed when selecting one field. 309 | func (s *selector) String(ctx context.Context) (_ string, err error) { 310 | var v []string 311 | if v, err = s.Strings(ctx); err != nil { 312 | return 313 | } 314 | switch len(v) { 315 | case 1: 316 | return v[0], nil 317 | case 0: 318 | err = &NotFoundError{s.label} 319 | default: 320 | err = fmt.Errorf("ent: Strings returned %d results when one was expected", len(v)) 321 | } 322 | return 323 | } 324 | 325 | // StringX is like String, but panics if an error occurs. 326 | func (s *selector) StringX(ctx context.Context) string { 327 | v, err := s.String(ctx) 328 | if err != nil { 329 | panic(err) 330 | } 331 | return v 332 | } 333 | 334 | // Ints returns list of ints from a selector. It is only allowed when selecting one field. 335 | func (s *selector) Ints(ctx context.Context) ([]int, error) { 336 | if len(*s.flds) > 1 { 337 | return nil, errors.New("ent: Ints is not achievable when selecting more than 1 field") 338 | } 339 | var v []int 340 | if err := s.scan(ctx, &v); err != nil { 341 | return nil, err 342 | } 343 | return v, nil 344 | } 345 | 346 | // IntsX is like Ints, but panics if an error occurs. 347 | func (s *selector) IntsX(ctx context.Context) []int { 348 | v, err := s.Ints(ctx) 349 | if err != nil { 350 | panic(err) 351 | } 352 | return v 353 | } 354 | 355 | // Int returns a single int from a selector. It is only allowed when selecting one field. 356 | func (s *selector) Int(ctx context.Context) (_ int, err error) { 357 | var v []int 358 | if v, err = s.Ints(ctx); err != nil { 359 | return 360 | } 361 | switch len(v) { 362 | case 1: 363 | return v[0], nil 364 | case 0: 365 | err = &NotFoundError{s.label} 366 | default: 367 | err = fmt.Errorf("ent: Ints returned %d results when one was expected", len(v)) 368 | } 369 | return 370 | } 371 | 372 | // IntX is like Int, but panics if an error occurs. 373 | func (s *selector) IntX(ctx context.Context) int { 374 | v, err := s.Int(ctx) 375 | if err != nil { 376 | panic(err) 377 | } 378 | return v 379 | } 380 | 381 | // Float64s returns list of float64s from a selector. It is only allowed when selecting one field. 382 | func (s *selector) Float64s(ctx context.Context) ([]float64, error) { 383 | if len(*s.flds) > 1 { 384 | return nil, errors.New("ent: Float64s is not achievable when selecting more than 1 field") 385 | } 386 | var v []float64 387 | if err := s.scan(ctx, &v); err != nil { 388 | return nil, err 389 | } 390 | return v, nil 391 | } 392 | 393 | // Float64sX is like Float64s, but panics if an error occurs. 394 | func (s *selector) Float64sX(ctx context.Context) []float64 { 395 | v, err := s.Float64s(ctx) 396 | if err != nil { 397 | panic(err) 398 | } 399 | return v 400 | } 401 | 402 | // Float64 returns a single float64 from a selector. It is only allowed when selecting one field. 403 | func (s *selector) Float64(ctx context.Context) (_ float64, err error) { 404 | var v []float64 405 | if v, err = s.Float64s(ctx); err != nil { 406 | return 407 | } 408 | switch len(v) { 409 | case 1: 410 | return v[0], nil 411 | case 0: 412 | err = &NotFoundError{s.label} 413 | default: 414 | err = fmt.Errorf("ent: Float64s returned %d results when one was expected", len(v)) 415 | } 416 | return 417 | } 418 | 419 | // Float64X is like Float64, but panics if an error occurs. 420 | func (s *selector) Float64X(ctx context.Context) float64 { 421 | v, err := s.Float64(ctx) 422 | if err != nil { 423 | panic(err) 424 | } 425 | return v 426 | } 427 | 428 | // Bools returns list of bools from a selector. It is only allowed when selecting one field. 429 | func (s *selector) Bools(ctx context.Context) ([]bool, error) { 430 | if len(*s.flds) > 1 { 431 | return nil, errors.New("ent: Bools is not achievable when selecting more than 1 field") 432 | } 433 | var v []bool 434 | if err := s.scan(ctx, &v); err != nil { 435 | return nil, err 436 | } 437 | return v, nil 438 | } 439 | 440 | // BoolsX is like Bools, but panics if an error occurs. 441 | func (s *selector) BoolsX(ctx context.Context) []bool { 442 | v, err := s.Bools(ctx) 443 | if err != nil { 444 | panic(err) 445 | } 446 | return v 447 | } 448 | 449 | // Bool returns a single bool from a selector. It is only allowed when selecting one field. 450 | func (s *selector) Bool(ctx context.Context) (_ bool, err error) { 451 | var v []bool 452 | if v, err = s.Bools(ctx); err != nil { 453 | return 454 | } 455 | switch len(v) { 456 | case 1: 457 | return v[0], nil 458 | case 0: 459 | err = &NotFoundError{s.label} 460 | default: 461 | err = fmt.Errorf("ent: Bools returned %d results when one was expected", len(v)) 462 | } 463 | return 464 | } 465 | 466 | // BoolX is like Bool, but panics if an error occurs. 467 | func (s *selector) BoolX(ctx context.Context) bool { 468 | v, err := s.Bool(ctx) 469 | if err != nil { 470 | panic(err) 471 | } 472 | return v 473 | } 474 | 475 | // withHooks invokes the builder operation with the given hooks, if any. 476 | func withHooks[V Value, M any, PM interface { 477 | *M 478 | Mutation 479 | }](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) { 480 | if len(hooks) == 0 { 481 | return exec(ctx) 482 | } 483 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 484 | mutationT, ok := m.(PM) 485 | if !ok { 486 | return nil, fmt.Errorf("unexpected mutation type %T", m) 487 | } 488 | // Set the mutation to the builder. 489 | *mutation = *mutationT 490 | return exec(ctx) 491 | }) 492 | for i := len(hooks) - 1; i >= 0; i-- { 493 | if hooks[i] == nil { 494 | return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 495 | } 496 | mut = hooks[i](mut) 497 | } 498 | v, err := mut.Mutate(ctx, mutation) 499 | if err != nil { 500 | return value, err 501 | } 502 | nv, ok := v.(V) 503 | if !ok { 504 | return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation) 505 | } 506 | return nv, nil 507 | } 508 | 509 | // setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist. 510 | func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context { 511 | if ent.QueryFromContext(ctx) == nil { 512 | qc.Op = op 513 | ctx = ent.NewQueryContext(ctx, qc) 514 | } 515 | return ctx 516 | } 517 | 518 | func querierAll[V Value, Q interface { 519 | sqlAll(context.Context, ...queryHook) (V, error) 520 | }]() Querier { 521 | return QuerierFunc(func(ctx context.Context, q Query) (Value, error) { 522 | query, ok := q.(Q) 523 | if !ok { 524 | return nil, fmt.Errorf("unexpected query type %T", q) 525 | } 526 | return query.sqlAll(ctx) 527 | }) 528 | } 529 | 530 | func querierCount[Q interface { 531 | sqlCount(context.Context) (int, error) 532 | }]() Querier { 533 | return QuerierFunc(func(ctx context.Context, q Query) (Value, error) { 534 | query, ok := q.(Q) 535 | if !ok { 536 | return nil, fmt.Errorf("unexpected query type %T", q) 537 | } 538 | return query.sqlCount(ctx) 539 | }) 540 | } 541 | 542 | func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) { 543 | for i := len(inters) - 1; i >= 0; i-- { 544 | qr = inters[i].Intercept(qr) 545 | } 546 | rv, err := qr.Query(ctx, q) 547 | if err != nil { 548 | return v, err 549 | } 550 | vt, ok := rv.(V) 551 | if !ok { 552 | return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v) 553 | } 554 | return vt, nil 555 | } 556 | 557 | func scanWithInterceptors[Q1 ent.Query, Q2 interface { 558 | sqlScan(context.Context, Q1, any) error 559 | }](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error { 560 | rv := reflect.ValueOf(v) 561 | var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) { 562 | query, ok := q.(Q1) 563 | if !ok { 564 | return nil, fmt.Errorf("unexpected query type %T", q) 565 | } 566 | if err := selectOrGroup.sqlScan(ctx, query, v); err != nil { 567 | return nil, err 568 | } 569 | if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() { 570 | return rv.Elem().Interface(), nil 571 | } 572 | return v, nil 573 | }) 574 | for i := len(inters) - 1; i >= 0; i-- { 575 | qr = inters[i].Intercept(qr) 576 | } 577 | vv, err := qr.Query(ctx, rootQuery) 578 | if err != nil { 579 | return err 580 | } 581 | switch rv2 := reflect.ValueOf(vv); { 582 | case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer: 583 | case rv.Type() == rv2.Type(): 584 | rv.Elem().Set(rv2.Elem()) 585 | case rv.Elem().Type() == rv2.Type(): 586 | rv.Elem().Set(rv2) 587 | } 588 | return nil 589 | } 590 | 591 | // queryHook describes an internal hook for the different sqlAll methods. 592 | type queryHook func(context.Context, *sqlgraph.QuerySpec) 593 | -------------------------------------------------------------------------------- /ent/enttest/enttest.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package enttest 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/rotemtam/ent-grpc-example/ent" 9 | // required by schema hooks. 10 | _ "github.com/rotemtam/ent-grpc-example/ent/runtime" 11 | 12 | "entgo.io/ent/dialect/sql/schema" 13 | "github.com/rotemtam/ent-grpc-example/ent/migrate" 14 | ) 15 | 16 | type ( 17 | // TestingT is the interface that is shared between 18 | // testing.T and testing.B and used by enttest. 19 | TestingT interface { 20 | FailNow() 21 | Error(...any) 22 | } 23 | 24 | // Option configures client creation. 25 | Option func(*options) 26 | 27 | options struct { 28 | opts []ent.Option 29 | migrateOpts []schema.MigrateOption 30 | } 31 | ) 32 | 33 | // WithOptions forwards options to client creation. 34 | func WithOptions(opts ...ent.Option) Option { 35 | return func(o *options) { 36 | o.opts = append(o.opts, opts...) 37 | } 38 | } 39 | 40 | // WithMigrateOptions forwards options to auto migration. 41 | func WithMigrateOptions(opts ...schema.MigrateOption) Option { 42 | return func(o *options) { 43 | o.migrateOpts = append(o.migrateOpts, opts...) 44 | } 45 | } 46 | 47 | func newOptions(opts []Option) *options { 48 | o := &options{} 49 | for _, opt := range opts { 50 | opt(o) 51 | } 52 | return o 53 | } 54 | 55 | // Open calls ent.Open and auto-run migration. 56 | func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Client { 57 | o := newOptions(opts) 58 | c, err := ent.Open(driverName, dataSourceName, o.opts...) 59 | if err != nil { 60 | t.Error(err) 61 | t.FailNow() 62 | } 63 | migrateSchema(t, c, o) 64 | return c 65 | } 66 | 67 | // NewClient calls ent.NewClient and auto-run migration. 68 | func NewClient(t TestingT, opts ...Option) *ent.Client { 69 | o := newOptions(opts) 70 | c := ent.NewClient(o.opts...) 71 | migrateSchema(t, c, o) 72 | return c 73 | } 74 | func migrateSchema(t TestingT, c *ent.Client, o *options) { 75 | tables, err := schema.CopyTables(migrate.Tables) 76 | if err != nil { 77 | t.Error(err) 78 | t.FailNow() 79 | } 80 | if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil { 81 | t.Error(err) 82 | t.FailNow() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ent/generate.go: -------------------------------------------------------------------------------- 1 | package ent 2 | 3 | //go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema 4 | //go:generate go run -mod=mod entgo.io/contrib/entproto/cmd/entproto -path ./schema 5 | -------------------------------------------------------------------------------- /ent/hook/hook.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package hook 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/rotemtam/ent-grpc-example/ent" 10 | ) 11 | 12 | // The CategoryFunc type is an adapter to allow the use of ordinary 13 | // function as Category mutator. 14 | type CategoryFunc func(context.Context, *ent.CategoryMutation) (ent.Value, error) 15 | 16 | // Mutate calls f(ctx, m). 17 | func (f CategoryFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { 18 | if mv, ok := m.(*ent.CategoryMutation); ok { 19 | return f(ctx, mv) 20 | } 21 | return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CategoryMutation", m) 22 | } 23 | 24 | // The UserFunc type is an adapter to allow the use of ordinary 25 | // function as User mutator. 26 | type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error) 27 | 28 | // Mutate calls f(ctx, m). 29 | func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { 30 | if mv, ok := m.(*ent.UserMutation); ok { 31 | return f(ctx, mv) 32 | } 33 | return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m) 34 | } 35 | 36 | // Condition is a hook condition function. 37 | type Condition func(context.Context, ent.Mutation) bool 38 | 39 | // And groups conditions with the AND operator. 40 | func And(first, second Condition, rest ...Condition) Condition { 41 | return func(ctx context.Context, m ent.Mutation) bool { 42 | if !first(ctx, m) || !second(ctx, m) { 43 | return false 44 | } 45 | for _, cond := range rest { 46 | if !cond(ctx, m) { 47 | return false 48 | } 49 | } 50 | return true 51 | } 52 | } 53 | 54 | // Or groups conditions with the OR operator. 55 | func Or(first, second Condition, rest ...Condition) Condition { 56 | return func(ctx context.Context, m ent.Mutation) bool { 57 | if first(ctx, m) || second(ctx, m) { 58 | return true 59 | } 60 | for _, cond := range rest { 61 | if cond(ctx, m) { 62 | return true 63 | } 64 | } 65 | return false 66 | } 67 | } 68 | 69 | // Not negates a given condition. 70 | func Not(cond Condition) Condition { 71 | return func(ctx context.Context, m ent.Mutation) bool { 72 | return !cond(ctx, m) 73 | } 74 | } 75 | 76 | // HasOp is a condition testing mutation operation. 77 | func HasOp(op ent.Op) Condition { 78 | return func(_ context.Context, m ent.Mutation) bool { 79 | return m.Op().Is(op) 80 | } 81 | } 82 | 83 | // HasAddedFields is a condition validating `.AddedField` on fields. 84 | func HasAddedFields(field string, fields ...string) Condition { 85 | return func(_ context.Context, m ent.Mutation) bool { 86 | if _, exists := m.AddedField(field); !exists { 87 | return false 88 | } 89 | for _, field := range fields { 90 | if _, exists := m.AddedField(field); !exists { 91 | return false 92 | } 93 | } 94 | return true 95 | } 96 | } 97 | 98 | // HasClearedFields is a condition validating `.FieldCleared` on fields. 99 | func HasClearedFields(field string, fields ...string) Condition { 100 | return func(_ context.Context, m ent.Mutation) bool { 101 | if exists := m.FieldCleared(field); !exists { 102 | return false 103 | } 104 | for _, field := range fields { 105 | if exists := m.FieldCleared(field); !exists { 106 | return false 107 | } 108 | } 109 | return true 110 | } 111 | } 112 | 113 | // HasFields is a condition validating `.Field` on fields. 114 | func HasFields(field string, fields ...string) Condition { 115 | return func(_ context.Context, m ent.Mutation) bool { 116 | if _, exists := m.Field(field); !exists { 117 | return false 118 | } 119 | for _, field := range fields { 120 | if _, exists := m.Field(field); !exists { 121 | return false 122 | } 123 | } 124 | return true 125 | } 126 | } 127 | 128 | // If executes the given hook under condition. 129 | // 130 | // hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...))) 131 | func If(hk ent.Hook, cond Condition) ent.Hook { 132 | return func(next ent.Mutator) ent.Mutator { 133 | return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) { 134 | if cond(ctx, m) { 135 | return hk(next).Mutate(ctx, m) 136 | } 137 | return next.Mutate(ctx, m) 138 | }) 139 | } 140 | } 141 | 142 | // On executes the given hook only for the given operation. 143 | // 144 | // hook.On(Log, ent.Delete|ent.Create) 145 | func On(hk ent.Hook, op ent.Op) ent.Hook { 146 | return If(hk, HasOp(op)) 147 | } 148 | 149 | // Unless skips the given hook only for the given operation. 150 | // 151 | // hook.Unless(Log, ent.Update|ent.UpdateOne) 152 | func Unless(hk ent.Hook, op ent.Op) ent.Hook { 153 | return If(hk, Not(HasOp(op))) 154 | } 155 | 156 | // FixedError is a hook returning a fixed error. 157 | func FixedError(err error) ent.Hook { 158 | return func(ent.Mutator) ent.Mutator { 159 | return ent.MutateFunc(func(context.Context, ent.Mutation) (ent.Value, error) { 160 | return nil, err 161 | }) 162 | } 163 | } 164 | 165 | // Reject returns a hook that rejects all operations that match op. 166 | // 167 | // func (T) Hooks() []ent.Hook { 168 | // return []ent.Hook{ 169 | // Reject(ent.Delete|ent.Update), 170 | // } 171 | // } 172 | func Reject(op ent.Op) ent.Hook { 173 | hk := FixedError(fmt.Errorf("%s operation is not allowed", op)) 174 | return On(hk, op) 175 | } 176 | 177 | // Chain acts as a list of hooks and is effectively immutable. 178 | // Once created, it will always hold the same set of hooks in the same order. 179 | type Chain struct { 180 | hooks []ent.Hook 181 | } 182 | 183 | // NewChain creates a new chain of hooks. 184 | func NewChain(hooks ...ent.Hook) Chain { 185 | return Chain{append([]ent.Hook(nil), hooks...)} 186 | } 187 | 188 | // Hook chains the list of hooks and returns the final hook. 189 | func (c Chain) Hook() ent.Hook { 190 | return func(mutator ent.Mutator) ent.Mutator { 191 | for i := len(c.hooks) - 1; i >= 0; i-- { 192 | mutator = c.hooks[i](mutator) 193 | } 194 | return mutator 195 | } 196 | } 197 | 198 | // Append extends a chain, adding the specified hook 199 | // as the last ones in the mutation flow. 200 | func (c Chain) Append(hooks ...ent.Hook) Chain { 201 | newHooks := make([]ent.Hook, 0, len(c.hooks)+len(hooks)) 202 | newHooks = append(newHooks, c.hooks...) 203 | newHooks = append(newHooks, hooks...) 204 | return Chain{newHooks} 205 | } 206 | 207 | // Extend extends a chain, adding the specified chain 208 | // as the last ones in the mutation flow. 209 | func (c Chain) Extend(chain Chain) Chain { 210 | return c.Append(chain.hooks...) 211 | } 212 | -------------------------------------------------------------------------------- /ent/migrate/migrate.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package migrate 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "io" 9 | 10 | "entgo.io/ent/dialect" 11 | "entgo.io/ent/dialect/sql/schema" 12 | ) 13 | 14 | var ( 15 | // WithGlobalUniqueID sets the universal ids options to the migration. 16 | // If this option is enabled, ent migration will allocate a 1<<32 range 17 | // for the ids of each entity (table). 18 | // Note that this option cannot be applied on tables that already exist. 19 | WithGlobalUniqueID = schema.WithGlobalUniqueID 20 | // WithDropColumn sets the drop column option to the migration. 21 | // If this option is enabled, ent migration will drop old columns 22 | // that were used for both fields and edges. This defaults to false. 23 | WithDropColumn = schema.WithDropColumn 24 | // WithDropIndex sets the drop index option to the migration. 25 | // If this option is enabled, ent migration will drop old indexes 26 | // that were defined in the schema. This defaults to false. 27 | // Note that unique constraints are defined using `UNIQUE INDEX`, 28 | // and therefore, it's recommended to enable this option to get more 29 | // flexibility in the schema changes. 30 | WithDropIndex = schema.WithDropIndex 31 | // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true. 32 | WithForeignKeys = schema.WithForeignKeys 33 | ) 34 | 35 | // Schema is the API for creating, migrating and dropping a schema. 36 | type Schema struct { 37 | drv dialect.Driver 38 | } 39 | 40 | // NewSchema creates a new schema client. 41 | func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} } 42 | 43 | // Create creates all schema resources. 44 | func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { 45 | return Create(ctx, s, Tables, opts...) 46 | } 47 | 48 | // Create creates all table resources using the given schema driver. 49 | func Create(ctx context.Context, s *Schema, tables []*schema.Table, opts ...schema.MigrateOption) error { 50 | migrate, err := schema.NewMigrate(s.drv, opts...) 51 | if err != nil { 52 | return fmt.Errorf("ent/migrate: %w", err) 53 | } 54 | return migrate.Create(ctx, tables...) 55 | } 56 | 57 | // WriteTo writes the schema changes to w instead of running them against the database. 58 | // 59 | // if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil { 60 | // log.Fatal(err) 61 | // } 62 | func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error { 63 | return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv}}, Tables, opts...) 64 | } 65 | -------------------------------------------------------------------------------- /ent/migrate/schema.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package migrate 4 | 5 | import ( 6 | "entgo.io/ent/dialect/sql/schema" 7 | "entgo.io/ent/schema/field" 8 | ) 9 | 10 | var ( 11 | // CategoriesColumns holds the columns for the "categories" table. 12 | CategoriesColumns = []*schema.Column{ 13 | {Name: "id", Type: field.TypeInt, Increment: true}, 14 | {Name: "name", Type: field.TypeString}, 15 | {Name: "category_admin", Type: field.TypeInt, Nullable: true}, 16 | } 17 | // CategoriesTable holds the schema information for the "categories" table. 18 | CategoriesTable = &schema.Table{ 19 | Name: "categories", 20 | Columns: CategoriesColumns, 21 | PrimaryKey: []*schema.Column{CategoriesColumns[0]}, 22 | ForeignKeys: []*schema.ForeignKey{ 23 | { 24 | Symbol: "categories_users_admin", 25 | Columns: []*schema.Column{CategoriesColumns[2]}, 26 | RefColumns: []*schema.Column{UsersColumns[0]}, 27 | OnDelete: schema.SetNull, 28 | }, 29 | }, 30 | } 31 | // UsersColumns holds the columns for the "users" table. 32 | UsersColumns = []*schema.Column{ 33 | {Name: "id", Type: field.TypeInt, Increment: true}, 34 | {Name: "name", Type: field.TypeString, Unique: true}, 35 | {Name: "email_address", Type: field.TypeString, Unique: true}, 36 | {Name: "alias", Type: field.TypeString, Nullable: true}, 37 | } 38 | // UsersTable holds the schema information for the "users" table. 39 | UsersTable = &schema.Table{ 40 | Name: "users", 41 | Columns: UsersColumns, 42 | PrimaryKey: []*schema.Column{UsersColumns[0]}, 43 | } 44 | // Tables holds all the tables in the schema. 45 | Tables = []*schema.Table{ 46 | CategoriesTable, 47 | UsersTable, 48 | } 49 | ) 50 | 51 | func init() { 52 | CategoriesTable.ForeignKeys[0].RefTable = UsersTable 53 | } 54 | -------------------------------------------------------------------------------- /ent/predicate/predicate.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package predicate 4 | 5 | import ( 6 | "entgo.io/ent/dialect/sql" 7 | ) 8 | 9 | // Category is the predicate function for category builders. 10 | type Category func(*sql.Selector) 11 | 12 | // User is the predicate function for user builders. 13 | type User func(*sql.Selector) 14 | -------------------------------------------------------------------------------- /ent/proto/entpb/entpb.proto: -------------------------------------------------------------------------------- 1 | // Code generated by entproto. DO NOT EDIT. 2 | syntax = "proto3"; 3 | 4 | package entpb; 5 | 6 | import "google/protobuf/empty.proto"; 7 | 8 | import "google/protobuf/wrappers.proto"; 9 | 10 | option go_package = "github.com/rotemtam/ent-grpc-example/ent/proto/entpb"; 11 | 12 | message Category { 13 | int64 id = 1; 14 | 15 | string name = 2; 16 | 17 | User admin = 3; 18 | } 19 | 20 | message User { 21 | int64 id = 1; 22 | 23 | string name = 2; 24 | 25 | string email_address = 3; 26 | 27 | google.protobuf.StringValue alias = 4; 28 | 29 | repeated Category administered = 5; 30 | } 31 | 32 | message CreateUserRequest { 33 | User user = 1; 34 | } 35 | 36 | message GetUserRequest { 37 | int64 id = 1; 38 | 39 | View view = 2; 40 | 41 | enum View { 42 | VIEW_UNSPECIFIED = 0; 43 | 44 | BASIC = 1; 45 | 46 | WITH_EDGE_IDS = 2; 47 | } 48 | } 49 | 50 | message UpdateUserRequest { 51 | User user = 1; 52 | } 53 | 54 | message DeleteUserRequest { 55 | int64 id = 1; 56 | } 57 | 58 | message ListUserRequest { 59 | int32 page_size = 1; 60 | 61 | string page_token = 2; 62 | 63 | View view = 3; 64 | 65 | enum View { 66 | VIEW_UNSPECIFIED = 0; 67 | 68 | BASIC = 1; 69 | 70 | WITH_EDGE_IDS = 2; 71 | } 72 | } 73 | 74 | message ListUserResponse { 75 | repeated User user_list = 1; 76 | 77 | string next_page_token = 2; 78 | } 79 | 80 | message BatchCreateUsersRequest { 81 | repeated CreateUserRequest requests = 1; 82 | } 83 | 84 | message BatchCreateUsersResponse { 85 | repeated User users = 1; 86 | } 87 | 88 | service UserService { 89 | rpc Create ( CreateUserRequest ) returns ( User ); 90 | 91 | rpc Get ( GetUserRequest ) returns ( User ); 92 | 93 | rpc Update ( UpdateUserRequest ) returns ( User ); 94 | 95 | rpc Delete ( DeleteUserRequest ) returns ( google.protobuf.Empty ); 96 | 97 | rpc List ( ListUserRequest ) returns ( ListUserResponse ); 98 | 99 | rpc BatchCreate ( BatchCreateUsersRequest ) returns ( BatchCreateUsersResponse ); 100 | } 101 | -------------------------------------------------------------------------------- /ent/proto/entpb/entpb_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v3.19.4 5 | // source: entpb/entpb.proto 6 | 7 | package entpb 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | emptypb "google.golang.org/protobuf/types/known/emptypb" 15 | ) 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the grpc package it is being compiled against. 19 | // Requires gRPC-Go v1.32.0 or later. 20 | const _ = grpc.SupportPackageIsVersion7 21 | 22 | // UserServiceClient is the client API for UserService service. 23 | // 24 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 25 | type UserServiceClient interface { 26 | Create(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*User, error) 27 | Get(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error) 28 | Update(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error) 29 | Delete(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) 30 | List(ctx context.Context, in *ListUserRequest, opts ...grpc.CallOption) (*ListUserResponse, error) 31 | BatchCreate(ctx context.Context, in *BatchCreateUsersRequest, opts ...grpc.CallOption) (*BatchCreateUsersResponse, error) 32 | } 33 | 34 | type userServiceClient struct { 35 | cc grpc.ClientConnInterface 36 | } 37 | 38 | func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { 39 | return &userServiceClient{cc} 40 | } 41 | 42 | func (c *userServiceClient) Create(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*User, error) { 43 | out := new(User) 44 | err := c.cc.Invoke(ctx, "/entpb.UserService/Create", in, out, opts...) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return out, nil 49 | } 50 | 51 | func (c *userServiceClient) Get(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error) { 52 | out := new(User) 53 | err := c.cc.Invoke(ctx, "/entpb.UserService/Get", in, out, opts...) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return out, nil 58 | } 59 | 60 | func (c *userServiceClient) Update(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error) { 61 | out := new(User) 62 | err := c.cc.Invoke(ctx, "/entpb.UserService/Update", in, out, opts...) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return out, nil 67 | } 68 | 69 | func (c *userServiceClient) Delete(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { 70 | out := new(emptypb.Empty) 71 | err := c.cc.Invoke(ctx, "/entpb.UserService/Delete", in, out, opts...) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return out, nil 76 | } 77 | 78 | func (c *userServiceClient) List(ctx context.Context, in *ListUserRequest, opts ...grpc.CallOption) (*ListUserResponse, error) { 79 | out := new(ListUserResponse) 80 | err := c.cc.Invoke(ctx, "/entpb.UserService/List", in, out, opts...) 81 | if err != nil { 82 | return nil, err 83 | } 84 | return out, nil 85 | } 86 | 87 | func (c *userServiceClient) BatchCreate(ctx context.Context, in *BatchCreateUsersRequest, opts ...grpc.CallOption) (*BatchCreateUsersResponse, error) { 88 | out := new(BatchCreateUsersResponse) 89 | err := c.cc.Invoke(ctx, "/entpb.UserService/BatchCreate", in, out, opts...) 90 | if err != nil { 91 | return nil, err 92 | } 93 | return out, nil 94 | } 95 | 96 | // UserServiceServer is the server API for UserService service. 97 | // All implementations must embed UnimplementedUserServiceServer 98 | // for forward compatibility 99 | type UserServiceServer interface { 100 | Create(context.Context, *CreateUserRequest) (*User, error) 101 | Get(context.Context, *GetUserRequest) (*User, error) 102 | Update(context.Context, *UpdateUserRequest) (*User, error) 103 | Delete(context.Context, *DeleteUserRequest) (*emptypb.Empty, error) 104 | List(context.Context, *ListUserRequest) (*ListUserResponse, error) 105 | BatchCreate(context.Context, *BatchCreateUsersRequest) (*BatchCreateUsersResponse, error) 106 | mustEmbedUnimplementedUserServiceServer() 107 | } 108 | 109 | // UnimplementedUserServiceServer must be embedded to have forward compatible implementations. 110 | type UnimplementedUserServiceServer struct { 111 | } 112 | 113 | func (UnimplementedUserServiceServer) Create(context.Context, *CreateUserRequest) (*User, error) { 114 | return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") 115 | } 116 | func (UnimplementedUserServiceServer) Get(context.Context, *GetUserRequest) (*User, error) { 117 | return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") 118 | } 119 | func (UnimplementedUserServiceServer) Update(context.Context, *UpdateUserRequest) (*User, error) { 120 | return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") 121 | } 122 | func (UnimplementedUserServiceServer) Delete(context.Context, *DeleteUserRequest) (*emptypb.Empty, error) { 123 | return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") 124 | } 125 | func (UnimplementedUserServiceServer) List(context.Context, *ListUserRequest) (*ListUserResponse, error) { 126 | return nil, status.Errorf(codes.Unimplemented, "method List not implemented") 127 | } 128 | func (UnimplementedUserServiceServer) BatchCreate(context.Context, *BatchCreateUsersRequest) (*BatchCreateUsersResponse, error) { 129 | return nil, status.Errorf(codes.Unimplemented, "method BatchCreate not implemented") 130 | } 131 | func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} 132 | 133 | // UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service. 134 | // Use of this interface is not recommended, as added methods to UserServiceServer will 135 | // result in compilation errors. 136 | type UnsafeUserServiceServer interface { 137 | mustEmbedUnimplementedUserServiceServer() 138 | } 139 | 140 | func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) { 141 | s.RegisterService(&UserService_ServiceDesc, srv) 142 | } 143 | 144 | func _UserService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 145 | in := new(CreateUserRequest) 146 | if err := dec(in); err != nil { 147 | return nil, err 148 | } 149 | if interceptor == nil { 150 | return srv.(UserServiceServer).Create(ctx, in) 151 | } 152 | info := &grpc.UnaryServerInfo{ 153 | Server: srv, 154 | FullMethod: "/entpb.UserService/Create", 155 | } 156 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 157 | return srv.(UserServiceServer).Create(ctx, req.(*CreateUserRequest)) 158 | } 159 | return interceptor(ctx, in, info, handler) 160 | } 161 | 162 | func _UserService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 163 | in := new(GetUserRequest) 164 | if err := dec(in); err != nil { 165 | return nil, err 166 | } 167 | if interceptor == nil { 168 | return srv.(UserServiceServer).Get(ctx, in) 169 | } 170 | info := &grpc.UnaryServerInfo{ 171 | Server: srv, 172 | FullMethod: "/entpb.UserService/Get", 173 | } 174 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 175 | return srv.(UserServiceServer).Get(ctx, req.(*GetUserRequest)) 176 | } 177 | return interceptor(ctx, in, info, handler) 178 | } 179 | 180 | func _UserService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 181 | in := new(UpdateUserRequest) 182 | if err := dec(in); err != nil { 183 | return nil, err 184 | } 185 | if interceptor == nil { 186 | return srv.(UserServiceServer).Update(ctx, in) 187 | } 188 | info := &grpc.UnaryServerInfo{ 189 | Server: srv, 190 | FullMethod: "/entpb.UserService/Update", 191 | } 192 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 193 | return srv.(UserServiceServer).Update(ctx, req.(*UpdateUserRequest)) 194 | } 195 | return interceptor(ctx, in, info, handler) 196 | } 197 | 198 | func _UserService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 199 | in := new(DeleteUserRequest) 200 | if err := dec(in); err != nil { 201 | return nil, err 202 | } 203 | if interceptor == nil { 204 | return srv.(UserServiceServer).Delete(ctx, in) 205 | } 206 | info := &grpc.UnaryServerInfo{ 207 | Server: srv, 208 | FullMethod: "/entpb.UserService/Delete", 209 | } 210 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 211 | return srv.(UserServiceServer).Delete(ctx, req.(*DeleteUserRequest)) 212 | } 213 | return interceptor(ctx, in, info, handler) 214 | } 215 | 216 | func _UserService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 217 | in := new(ListUserRequest) 218 | if err := dec(in); err != nil { 219 | return nil, err 220 | } 221 | if interceptor == nil { 222 | return srv.(UserServiceServer).List(ctx, in) 223 | } 224 | info := &grpc.UnaryServerInfo{ 225 | Server: srv, 226 | FullMethod: "/entpb.UserService/List", 227 | } 228 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 229 | return srv.(UserServiceServer).List(ctx, req.(*ListUserRequest)) 230 | } 231 | return interceptor(ctx, in, info, handler) 232 | } 233 | 234 | func _UserService_BatchCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 235 | in := new(BatchCreateUsersRequest) 236 | if err := dec(in); err != nil { 237 | return nil, err 238 | } 239 | if interceptor == nil { 240 | return srv.(UserServiceServer).BatchCreate(ctx, in) 241 | } 242 | info := &grpc.UnaryServerInfo{ 243 | Server: srv, 244 | FullMethod: "/entpb.UserService/BatchCreate", 245 | } 246 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 247 | return srv.(UserServiceServer).BatchCreate(ctx, req.(*BatchCreateUsersRequest)) 248 | } 249 | return interceptor(ctx, in, info, handler) 250 | } 251 | 252 | // UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. 253 | // It's only intended for direct use with grpc.RegisterService, 254 | // and not to be introspected or modified (even as a copy) 255 | var UserService_ServiceDesc = grpc.ServiceDesc{ 256 | ServiceName: "entpb.UserService", 257 | HandlerType: (*UserServiceServer)(nil), 258 | Methods: []grpc.MethodDesc{ 259 | { 260 | MethodName: "Create", 261 | Handler: _UserService_Create_Handler, 262 | }, 263 | { 264 | MethodName: "Get", 265 | Handler: _UserService_Get_Handler, 266 | }, 267 | { 268 | MethodName: "Update", 269 | Handler: _UserService_Update_Handler, 270 | }, 271 | { 272 | MethodName: "Delete", 273 | Handler: _UserService_Delete_Handler, 274 | }, 275 | { 276 | MethodName: "List", 277 | Handler: _UserService_List_Handler, 278 | }, 279 | { 280 | MethodName: "BatchCreate", 281 | Handler: _UserService_BatchCreate_Handler, 282 | }, 283 | }, 284 | Streams: []grpc.StreamDesc{}, 285 | Metadata: "entpb/entpb.proto", 286 | } 287 | -------------------------------------------------------------------------------- /ent/proto/entpb/entpb_user_service.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-entgrpc. DO NOT EDIT. 2 | package entpb 3 | 4 | import ( 5 | context "context" 6 | base64 "encoding/base64" 7 | entproto "entgo.io/contrib/entproto" 8 | sqlgraph "entgo.io/ent/dialect/sql/sqlgraph" 9 | fmt "fmt" 10 | ent "github.com/rotemtam/ent-grpc-example/ent" 11 | category "github.com/rotemtam/ent-grpc-example/ent/category" 12 | user "github.com/rotemtam/ent-grpc-example/ent/user" 13 | codes "google.golang.org/grpc/codes" 14 | status "google.golang.org/grpc/status" 15 | emptypb "google.golang.org/protobuf/types/known/emptypb" 16 | wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" 17 | strconv "strconv" 18 | ) 19 | 20 | // UserService implements UserServiceServer 21 | type UserService struct { 22 | client *ent.Client 23 | UnimplementedUserServiceServer 24 | } 25 | 26 | // NewUserService returns a new UserService 27 | func NewUserService(client *ent.Client) *UserService { 28 | return &UserService{ 29 | client: client, 30 | } 31 | } 32 | 33 | // toProtoUser transforms the ent type to the pb type 34 | func toProtoUser(e *ent.User) (*User, error) { 35 | v := &User{} 36 | alias := wrapperspb.String(e.Alias) 37 | v.Alias = alias 38 | email_address := e.EmailAddress 39 | v.EmailAddress = email_address 40 | id := int64(e.ID) 41 | v.Id = id 42 | name := e.Name 43 | v.Name = name 44 | for _, edg := range e.Edges.Administered { 45 | id := int64(edg.ID) 46 | v.Administered = append(v.Administered, &Category{ 47 | Id: id, 48 | }) 49 | } 50 | return v, nil 51 | } 52 | 53 | // toProtoUserList transforms a list of ent type to a list of pb type 54 | func toProtoUserList(e []*ent.User) ([]*User, error) { 55 | var pbList []*User 56 | for _, entEntity := range e { 57 | pbEntity, err := toProtoUser(entEntity) 58 | if err != nil { 59 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 60 | } 61 | pbList = append(pbList, pbEntity) 62 | } 63 | return pbList, nil 64 | } 65 | 66 | // Create implements UserServiceServer.Create 67 | func (svc *UserService) Create(ctx context.Context, req *CreateUserRequest) (*User, error) { 68 | user := req.GetUser() 69 | m, err := svc.createBuilder(user) 70 | if err != nil { 71 | return nil, err 72 | } 73 | res, err := m.Save(ctx) 74 | switch { 75 | case err == nil: 76 | proto, err := toProtoUser(res) 77 | if err != nil { 78 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 79 | } 80 | return proto, nil 81 | case sqlgraph.IsUniqueConstraintError(err): 82 | return nil, status.Errorf(codes.AlreadyExists, "already exists: %s", err) 83 | case ent.IsConstraintError(err): 84 | return nil, status.Errorf(codes.InvalidArgument, "invalid argument: %s", err) 85 | default: 86 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 87 | } 88 | 89 | } 90 | 91 | // Get implements UserServiceServer.Get 92 | func (svc *UserService) Get(ctx context.Context, req *GetUserRequest) (*User, error) { 93 | var ( 94 | err error 95 | get *ent.User 96 | ) 97 | id := int(req.GetId()) 98 | switch req.GetView() { 99 | case GetUserRequest_VIEW_UNSPECIFIED, GetUserRequest_BASIC: 100 | get, err = svc.client.User.Get(ctx, id) 101 | case GetUserRequest_WITH_EDGE_IDS: 102 | get, err = svc.client.User.Query(). 103 | Where(user.ID(id)). 104 | WithAdministered(func(query *ent.CategoryQuery) { 105 | query.Select(category.FieldID) 106 | }). 107 | Only(ctx) 108 | default: 109 | return nil, status.Error(codes.InvalidArgument, "invalid argument: unknown view") 110 | } 111 | switch { 112 | case err == nil: 113 | return toProtoUser(get) 114 | case ent.IsNotFound(err): 115 | return nil, status.Errorf(codes.NotFound, "not found: %s", err) 116 | default: 117 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 118 | } 119 | 120 | } 121 | 122 | // Update implements UserServiceServer.Update 123 | func (svc *UserService) Update(ctx context.Context, req *UpdateUserRequest) (*User, error) { 124 | user := req.GetUser() 125 | userID := int(user.GetId()) 126 | m := svc.client.User.UpdateOneID(userID) 127 | if user.GetAlias() != nil { 128 | userAlias := user.GetAlias().GetValue() 129 | m.SetAlias(userAlias) 130 | } 131 | userEmailAddress := user.GetEmailAddress() 132 | m.SetEmailAddress(userEmailAddress) 133 | userName := user.GetName() 134 | m.SetName(userName) 135 | for _, item := range user.GetAdministered() { 136 | administered := int(item.GetId()) 137 | m.AddAdministeredIDs(administered) 138 | } 139 | 140 | res, err := m.Save(ctx) 141 | switch { 142 | case err == nil: 143 | proto, err := toProtoUser(res) 144 | if err != nil { 145 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 146 | } 147 | return proto, nil 148 | case sqlgraph.IsUniqueConstraintError(err): 149 | return nil, status.Errorf(codes.AlreadyExists, "already exists: %s", err) 150 | case ent.IsConstraintError(err): 151 | return nil, status.Errorf(codes.InvalidArgument, "invalid argument: %s", err) 152 | default: 153 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 154 | } 155 | 156 | } 157 | 158 | // Delete implements UserServiceServer.Delete 159 | func (svc *UserService) Delete(ctx context.Context, req *DeleteUserRequest) (*emptypb.Empty, error) { 160 | var err error 161 | id := int(req.GetId()) 162 | err = svc.client.User.DeleteOneID(id).Exec(ctx) 163 | switch { 164 | case err == nil: 165 | return &emptypb.Empty{}, nil 166 | case ent.IsNotFound(err): 167 | return nil, status.Errorf(codes.NotFound, "not found: %s", err) 168 | default: 169 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 170 | } 171 | 172 | } 173 | 174 | // List implements UserServiceServer.List 175 | func (svc *UserService) List(ctx context.Context, req *ListUserRequest) (*ListUserResponse, error) { 176 | var ( 177 | err error 178 | entList []*ent.User 179 | pageSize int 180 | ) 181 | pageSize = int(req.GetPageSize()) 182 | switch { 183 | case pageSize < 0: 184 | return nil, status.Errorf(codes.InvalidArgument, "page size cannot be less than zero") 185 | case pageSize == 0 || pageSize > entproto.MaxPageSize: 186 | pageSize = entproto.MaxPageSize 187 | } 188 | listQuery := svc.client.User.Query(). 189 | Order(ent.Desc(user.FieldID)). 190 | Limit(pageSize + 1) 191 | if req.GetPageToken() != "" { 192 | bytes, err := base64.StdEncoding.DecodeString(req.PageToken) 193 | if err != nil { 194 | return nil, status.Errorf(codes.InvalidArgument, "page token is invalid") 195 | } 196 | token, err := strconv.ParseInt(string(bytes), 10, 32) 197 | if err != nil { 198 | return nil, status.Errorf(codes.InvalidArgument, "page token is invalid") 199 | } 200 | pageToken := int(token) 201 | listQuery = listQuery. 202 | Where(user.IDLTE(pageToken)) 203 | } 204 | switch req.GetView() { 205 | case ListUserRequest_VIEW_UNSPECIFIED, ListUserRequest_BASIC: 206 | entList, err = listQuery.All(ctx) 207 | case ListUserRequest_WITH_EDGE_IDS: 208 | entList, err = listQuery. 209 | WithAdministered(func(query *ent.CategoryQuery) { 210 | query.Select(category.FieldID) 211 | }). 212 | All(ctx) 213 | } 214 | switch { 215 | case err == nil: 216 | var nextPageToken string 217 | if len(entList) == pageSize+1 { 218 | nextPageToken = base64.StdEncoding.EncodeToString( 219 | []byte(fmt.Sprintf("%v", entList[len(entList)-1].ID))) 220 | entList = entList[:len(entList)-1] 221 | } 222 | protoList, err := toProtoUserList(entList) 223 | if err != nil { 224 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 225 | } 226 | return &ListUserResponse{ 227 | UserList: protoList, 228 | NextPageToken: nextPageToken, 229 | }, nil 230 | default: 231 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 232 | } 233 | 234 | } 235 | 236 | // BatchCreate implements UserServiceServer.BatchCreate 237 | func (svc *UserService) BatchCreate(ctx context.Context, req *BatchCreateUsersRequest) (*BatchCreateUsersResponse, error) { 238 | requests := req.GetRequests() 239 | if len(requests) > entproto.MaxBatchCreateSize { 240 | return nil, status.Errorf(codes.InvalidArgument, "batch size cannot be greater than %d", entproto.MaxBatchCreateSize) 241 | } 242 | bulk := make([]*ent.UserCreate, len(requests)) 243 | for i, req := range requests { 244 | user := req.GetUser() 245 | var err error 246 | bulk[i], err = svc.createBuilder(user) 247 | if err != nil { 248 | return nil, err 249 | } 250 | } 251 | res, err := svc.client.User.CreateBulk(bulk...).Save(ctx) 252 | switch { 253 | case err == nil: 254 | protoList, err := toProtoUserList(res) 255 | if err != nil { 256 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 257 | } 258 | return &BatchCreateUsersResponse{ 259 | Users: protoList, 260 | }, nil 261 | case sqlgraph.IsUniqueConstraintError(err): 262 | return nil, status.Errorf(codes.AlreadyExists, "already exists: %s", err) 263 | case ent.IsConstraintError(err): 264 | return nil, status.Errorf(codes.InvalidArgument, "invalid argument: %s", err) 265 | default: 266 | return nil, status.Errorf(codes.Internal, "internal error: %s", err) 267 | } 268 | 269 | } 270 | 271 | func (svc *UserService) createBuilder(user *User) (*ent.UserCreate, error) { 272 | m := svc.client.User.Create() 273 | if user.GetAlias() != nil { 274 | userAlias := user.GetAlias().GetValue() 275 | m.SetAlias(userAlias) 276 | } 277 | userEmailAddress := user.GetEmailAddress() 278 | m.SetEmailAddress(userEmailAddress) 279 | userName := user.GetName() 280 | m.SetName(userName) 281 | for _, item := range user.GetAdministered() { 282 | administered := int(item.GetId()) 283 | m.AddAdministeredIDs(administered) 284 | } 285 | return m, nil 286 | } 287 | -------------------------------------------------------------------------------- /ent/proto/entpb/generate.go: -------------------------------------------------------------------------------- 1 | package entpb 2 | 3 | //go:generate protoc -I=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --entgrpc_out=.. --entgrpc_opt=paths=source_relative,schema_path=../../schema entpb/entpb.proto 4 | 5 | -------------------------------------------------------------------------------- /ent/runtime.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | // The init function reads all schema descriptors with runtime code 6 | // (default values, validators, hooks and policies) and stitches it 7 | // to their package variables. 8 | func init() { 9 | } 10 | -------------------------------------------------------------------------------- /ent/runtime/runtime.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package runtime 4 | 5 | // The schema-stitching logic is generated in github.com/rotemtam/ent-grpc-example/ent/runtime.go 6 | 7 | const ( 8 | Version = "v0.11.6" // Version of ent codegen. 9 | Sum = "h1:fMQwhuzbPv12AXdrAGyHoOcgh9r0D9F8WEsCRoUWxVc=" // Sum of ent codegen. 10 | ) 11 | -------------------------------------------------------------------------------- /ent/schema/category.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "entgo.io/contrib/entproto" 5 | "entgo.io/ent" 6 | "entgo.io/ent/schema" 7 | "entgo.io/ent/schema/edge" 8 | "entgo.io/ent/schema/field" 9 | ) 10 | 11 | type Category struct { 12 | ent.Schema 13 | } 14 | 15 | func (Category) Fields() []ent.Field { 16 | return []ent.Field{ 17 | field.String("name"). 18 | Annotations(entproto.Field(2)), 19 | } 20 | } 21 | 22 | func (Category) Annotations() []schema.Annotation { 23 | return []schema.Annotation{ 24 | entproto.Message(), 25 | } 26 | } 27 | 28 | func (Category) Edges() []ent.Edge { 29 | return []ent.Edge{ 30 | edge.To("admin", User.Type). 31 | Unique(). 32 | Annotations(entproto.Field(3)), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ent/schema/user.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "entgo.io/contrib/entproto" 5 | "entgo.io/ent" 6 | "entgo.io/ent/schema" 7 | "entgo.io/ent/schema/edge" 8 | "entgo.io/ent/schema/field" 9 | ) 10 | 11 | // User holds the schema definition for the User entity. 12 | type User struct { 13 | ent.Schema 14 | } 15 | 16 | // Fields of the User. 17 | func (User) Fields() []ent.Field { 18 | return []ent.Field{ 19 | field.String("name"). 20 | Unique(). 21 | Annotations( 22 | entproto.Field(2), 23 | ), 24 | field.String("email_address"). 25 | Unique(). 26 | Annotations( 27 | entproto.Field(3), 28 | ), 29 | field.String("alias"). 30 | Optional(). 31 | Annotations(entproto.Field(4)), 32 | } 33 | } 34 | 35 | func (User) Annotations() []schema.Annotation { 36 | return []schema.Annotation{ 37 | entproto.Message(), 38 | entproto.Service(), 39 | } 40 | } 41 | 42 | // Edges of the User. 43 | func (User) Edges() []ent.Edge { 44 | return []ent.Edge{ 45 | edge.From("administered", Category.Type). 46 | Ref("admin"). 47 | Annotations(entproto.Field(5)), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ent/tx.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "sync" 8 | 9 | "entgo.io/ent/dialect" 10 | ) 11 | 12 | // Tx is a transactional client that is created by calling Client.Tx(). 13 | type Tx struct { 14 | config 15 | // Category is the client for interacting with the Category builders. 16 | Category *CategoryClient 17 | // User is the client for interacting with the User builders. 18 | User *UserClient 19 | 20 | // lazily loaded. 21 | client *Client 22 | clientOnce sync.Once 23 | // ctx lives for the life of the transaction. It is 24 | // the same context used by the underlying connection. 25 | ctx context.Context 26 | } 27 | 28 | type ( 29 | // Committer is the interface that wraps the Commit method. 30 | Committer interface { 31 | Commit(context.Context, *Tx) error 32 | } 33 | 34 | // The CommitFunc type is an adapter to allow the use of ordinary 35 | // function as a Committer. If f is a function with the appropriate 36 | // signature, CommitFunc(f) is a Committer that calls f. 37 | CommitFunc func(context.Context, *Tx) error 38 | 39 | // CommitHook defines the "commit middleware". A function that gets a Committer 40 | // and returns a Committer. For example: 41 | // 42 | // hook := func(next ent.Committer) ent.Committer { 43 | // return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error { 44 | // // Do some stuff before. 45 | // if err := next.Commit(ctx, tx); err != nil { 46 | // return err 47 | // } 48 | // // Do some stuff after. 49 | // return nil 50 | // }) 51 | // } 52 | // 53 | CommitHook func(Committer) Committer 54 | ) 55 | 56 | // Commit calls f(ctx, m). 57 | func (f CommitFunc) Commit(ctx context.Context, tx *Tx) error { 58 | return f(ctx, tx) 59 | } 60 | 61 | // Commit commits the transaction. 62 | func (tx *Tx) Commit() error { 63 | txDriver := tx.config.driver.(*txDriver) 64 | var fn Committer = CommitFunc(func(context.Context, *Tx) error { 65 | return txDriver.tx.Commit() 66 | }) 67 | txDriver.mu.Lock() 68 | hooks := append([]CommitHook(nil), txDriver.onCommit...) 69 | txDriver.mu.Unlock() 70 | for i := len(hooks) - 1; i >= 0; i-- { 71 | fn = hooks[i](fn) 72 | } 73 | return fn.Commit(tx.ctx, tx) 74 | } 75 | 76 | // OnCommit adds a hook to call on commit. 77 | func (tx *Tx) OnCommit(f CommitHook) { 78 | txDriver := tx.config.driver.(*txDriver) 79 | txDriver.mu.Lock() 80 | txDriver.onCommit = append(txDriver.onCommit, f) 81 | txDriver.mu.Unlock() 82 | } 83 | 84 | type ( 85 | // Rollbacker is the interface that wraps the Rollback method. 86 | Rollbacker interface { 87 | Rollback(context.Context, *Tx) error 88 | } 89 | 90 | // The RollbackFunc type is an adapter to allow the use of ordinary 91 | // function as a Rollbacker. If f is a function with the appropriate 92 | // signature, RollbackFunc(f) is a Rollbacker that calls f. 93 | RollbackFunc func(context.Context, *Tx) error 94 | 95 | // RollbackHook defines the "rollback middleware". A function that gets a Rollbacker 96 | // and returns a Rollbacker. For example: 97 | // 98 | // hook := func(next ent.Rollbacker) ent.Rollbacker { 99 | // return ent.RollbackFunc(func(ctx context.Context, tx *ent.Tx) error { 100 | // // Do some stuff before. 101 | // if err := next.Rollback(ctx, tx); err != nil { 102 | // return err 103 | // } 104 | // // Do some stuff after. 105 | // return nil 106 | // }) 107 | // } 108 | // 109 | RollbackHook func(Rollbacker) Rollbacker 110 | ) 111 | 112 | // Rollback calls f(ctx, m). 113 | func (f RollbackFunc) Rollback(ctx context.Context, tx *Tx) error { 114 | return f(ctx, tx) 115 | } 116 | 117 | // Rollback rollbacks the transaction. 118 | func (tx *Tx) Rollback() error { 119 | txDriver := tx.config.driver.(*txDriver) 120 | var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error { 121 | return txDriver.tx.Rollback() 122 | }) 123 | txDriver.mu.Lock() 124 | hooks := append([]RollbackHook(nil), txDriver.onRollback...) 125 | txDriver.mu.Unlock() 126 | for i := len(hooks) - 1; i >= 0; i-- { 127 | fn = hooks[i](fn) 128 | } 129 | return fn.Rollback(tx.ctx, tx) 130 | } 131 | 132 | // OnRollback adds a hook to call on rollback. 133 | func (tx *Tx) OnRollback(f RollbackHook) { 134 | txDriver := tx.config.driver.(*txDriver) 135 | txDriver.mu.Lock() 136 | txDriver.onRollback = append(txDriver.onRollback, f) 137 | txDriver.mu.Unlock() 138 | } 139 | 140 | // Client returns a Client that binds to current transaction. 141 | func (tx *Tx) Client() *Client { 142 | tx.clientOnce.Do(func() { 143 | tx.client = &Client{config: tx.config} 144 | tx.client.init() 145 | }) 146 | return tx.client 147 | } 148 | 149 | func (tx *Tx) init() { 150 | tx.Category = NewCategoryClient(tx.config) 151 | tx.User = NewUserClient(tx.config) 152 | } 153 | 154 | // txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. 155 | // The idea is to support transactions without adding any extra code to the builders. 156 | // When a builder calls to driver.Tx(), it gets the same dialect.Tx instance. 157 | // Commit and Rollback are nop for the internal builders and the user must call one 158 | // of them in order to commit or rollback the transaction. 159 | // 160 | // If a closed transaction is embedded in one of the generated entities, and the entity 161 | // applies a query, for example: Category.QueryXXX(), the query will be executed 162 | // through the driver which created this transaction. 163 | // 164 | // Note that txDriver is not goroutine safe. 165 | type txDriver struct { 166 | // the driver we started the transaction from. 167 | drv dialect.Driver 168 | // tx is the underlying transaction. 169 | tx dialect.Tx 170 | // completion hooks. 171 | mu sync.Mutex 172 | onCommit []CommitHook 173 | onRollback []RollbackHook 174 | } 175 | 176 | // newTx creates a new transactional driver. 177 | func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) { 178 | tx, err := drv.Tx(ctx) 179 | if err != nil { 180 | return nil, err 181 | } 182 | return &txDriver{tx: tx, drv: drv}, nil 183 | } 184 | 185 | // Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls 186 | // from the internal builders. Should be called only by the internal builders. 187 | func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil } 188 | 189 | // Dialect returns the dialect of the driver we started the transaction from. 190 | func (tx *txDriver) Dialect() string { return tx.drv.Dialect() } 191 | 192 | // Close is a nop close. 193 | func (*txDriver) Close() error { return nil } 194 | 195 | // Commit is a nop commit for the internal builders. 196 | // User must call `Tx.Commit` in order to commit the transaction. 197 | func (*txDriver) Commit() error { return nil } 198 | 199 | // Rollback is a nop rollback for the internal builders. 200 | // User must call `Tx.Rollback` in order to rollback the transaction. 201 | func (*txDriver) Rollback() error { return nil } 202 | 203 | // Exec calls tx.Exec. 204 | func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error { 205 | return tx.tx.Exec(ctx, query, args, v) 206 | } 207 | 208 | // Query calls tx.Query. 209 | func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error { 210 | return tx.tx.Query(ctx, query, args, v) 211 | } 212 | 213 | var _ dialect.Driver = (*txDriver)(nil) 214 | -------------------------------------------------------------------------------- /ent/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "entgo.io/ent/dialect/sql" 10 | "github.com/rotemtam/ent-grpc-example/ent/user" 11 | ) 12 | 13 | // User is the model entity for the User schema. 14 | type User struct { 15 | config `json:"-"` 16 | // ID of the ent. 17 | ID int `json:"id,omitempty"` 18 | // Name holds the value of the "name" field. 19 | Name string `json:"name,omitempty"` 20 | // EmailAddress holds the value of the "email_address" field. 21 | EmailAddress string `json:"email_address,omitempty"` 22 | // Alias holds the value of the "alias" field. 23 | Alias string `json:"alias,omitempty"` 24 | // Edges holds the relations/edges for other nodes in the graph. 25 | // The values are being populated by the UserQuery when eager-loading is set. 26 | Edges UserEdges `json:"edges"` 27 | } 28 | 29 | // UserEdges holds the relations/edges for other nodes in the graph. 30 | type UserEdges struct { 31 | // Administered holds the value of the administered edge. 32 | Administered []*Category `json:"administered,omitempty"` 33 | // loadedTypes holds the information for reporting if a 34 | // type was loaded (or requested) in eager-loading or not. 35 | loadedTypes [1]bool 36 | } 37 | 38 | // AdministeredOrErr returns the Administered value or an error if the edge 39 | // was not loaded in eager-loading. 40 | func (e UserEdges) AdministeredOrErr() ([]*Category, error) { 41 | if e.loadedTypes[0] { 42 | return e.Administered, nil 43 | } 44 | return nil, &NotLoadedError{edge: "administered"} 45 | } 46 | 47 | // scanValues returns the types for scanning values from sql.Rows. 48 | func (*User) scanValues(columns []string) ([]any, error) { 49 | values := make([]any, len(columns)) 50 | for i := range columns { 51 | switch columns[i] { 52 | case user.FieldID: 53 | values[i] = new(sql.NullInt64) 54 | case user.FieldName, user.FieldEmailAddress, user.FieldAlias: 55 | values[i] = new(sql.NullString) 56 | default: 57 | return nil, fmt.Errorf("unexpected column %q for type User", columns[i]) 58 | } 59 | } 60 | return values, nil 61 | } 62 | 63 | // assignValues assigns the values that were returned from sql.Rows (after scanning) 64 | // to the User fields. 65 | func (u *User) assignValues(columns []string, values []any) error { 66 | if m, n := len(values), len(columns); m < n { 67 | return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) 68 | } 69 | for i := range columns { 70 | switch columns[i] { 71 | case user.FieldID: 72 | value, ok := values[i].(*sql.NullInt64) 73 | if !ok { 74 | return fmt.Errorf("unexpected type %T for field id", value) 75 | } 76 | u.ID = int(value.Int64) 77 | case user.FieldName: 78 | if value, ok := values[i].(*sql.NullString); !ok { 79 | return fmt.Errorf("unexpected type %T for field name", values[i]) 80 | } else if value.Valid { 81 | u.Name = value.String 82 | } 83 | case user.FieldEmailAddress: 84 | if value, ok := values[i].(*sql.NullString); !ok { 85 | return fmt.Errorf("unexpected type %T for field email_address", values[i]) 86 | } else if value.Valid { 87 | u.EmailAddress = value.String 88 | } 89 | case user.FieldAlias: 90 | if value, ok := values[i].(*sql.NullString); !ok { 91 | return fmt.Errorf("unexpected type %T for field alias", values[i]) 92 | } else if value.Valid { 93 | u.Alias = value.String 94 | } 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | // QueryAdministered queries the "administered" edge of the User entity. 101 | func (u *User) QueryAdministered() *CategoryQuery { 102 | return NewUserClient(u.config).QueryAdministered(u) 103 | } 104 | 105 | // Update returns a builder for updating this User. 106 | // Note that you need to call User.Unwrap() before calling this method if this User 107 | // was returned from a transaction, and the transaction was committed or rolled back. 108 | func (u *User) Update() *UserUpdateOne { 109 | return NewUserClient(u.config).UpdateOne(u) 110 | } 111 | 112 | // Unwrap unwraps the User entity that was returned from a transaction after it was closed, 113 | // so that all future queries will be executed through the driver which created the transaction. 114 | func (u *User) Unwrap() *User { 115 | _tx, ok := u.config.driver.(*txDriver) 116 | if !ok { 117 | panic("ent: User is not a transactional entity") 118 | } 119 | u.config.driver = _tx.drv 120 | return u 121 | } 122 | 123 | // String implements the fmt.Stringer. 124 | func (u *User) String() string { 125 | var builder strings.Builder 126 | builder.WriteString("User(") 127 | builder.WriteString(fmt.Sprintf("id=%v, ", u.ID)) 128 | builder.WriteString("name=") 129 | builder.WriteString(u.Name) 130 | builder.WriteString(", ") 131 | builder.WriteString("email_address=") 132 | builder.WriteString(u.EmailAddress) 133 | builder.WriteString(", ") 134 | builder.WriteString("alias=") 135 | builder.WriteString(u.Alias) 136 | builder.WriteByte(')') 137 | return builder.String() 138 | } 139 | 140 | // Users is a parsable slice of User. 141 | type Users []*User 142 | 143 | func (u Users) config(cfg config) { 144 | for _i := range u { 145 | u[_i].config = cfg 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /ent/user/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package user 4 | 5 | const ( 6 | // Label holds the string label denoting the user type in the database. 7 | Label = "user" 8 | // FieldID holds the string denoting the id field in the database. 9 | FieldID = "id" 10 | // FieldName holds the string denoting the name field in the database. 11 | FieldName = "name" 12 | // FieldEmailAddress holds the string denoting the email_address field in the database. 13 | FieldEmailAddress = "email_address" 14 | // FieldAlias holds the string denoting the alias field in the database. 15 | FieldAlias = "alias" 16 | // EdgeAdministered holds the string denoting the administered edge name in mutations. 17 | EdgeAdministered = "administered" 18 | // Table holds the table name of the user in the database. 19 | Table = "users" 20 | // AdministeredTable is the table that holds the administered relation/edge. 21 | AdministeredTable = "categories" 22 | // AdministeredInverseTable is the table name for the Category entity. 23 | // It exists in this package in order to avoid circular dependency with the "category" package. 24 | AdministeredInverseTable = "categories" 25 | // AdministeredColumn is the table column denoting the administered relation/edge. 26 | AdministeredColumn = "category_admin" 27 | ) 28 | 29 | // Columns holds all SQL columns for user fields. 30 | var Columns = []string{ 31 | FieldID, 32 | FieldName, 33 | FieldEmailAddress, 34 | FieldAlias, 35 | } 36 | 37 | // ValidColumn reports if the column name is valid (part of the table columns). 38 | func ValidColumn(column string) bool { 39 | for i := range Columns { 40 | if column == Columns[i] { 41 | return true 42 | } 43 | } 44 | return false 45 | } 46 | -------------------------------------------------------------------------------- /ent/user/where.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package user 4 | 5 | import ( 6 | "entgo.io/ent/dialect/sql" 7 | "entgo.io/ent/dialect/sql/sqlgraph" 8 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 9 | ) 10 | 11 | // ID filters vertices based on their ID field. 12 | func ID(id int) predicate.User { 13 | return predicate.User(sql.FieldEQ(FieldID, id)) 14 | } 15 | 16 | // IDEQ applies the EQ predicate on the ID field. 17 | func IDEQ(id int) predicate.User { 18 | return predicate.User(sql.FieldEQ(FieldID, id)) 19 | } 20 | 21 | // IDNEQ applies the NEQ predicate on the ID field. 22 | func IDNEQ(id int) predicate.User { 23 | return predicate.User(sql.FieldNEQ(FieldID, id)) 24 | } 25 | 26 | // IDIn applies the In predicate on the ID field. 27 | func IDIn(ids ...int) predicate.User { 28 | return predicate.User(sql.FieldIn(FieldID, ids...)) 29 | } 30 | 31 | // IDNotIn applies the NotIn predicate on the ID field. 32 | func IDNotIn(ids ...int) predicate.User { 33 | return predicate.User(sql.FieldNotIn(FieldID, ids...)) 34 | } 35 | 36 | // IDGT applies the GT predicate on the ID field. 37 | func IDGT(id int) predicate.User { 38 | return predicate.User(sql.FieldGT(FieldID, id)) 39 | } 40 | 41 | // IDGTE applies the GTE predicate on the ID field. 42 | func IDGTE(id int) predicate.User { 43 | return predicate.User(sql.FieldGTE(FieldID, id)) 44 | } 45 | 46 | // IDLT applies the LT predicate on the ID field. 47 | func IDLT(id int) predicate.User { 48 | return predicate.User(sql.FieldLT(FieldID, id)) 49 | } 50 | 51 | // IDLTE applies the LTE predicate on the ID field. 52 | func IDLTE(id int) predicate.User { 53 | return predicate.User(sql.FieldLTE(FieldID, id)) 54 | } 55 | 56 | // Name applies equality check predicate on the "name" field. It's identical to NameEQ. 57 | func Name(v string) predicate.User { 58 | return predicate.User(sql.FieldEQ(FieldName, v)) 59 | } 60 | 61 | // EmailAddress applies equality check predicate on the "email_address" field. It's identical to EmailAddressEQ. 62 | func EmailAddress(v string) predicate.User { 63 | return predicate.User(sql.FieldEQ(FieldEmailAddress, v)) 64 | } 65 | 66 | // Alias applies equality check predicate on the "alias" field. It's identical to AliasEQ. 67 | func Alias(v string) predicate.User { 68 | return predicate.User(sql.FieldEQ(FieldAlias, v)) 69 | } 70 | 71 | // NameEQ applies the EQ predicate on the "name" field. 72 | func NameEQ(v string) predicate.User { 73 | return predicate.User(sql.FieldEQ(FieldName, v)) 74 | } 75 | 76 | // NameNEQ applies the NEQ predicate on the "name" field. 77 | func NameNEQ(v string) predicate.User { 78 | return predicate.User(sql.FieldNEQ(FieldName, v)) 79 | } 80 | 81 | // NameIn applies the In predicate on the "name" field. 82 | func NameIn(vs ...string) predicate.User { 83 | return predicate.User(sql.FieldIn(FieldName, vs...)) 84 | } 85 | 86 | // NameNotIn applies the NotIn predicate on the "name" field. 87 | func NameNotIn(vs ...string) predicate.User { 88 | return predicate.User(sql.FieldNotIn(FieldName, vs...)) 89 | } 90 | 91 | // NameGT applies the GT predicate on the "name" field. 92 | func NameGT(v string) predicate.User { 93 | return predicate.User(sql.FieldGT(FieldName, v)) 94 | } 95 | 96 | // NameGTE applies the GTE predicate on the "name" field. 97 | func NameGTE(v string) predicate.User { 98 | return predicate.User(sql.FieldGTE(FieldName, v)) 99 | } 100 | 101 | // NameLT applies the LT predicate on the "name" field. 102 | func NameLT(v string) predicate.User { 103 | return predicate.User(sql.FieldLT(FieldName, v)) 104 | } 105 | 106 | // NameLTE applies the LTE predicate on the "name" field. 107 | func NameLTE(v string) predicate.User { 108 | return predicate.User(sql.FieldLTE(FieldName, v)) 109 | } 110 | 111 | // NameContains applies the Contains predicate on the "name" field. 112 | func NameContains(v string) predicate.User { 113 | return predicate.User(sql.FieldContains(FieldName, v)) 114 | } 115 | 116 | // NameHasPrefix applies the HasPrefix predicate on the "name" field. 117 | func NameHasPrefix(v string) predicate.User { 118 | return predicate.User(sql.FieldHasPrefix(FieldName, v)) 119 | } 120 | 121 | // NameHasSuffix applies the HasSuffix predicate on the "name" field. 122 | func NameHasSuffix(v string) predicate.User { 123 | return predicate.User(sql.FieldHasSuffix(FieldName, v)) 124 | } 125 | 126 | // NameEqualFold applies the EqualFold predicate on the "name" field. 127 | func NameEqualFold(v string) predicate.User { 128 | return predicate.User(sql.FieldEqualFold(FieldName, v)) 129 | } 130 | 131 | // NameContainsFold applies the ContainsFold predicate on the "name" field. 132 | func NameContainsFold(v string) predicate.User { 133 | return predicate.User(sql.FieldContainsFold(FieldName, v)) 134 | } 135 | 136 | // EmailAddressEQ applies the EQ predicate on the "email_address" field. 137 | func EmailAddressEQ(v string) predicate.User { 138 | return predicate.User(sql.FieldEQ(FieldEmailAddress, v)) 139 | } 140 | 141 | // EmailAddressNEQ applies the NEQ predicate on the "email_address" field. 142 | func EmailAddressNEQ(v string) predicate.User { 143 | return predicate.User(sql.FieldNEQ(FieldEmailAddress, v)) 144 | } 145 | 146 | // EmailAddressIn applies the In predicate on the "email_address" field. 147 | func EmailAddressIn(vs ...string) predicate.User { 148 | return predicate.User(sql.FieldIn(FieldEmailAddress, vs...)) 149 | } 150 | 151 | // EmailAddressNotIn applies the NotIn predicate on the "email_address" field. 152 | func EmailAddressNotIn(vs ...string) predicate.User { 153 | return predicate.User(sql.FieldNotIn(FieldEmailAddress, vs...)) 154 | } 155 | 156 | // EmailAddressGT applies the GT predicate on the "email_address" field. 157 | func EmailAddressGT(v string) predicate.User { 158 | return predicate.User(sql.FieldGT(FieldEmailAddress, v)) 159 | } 160 | 161 | // EmailAddressGTE applies the GTE predicate on the "email_address" field. 162 | func EmailAddressGTE(v string) predicate.User { 163 | return predicate.User(sql.FieldGTE(FieldEmailAddress, v)) 164 | } 165 | 166 | // EmailAddressLT applies the LT predicate on the "email_address" field. 167 | func EmailAddressLT(v string) predicate.User { 168 | return predicate.User(sql.FieldLT(FieldEmailAddress, v)) 169 | } 170 | 171 | // EmailAddressLTE applies the LTE predicate on the "email_address" field. 172 | func EmailAddressLTE(v string) predicate.User { 173 | return predicate.User(sql.FieldLTE(FieldEmailAddress, v)) 174 | } 175 | 176 | // EmailAddressContains applies the Contains predicate on the "email_address" field. 177 | func EmailAddressContains(v string) predicate.User { 178 | return predicate.User(sql.FieldContains(FieldEmailAddress, v)) 179 | } 180 | 181 | // EmailAddressHasPrefix applies the HasPrefix predicate on the "email_address" field. 182 | func EmailAddressHasPrefix(v string) predicate.User { 183 | return predicate.User(sql.FieldHasPrefix(FieldEmailAddress, v)) 184 | } 185 | 186 | // EmailAddressHasSuffix applies the HasSuffix predicate on the "email_address" field. 187 | func EmailAddressHasSuffix(v string) predicate.User { 188 | return predicate.User(sql.FieldHasSuffix(FieldEmailAddress, v)) 189 | } 190 | 191 | // EmailAddressEqualFold applies the EqualFold predicate on the "email_address" field. 192 | func EmailAddressEqualFold(v string) predicate.User { 193 | return predicate.User(sql.FieldEqualFold(FieldEmailAddress, v)) 194 | } 195 | 196 | // EmailAddressContainsFold applies the ContainsFold predicate on the "email_address" field. 197 | func EmailAddressContainsFold(v string) predicate.User { 198 | return predicate.User(sql.FieldContainsFold(FieldEmailAddress, v)) 199 | } 200 | 201 | // AliasEQ applies the EQ predicate on the "alias" field. 202 | func AliasEQ(v string) predicate.User { 203 | return predicate.User(sql.FieldEQ(FieldAlias, v)) 204 | } 205 | 206 | // AliasNEQ applies the NEQ predicate on the "alias" field. 207 | func AliasNEQ(v string) predicate.User { 208 | return predicate.User(sql.FieldNEQ(FieldAlias, v)) 209 | } 210 | 211 | // AliasIn applies the In predicate on the "alias" field. 212 | func AliasIn(vs ...string) predicate.User { 213 | return predicate.User(sql.FieldIn(FieldAlias, vs...)) 214 | } 215 | 216 | // AliasNotIn applies the NotIn predicate on the "alias" field. 217 | func AliasNotIn(vs ...string) predicate.User { 218 | return predicate.User(sql.FieldNotIn(FieldAlias, vs...)) 219 | } 220 | 221 | // AliasGT applies the GT predicate on the "alias" field. 222 | func AliasGT(v string) predicate.User { 223 | return predicate.User(sql.FieldGT(FieldAlias, v)) 224 | } 225 | 226 | // AliasGTE applies the GTE predicate on the "alias" field. 227 | func AliasGTE(v string) predicate.User { 228 | return predicate.User(sql.FieldGTE(FieldAlias, v)) 229 | } 230 | 231 | // AliasLT applies the LT predicate on the "alias" field. 232 | func AliasLT(v string) predicate.User { 233 | return predicate.User(sql.FieldLT(FieldAlias, v)) 234 | } 235 | 236 | // AliasLTE applies the LTE predicate on the "alias" field. 237 | func AliasLTE(v string) predicate.User { 238 | return predicate.User(sql.FieldLTE(FieldAlias, v)) 239 | } 240 | 241 | // AliasContains applies the Contains predicate on the "alias" field. 242 | func AliasContains(v string) predicate.User { 243 | return predicate.User(sql.FieldContains(FieldAlias, v)) 244 | } 245 | 246 | // AliasHasPrefix applies the HasPrefix predicate on the "alias" field. 247 | func AliasHasPrefix(v string) predicate.User { 248 | return predicate.User(sql.FieldHasPrefix(FieldAlias, v)) 249 | } 250 | 251 | // AliasHasSuffix applies the HasSuffix predicate on the "alias" field. 252 | func AliasHasSuffix(v string) predicate.User { 253 | return predicate.User(sql.FieldHasSuffix(FieldAlias, v)) 254 | } 255 | 256 | // AliasIsNil applies the IsNil predicate on the "alias" field. 257 | func AliasIsNil() predicate.User { 258 | return predicate.User(sql.FieldIsNull(FieldAlias)) 259 | } 260 | 261 | // AliasNotNil applies the NotNil predicate on the "alias" field. 262 | func AliasNotNil() predicate.User { 263 | return predicate.User(sql.FieldNotNull(FieldAlias)) 264 | } 265 | 266 | // AliasEqualFold applies the EqualFold predicate on the "alias" field. 267 | func AliasEqualFold(v string) predicate.User { 268 | return predicate.User(sql.FieldEqualFold(FieldAlias, v)) 269 | } 270 | 271 | // AliasContainsFold applies the ContainsFold predicate on the "alias" field. 272 | func AliasContainsFold(v string) predicate.User { 273 | return predicate.User(sql.FieldContainsFold(FieldAlias, v)) 274 | } 275 | 276 | // HasAdministered applies the HasEdge predicate on the "administered" edge. 277 | func HasAdministered() predicate.User { 278 | return predicate.User(func(s *sql.Selector) { 279 | step := sqlgraph.NewStep( 280 | sqlgraph.From(Table, FieldID), 281 | sqlgraph.Edge(sqlgraph.O2M, true, AdministeredTable, AdministeredColumn), 282 | ) 283 | sqlgraph.HasNeighbors(s, step) 284 | }) 285 | } 286 | 287 | // HasAdministeredWith applies the HasEdge predicate on the "administered" edge with a given conditions (other predicates). 288 | func HasAdministeredWith(preds ...predicate.Category) predicate.User { 289 | return predicate.User(func(s *sql.Selector) { 290 | step := sqlgraph.NewStep( 291 | sqlgraph.From(Table, FieldID), 292 | sqlgraph.To(AdministeredInverseTable, FieldID), 293 | sqlgraph.Edge(sqlgraph.O2M, true, AdministeredTable, AdministeredColumn), 294 | ) 295 | sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { 296 | for _, p := range preds { 297 | p(s) 298 | } 299 | }) 300 | }) 301 | } 302 | 303 | // And groups predicates with the AND operator between them. 304 | func And(predicates ...predicate.User) predicate.User { 305 | return predicate.User(func(s *sql.Selector) { 306 | s1 := s.Clone().SetP(nil) 307 | for _, p := range predicates { 308 | p(s1) 309 | } 310 | s.Where(s1.P()) 311 | }) 312 | } 313 | 314 | // Or groups predicates with the OR operator between them. 315 | func Or(predicates ...predicate.User) predicate.User { 316 | return predicate.User(func(s *sql.Selector) { 317 | s1 := s.Clone().SetP(nil) 318 | for i, p := range predicates { 319 | if i > 0 { 320 | s1.Or() 321 | } 322 | p(s1) 323 | } 324 | s.Where(s1.P()) 325 | }) 326 | } 327 | 328 | // Not applies the not operator on the given predicate. 329 | func Not(p predicate.User) predicate.User { 330 | return predicate.User(func(s *sql.Selector) { 331 | p(s.Not()) 332 | }) 333 | } 334 | -------------------------------------------------------------------------------- /ent/user_create.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | 10 | "entgo.io/ent/dialect/sql/sqlgraph" 11 | "entgo.io/ent/schema/field" 12 | "github.com/rotemtam/ent-grpc-example/ent/category" 13 | "github.com/rotemtam/ent-grpc-example/ent/user" 14 | ) 15 | 16 | // UserCreate is the builder for creating a User entity. 17 | type UserCreate struct { 18 | config 19 | mutation *UserMutation 20 | hooks []Hook 21 | } 22 | 23 | // SetName sets the "name" field. 24 | func (uc *UserCreate) SetName(s string) *UserCreate { 25 | uc.mutation.SetName(s) 26 | return uc 27 | } 28 | 29 | // SetEmailAddress sets the "email_address" field. 30 | func (uc *UserCreate) SetEmailAddress(s string) *UserCreate { 31 | uc.mutation.SetEmailAddress(s) 32 | return uc 33 | } 34 | 35 | // SetAlias sets the "alias" field. 36 | func (uc *UserCreate) SetAlias(s string) *UserCreate { 37 | uc.mutation.SetAlias(s) 38 | return uc 39 | } 40 | 41 | // SetNillableAlias sets the "alias" field if the given value is not nil. 42 | func (uc *UserCreate) SetNillableAlias(s *string) *UserCreate { 43 | if s != nil { 44 | uc.SetAlias(*s) 45 | } 46 | return uc 47 | } 48 | 49 | // AddAdministeredIDs adds the "administered" edge to the Category entity by IDs. 50 | func (uc *UserCreate) AddAdministeredIDs(ids ...int) *UserCreate { 51 | uc.mutation.AddAdministeredIDs(ids...) 52 | return uc 53 | } 54 | 55 | // AddAdministered adds the "administered" edges to the Category entity. 56 | func (uc *UserCreate) AddAdministered(c ...*Category) *UserCreate { 57 | ids := make([]int, len(c)) 58 | for i := range c { 59 | ids[i] = c[i].ID 60 | } 61 | return uc.AddAdministeredIDs(ids...) 62 | } 63 | 64 | // Mutation returns the UserMutation object of the builder. 65 | func (uc *UserCreate) Mutation() *UserMutation { 66 | return uc.mutation 67 | } 68 | 69 | // Save creates the User in the database. 70 | func (uc *UserCreate) Save(ctx context.Context) (*User, error) { 71 | return withHooks[*User, UserMutation](ctx, uc.sqlSave, uc.mutation, uc.hooks) 72 | } 73 | 74 | // SaveX calls Save and panics if Save returns an error. 75 | func (uc *UserCreate) SaveX(ctx context.Context) *User { 76 | v, err := uc.Save(ctx) 77 | if err != nil { 78 | panic(err) 79 | } 80 | return v 81 | } 82 | 83 | // Exec executes the query. 84 | func (uc *UserCreate) Exec(ctx context.Context) error { 85 | _, err := uc.Save(ctx) 86 | return err 87 | } 88 | 89 | // ExecX is like Exec, but panics if an error occurs. 90 | func (uc *UserCreate) ExecX(ctx context.Context) { 91 | if err := uc.Exec(ctx); err != nil { 92 | panic(err) 93 | } 94 | } 95 | 96 | // check runs all checks and user-defined validators on the builder. 97 | func (uc *UserCreate) check() error { 98 | if _, ok := uc.mutation.Name(); !ok { 99 | return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "User.name"`)} 100 | } 101 | if _, ok := uc.mutation.EmailAddress(); !ok { 102 | return &ValidationError{Name: "email_address", err: errors.New(`ent: missing required field "User.email_address"`)} 103 | } 104 | return nil 105 | } 106 | 107 | func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) { 108 | if err := uc.check(); err != nil { 109 | return nil, err 110 | } 111 | _node, _spec := uc.createSpec() 112 | if err := sqlgraph.CreateNode(ctx, uc.driver, _spec); err != nil { 113 | if sqlgraph.IsConstraintError(err) { 114 | err = &ConstraintError{msg: err.Error(), wrap: err} 115 | } 116 | return nil, err 117 | } 118 | id := _spec.ID.Value.(int64) 119 | _node.ID = int(id) 120 | uc.mutation.id = &_node.ID 121 | uc.mutation.done = true 122 | return _node, nil 123 | } 124 | 125 | func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { 126 | var ( 127 | _node = &User{config: uc.config} 128 | _spec = &sqlgraph.CreateSpec{ 129 | Table: user.Table, 130 | ID: &sqlgraph.FieldSpec{ 131 | Type: field.TypeInt, 132 | Column: user.FieldID, 133 | }, 134 | } 135 | ) 136 | if value, ok := uc.mutation.Name(); ok { 137 | _spec.SetField(user.FieldName, field.TypeString, value) 138 | _node.Name = value 139 | } 140 | if value, ok := uc.mutation.EmailAddress(); ok { 141 | _spec.SetField(user.FieldEmailAddress, field.TypeString, value) 142 | _node.EmailAddress = value 143 | } 144 | if value, ok := uc.mutation.Alias(); ok { 145 | _spec.SetField(user.FieldAlias, field.TypeString, value) 146 | _node.Alias = value 147 | } 148 | if nodes := uc.mutation.AdministeredIDs(); len(nodes) > 0 { 149 | edge := &sqlgraph.EdgeSpec{ 150 | Rel: sqlgraph.O2M, 151 | Inverse: true, 152 | Table: user.AdministeredTable, 153 | Columns: []string{user.AdministeredColumn}, 154 | Bidi: false, 155 | Target: &sqlgraph.EdgeTarget{ 156 | IDSpec: &sqlgraph.FieldSpec{ 157 | Type: field.TypeInt, 158 | Column: category.FieldID, 159 | }, 160 | }, 161 | } 162 | for _, k := range nodes { 163 | edge.Target.Nodes = append(edge.Target.Nodes, k) 164 | } 165 | _spec.Edges = append(_spec.Edges, edge) 166 | } 167 | return _node, _spec 168 | } 169 | 170 | // UserCreateBulk is the builder for creating many User entities in bulk. 171 | type UserCreateBulk struct { 172 | config 173 | builders []*UserCreate 174 | } 175 | 176 | // Save creates the User entities in the database. 177 | func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) { 178 | specs := make([]*sqlgraph.CreateSpec, len(ucb.builders)) 179 | nodes := make([]*User, len(ucb.builders)) 180 | mutators := make([]Mutator, len(ucb.builders)) 181 | for i := range ucb.builders { 182 | func(i int, root context.Context) { 183 | builder := ucb.builders[i] 184 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 185 | mutation, ok := m.(*UserMutation) 186 | if !ok { 187 | return nil, fmt.Errorf("unexpected mutation type %T", m) 188 | } 189 | if err := builder.check(); err != nil { 190 | return nil, err 191 | } 192 | builder.mutation = mutation 193 | nodes[i], specs[i] = builder.createSpec() 194 | var err error 195 | if i < len(mutators)-1 { 196 | _, err = mutators[i+1].Mutate(root, ucb.builders[i+1].mutation) 197 | } else { 198 | spec := &sqlgraph.BatchCreateSpec{Nodes: specs} 199 | // Invoke the actual operation on the latest mutation in the chain. 200 | if err = sqlgraph.BatchCreate(ctx, ucb.driver, spec); err != nil { 201 | if sqlgraph.IsConstraintError(err) { 202 | err = &ConstraintError{msg: err.Error(), wrap: err} 203 | } 204 | } 205 | } 206 | if err != nil { 207 | return nil, err 208 | } 209 | mutation.id = &nodes[i].ID 210 | if specs[i].ID.Value != nil { 211 | id := specs[i].ID.Value.(int64) 212 | nodes[i].ID = int(id) 213 | } 214 | mutation.done = true 215 | return nodes[i], nil 216 | }) 217 | for i := len(builder.hooks) - 1; i >= 0; i-- { 218 | mut = builder.hooks[i](mut) 219 | } 220 | mutators[i] = mut 221 | }(i, ctx) 222 | } 223 | if len(mutators) > 0 { 224 | if _, err := mutators[0].Mutate(ctx, ucb.builders[0].mutation); err != nil { 225 | return nil, err 226 | } 227 | } 228 | return nodes, nil 229 | } 230 | 231 | // SaveX is like Save, but panics if an error occurs. 232 | func (ucb *UserCreateBulk) SaveX(ctx context.Context) []*User { 233 | v, err := ucb.Save(ctx) 234 | if err != nil { 235 | panic(err) 236 | } 237 | return v 238 | } 239 | 240 | // Exec executes the query. 241 | func (ucb *UserCreateBulk) Exec(ctx context.Context) error { 242 | _, err := ucb.Save(ctx) 243 | return err 244 | } 245 | 246 | // ExecX is like Exec, but panics if an error occurs. 247 | func (ucb *UserCreateBulk) ExecX(ctx context.Context) { 248 | if err := ucb.Exec(ctx); err != nil { 249 | panic(err) 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /ent/user_delete.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | 8 | "entgo.io/ent/dialect/sql" 9 | "entgo.io/ent/dialect/sql/sqlgraph" 10 | "entgo.io/ent/schema/field" 11 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 12 | "github.com/rotemtam/ent-grpc-example/ent/user" 13 | ) 14 | 15 | // UserDelete is the builder for deleting a User entity. 16 | type UserDelete struct { 17 | config 18 | hooks []Hook 19 | mutation *UserMutation 20 | } 21 | 22 | // Where appends a list predicates to the UserDelete builder. 23 | func (ud *UserDelete) Where(ps ...predicate.User) *UserDelete { 24 | ud.mutation.Where(ps...) 25 | return ud 26 | } 27 | 28 | // Exec executes the deletion query and returns how many vertices were deleted. 29 | func (ud *UserDelete) Exec(ctx context.Context) (int, error) { 30 | return withHooks[int, UserMutation](ctx, ud.sqlExec, ud.mutation, ud.hooks) 31 | } 32 | 33 | // ExecX is like Exec, but panics if an error occurs. 34 | func (ud *UserDelete) ExecX(ctx context.Context) int { 35 | n, err := ud.Exec(ctx) 36 | if err != nil { 37 | panic(err) 38 | } 39 | return n 40 | } 41 | 42 | func (ud *UserDelete) sqlExec(ctx context.Context) (int, error) { 43 | _spec := &sqlgraph.DeleteSpec{ 44 | Node: &sqlgraph.NodeSpec{ 45 | Table: user.Table, 46 | ID: &sqlgraph.FieldSpec{ 47 | Type: field.TypeInt, 48 | Column: user.FieldID, 49 | }, 50 | }, 51 | } 52 | if ps := ud.mutation.predicates; len(ps) > 0 { 53 | _spec.Predicate = func(selector *sql.Selector) { 54 | for i := range ps { 55 | ps[i](selector) 56 | } 57 | } 58 | } 59 | affected, err := sqlgraph.DeleteNodes(ctx, ud.driver, _spec) 60 | if err != nil && sqlgraph.IsConstraintError(err) { 61 | err = &ConstraintError{msg: err.Error(), wrap: err} 62 | } 63 | ud.mutation.done = true 64 | return affected, err 65 | } 66 | 67 | // UserDeleteOne is the builder for deleting a single User entity. 68 | type UserDeleteOne struct { 69 | ud *UserDelete 70 | } 71 | 72 | // Exec executes the deletion query. 73 | func (udo *UserDeleteOne) Exec(ctx context.Context) error { 74 | n, err := udo.ud.Exec(ctx) 75 | switch { 76 | case err != nil: 77 | return err 78 | case n == 0: 79 | return &NotFoundError{user.Label} 80 | default: 81 | return nil 82 | } 83 | } 84 | 85 | // ExecX is like Exec, but panics if an error occurs. 86 | func (udo *UserDeleteOne) ExecX(ctx context.Context) { 87 | udo.ud.ExecX(ctx) 88 | } 89 | -------------------------------------------------------------------------------- /ent/user_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "database/sql/driver" 8 | "fmt" 9 | "math" 10 | 11 | "entgo.io/ent/dialect/sql" 12 | "entgo.io/ent/dialect/sql/sqlgraph" 13 | "entgo.io/ent/schema/field" 14 | "github.com/rotemtam/ent-grpc-example/ent/category" 15 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 16 | "github.com/rotemtam/ent-grpc-example/ent/user" 17 | ) 18 | 19 | // UserQuery is the builder for querying User entities. 20 | type UserQuery struct { 21 | config 22 | ctx *QueryContext 23 | order []OrderFunc 24 | inters []Interceptor 25 | predicates []predicate.User 26 | withAdministered *CategoryQuery 27 | // intermediate query (i.e. traversal path). 28 | sql *sql.Selector 29 | path func(context.Context) (*sql.Selector, error) 30 | } 31 | 32 | // Where adds a new predicate for the UserQuery builder. 33 | func (uq *UserQuery) Where(ps ...predicate.User) *UserQuery { 34 | uq.predicates = append(uq.predicates, ps...) 35 | return uq 36 | } 37 | 38 | // Limit the number of records to be returned by this query. 39 | func (uq *UserQuery) Limit(limit int) *UserQuery { 40 | uq.ctx.Limit = &limit 41 | return uq 42 | } 43 | 44 | // Offset to start from. 45 | func (uq *UserQuery) Offset(offset int) *UserQuery { 46 | uq.ctx.Offset = &offset 47 | return uq 48 | } 49 | 50 | // Unique configures the query builder to filter duplicate records on query. 51 | // By default, unique is set to true, and can be disabled using this method. 52 | func (uq *UserQuery) Unique(unique bool) *UserQuery { 53 | uq.ctx.Unique = &unique 54 | return uq 55 | } 56 | 57 | // Order specifies how the records should be ordered. 58 | func (uq *UserQuery) Order(o ...OrderFunc) *UserQuery { 59 | uq.order = append(uq.order, o...) 60 | return uq 61 | } 62 | 63 | // QueryAdministered chains the current query on the "administered" edge. 64 | func (uq *UserQuery) QueryAdministered() *CategoryQuery { 65 | query := (&CategoryClient{config: uq.config}).Query() 66 | query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { 67 | if err := uq.prepareQuery(ctx); err != nil { 68 | return nil, err 69 | } 70 | selector := uq.sqlQuery(ctx) 71 | if err := selector.Err(); err != nil { 72 | return nil, err 73 | } 74 | step := sqlgraph.NewStep( 75 | sqlgraph.From(user.Table, user.FieldID, selector), 76 | sqlgraph.To(category.Table, category.FieldID), 77 | sqlgraph.Edge(sqlgraph.O2M, true, user.AdministeredTable, user.AdministeredColumn), 78 | ) 79 | fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) 80 | return fromU, nil 81 | } 82 | return query 83 | } 84 | 85 | // First returns the first User entity from the query. 86 | // Returns a *NotFoundError when no User was found. 87 | func (uq *UserQuery) First(ctx context.Context) (*User, error) { 88 | nodes, err := uq.Limit(1).All(setContextOp(ctx, uq.ctx, "First")) 89 | if err != nil { 90 | return nil, err 91 | } 92 | if len(nodes) == 0 { 93 | return nil, &NotFoundError{user.Label} 94 | } 95 | return nodes[0], nil 96 | } 97 | 98 | // FirstX is like First, but panics if an error occurs. 99 | func (uq *UserQuery) FirstX(ctx context.Context) *User { 100 | node, err := uq.First(ctx) 101 | if err != nil && !IsNotFound(err) { 102 | panic(err) 103 | } 104 | return node 105 | } 106 | 107 | // FirstID returns the first User ID from the query. 108 | // Returns a *NotFoundError when no User ID was found. 109 | func (uq *UserQuery) FirstID(ctx context.Context) (id int, err error) { 110 | var ids []int 111 | if ids, err = uq.Limit(1).IDs(setContextOp(ctx, uq.ctx, "FirstID")); err != nil { 112 | return 113 | } 114 | if len(ids) == 0 { 115 | err = &NotFoundError{user.Label} 116 | return 117 | } 118 | return ids[0], nil 119 | } 120 | 121 | // FirstIDX is like FirstID, but panics if an error occurs. 122 | func (uq *UserQuery) FirstIDX(ctx context.Context) int { 123 | id, err := uq.FirstID(ctx) 124 | if err != nil && !IsNotFound(err) { 125 | panic(err) 126 | } 127 | return id 128 | } 129 | 130 | // Only returns a single User entity found by the query, ensuring it only returns one. 131 | // Returns a *NotSingularError when more than one User entity is found. 132 | // Returns a *NotFoundError when no User entities are found. 133 | func (uq *UserQuery) Only(ctx context.Context) (*User, error) { 134 | nodes, err := uq.Limit(2).All(setContextOp(ctx, uq.ctx, "Only")) 135 | if err != nil { 136 | return nil, err 137 | } 138 | switch len(nodes) { 139 | case 1: 140 | return nodes[0], nil 141 | case 0: 142 | return nil, &NotFoundError{user.Label} 143 | default: 144 | return nil, &NotSingularError{user.Label} 145 | } 146 | } 147 | 148 | // OnlyX is like Only, but panics if an error occurs. 149 | func (uq *UserQuery) OnlyX(ctx context.Context) *User { 150 | node, err := uq.Only(ctx) 151 | if err != nil { 152 | panic(err) 153 | } 154 | return node 155 | } 156 | 157 | // OnlyID is like Only, but returns the only User ID in the query. 158 | // Returns a *NotSingularError when more than one User ID is found. 159 | // Returns a *NotFoundError when no entities are found. 160 | func (uq *UserQuery) OnlyID(ctx context.Context) (id int, err error) { 161 | var ids []int 162 | if ids, err = uq.Limit(2).IDs(setContextOp(ctx, uq.ctx, "OnlyID")); err != nil { 163 | return 164 | } 165 | switch len(ids) { 166 | case 1: 167 | id = ids[0] 168 | case 0: 169 | err = &NotFoundError{user.Label} 170 | default: 171 | err = &NotSingularError{user.Label} 172 | } 173 | return 174 | } 175 | 176 | // OnlyIDX is like OnlyID, but panics if an error occurs. 177 | func (uq *UserQuery) OnlyIDX(ctx context.Context) int { 178 | id, err := uq.OnlyID(ctx) 179 | if err != nil { 180 | panic(err) 181 | } 182 | return id 183 | } 184 | 185 | // All executes the query and returns a list of Users. 186 | func (uq *UserQuery) All(ctx context.Context) ([]*User, error) { 187 | ctx = setContextOp(ctx, uq.ctx, "All") 188 | if err := uq.prepareQuery(ctx); err != nil { 189 | return nil, err 190 | } 191 | qr := querierAll[[]*User, *UserQuery]() 192 | return withInterceptors[[]*User](ctx, uq, qr, uq.inters) 193 | } 194 | 195 | // AllX is like All, but panics if an error occurs. 196 | func (uq *UserQuery) AllX(ctx context.Context) []*User { 197 | nodes, err := uq.All(ctx) 198 | if err != nil { 199 | panic(err) 200 | } 201 | return nodes 202 | } 203 | 204 | // IDs executes the query and returns a list of User IDs. 205 | func (uq *UserQuery) IDs(ctx context.Context) ([]int, error) { 206 | var ids []int 207 | ctx = setContextOp(ctx, uq.ctx, "IDs") 208 | if err := uq.Select(user.FieldID).Scan(ctx, &ids); err != nil { 209 | return nil, err 210 | } 211 | return ids, nil 212 | } 213 | 214 | // IDsX is like IDs, but panics if an error occurs. 215 | func (uq *UserQuery) IDsX(ctx context.Context) []int { 216 | ids, err := uq.IDs(ctx) 217 | if err != nil { 218 | panic(err) 219 | } 220 | return ids 221 | } 222 | 223 | // Count returns the count of the given query. 224 | func (uq *UserQuery) Count(ctx context.Context) (int, error) { 225 | ctx = setContextOp(ctx, uq.ctx, "Count") 226 | if err := uq.prepareQuery(ctx); err != nil { 227 | return 0, err 228 | } 229 | return withInterceptors[int](ctx, uq, querierCount[*UserQuery](), uq.inters) 230 | } 231 | 232 | // CountX is like Count, but panics if an error occurs. 233 | func (uq *UserQuery) CountX(ctx context.Context) int { 234 | count, err := uq.Count(ctx) 235 | if err != nil { 236 | panic(err) 237 | } 238 | return count 239 | } 240 | 241 | // Exist returns true if the query has elements in the graph. 242 | func (uq *UserQuery) Exist(ctx context.Context) (bool, error) { 243 | ctx = setContextOp(ctx, uq.ctx, "Exist") 244 | switch _, err := uq.FirstID(ctx); { 245 | case IsNotFound(err): 246 | return false, nil 247 | case err != nil: 248 | return false, fmt.Errorf("ent: check existence: %w", err) 249 | default: 250 | return true, nil 251 | } 252 | } 253 | 254 | // ExistX is like Exist, but panics if an error occurs. 255 | func (uq *UserQuery) ExistX(ctx context.Context) bool { 256 | exist, err := uq.Exist(ctx) 257 | if err != nil { 258 | panic(err) 259 | } 260 | return exist 261 | } 262 | 263 | // Clone returns a duplicate of the UserQuery builder, including all associated steps. It can be 264 | // used to prepare common query builders and use them differently after the clone is made. 265 | func (uq *UserQuery) Clone() *UserQuery { 266 | if uq == nil { 267 | return nil 268 | } 269 | return &UserQuery{ 270 | config: uq.config, 271 | ctx: uq.ctx.Clone(), 272 | order: append([]OrderFunc{}, uq.order...), 273 | inters: append([]Interceptor{}, uq.inters...), 274 | predicates: append([]predicate.User{}, uq.predicates...), 275 | withAdministered: uq.withAdministered.Clone(), 276 | // clone intermediate query. 277 | sql: uq.sql.Clone(), 278 | path: uq.path, 279 | } 280 | } 281 | 282 | // WithAdministered tells the query-builder to eager-load the nodes that are connected to 283 | // the "administered" edge. The optional arguments are used to configure the query builder of the edge. 284 | func (uq *UserQuery) WithAdministered(opts ...func(*CategoryQuery)) *UserQuery { 285 | query := (&CategoryClient{config: uq.config}).Query() 286 | for _, opt := range opts { 287 | opt(query) 288 | } 289 | uq.withAdministered = query 290 | return uq 291 | } 292 | 293 | // GroupBy is used to group vertices by one or more fields/columns. 294 | // It is often used with aggregate functions, like: count, max, mean, min, sum. 295 | // 296 | // Example: 297 | // 298 | // var v []struct { 299 | // Name string `json:"name,omitempty"` 300 | // Count int `json:"count,omitempty"` 301 | // } 302 | // 303 | // client.User.Query(). 304 | // GroupBy(user.FieldName). 305 | // Aggregate(ent.Count()). 306 | // Scan(ctx, &v) 307 | func (uq *UserQuery) GroupBy(field string, fields ...string) *UserGroupBy { 308 | uq.ctx.Fields = append([]string{field}, fields...) 309 | grbuild := &UserGroupBy{build: uq} 310 | grbuild.flds = &uq.ctx.Fields 311 | grbuild.label = user.Label 312 | grbuild.scan = grbuild.Scan 313 | return grbuild 314 | } 315 | 316 | // Select allows the selection one or more fields/columns for the given query, 317 | // instead of selecting all fields in the entity. 318 | // 319 | // Example: 320 | // 321 | // var v []struct { 322 | // Name string `json:"name,omitempty"` 323 | // } 324 | // 325 | // client.User.Query(). 326 | // Select(user.FieldName). 327 | // Scan(ctx, &v) 328 | func (uq *UserQuery) Select(fields ...string) *UserSelect { 329 | uq.ctx.Fields = append(uq.ctx.Fields, fields...) 330 | sbuild := &UserSelect{UserQuery: uq} 331 | sbuild.label = user.Label 332 | sbuild.flds, sbuild.scan = &uq.ctx.Fields, sbuild.Scan 333 | return sbuild 334 | } 335 | 336 | // Aggregate returns a UserSelect configured with the given aggregations. 337 | func (uq *UserQuery) Aggregate(fns ...AggregateFunc) *UserSelect { 338 | return uq.Select().Aggregate(fns...) 339 | } 340 | 341 | func (uq *UserQuery) prepareQuery(ctx context.Context) error { 342 | for _, inter := range uq.inters { 343 | if inter == nil { 344 | return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") 345 | } 346 | if trv, ok := inter.(Traverser); ok { 347 | if err := trv.Traverse(ctx, uq); err != nil { 348 | return err 349 | } 350 | } 351 | } 352 | for _, f := range uq.ctx.Fields { 353 | if !user.ValidColumn(f) { 354 | return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} 355 | } 356 | } 357 | if uq.path != nil { 358 | prev, err := uq.path(ctx) 359 | if err != nil { 360 | return err 361 | } 362 | uq.sql = prev 363 | } 364 | return nil 365 | } 366 | 367 | func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, error) { 368 | var ( 369 | nodes = []*User{} 370 | _spec = uq.querySpec() 371 | loadedTypes = [1]bool{ 372 | uq.withAdministered != nil, 373 | } 374 | ) 375 | _spec.ScanValues = func(columns []string) ([]any, error) { 376 | return (*User).scanValues(nil, columns) 377 | } 378 | _spec.Assign = func(columns []string, values []any) error { 379 | node := &User{config: uq.config} 380 | nodes = append(nodes, node) 381 | node.Edges.loadedTypes = loadedTypes 382 | return node.assignValues(columns, values) 383 | } 384 | for i := range hooks { 385 | hooks[i](ctx, _spec) 386 | } 387 | if err := sqlgraph.QueryNodes(ctx, uq.driver, _spec); err != nil { 388 | return nil, err 389 | } 390 | if len(nodes) == 0 { 391 | return nodes, nil 392 | } 393 | if query := uq.withAdministered; query != nil { 394 | if err := uq.loadAdministered(ctx, query, nodes, 395 | func(n *User) { n.Edges.Administered = []*Category{} }, 396 | func(n *User, e *Category) { n.Edges.Administered = append(n.Edges.Administered, e) }); err != nil { 397 | return nil, err 398 | } 399 | } 400 | return nodes, nil 401 | } 402 | 403 | func (uq *UserQuery) loadAdministered(ctx context.Context, query *CategoryQuery, nodes []*User, init func(*User), assign func(*User, *Category)) error { 404 | fks := make([]driver.Value, 0, len(nodes)) 405 | nodeids := make(map[int]*User) 406 | for i := range nodes { 407 | fks = append(fks, nodes[i].ID) 408 | nodeids[nodes[i].ID] = nodes[i] 409 | if init != nil { 410 | init(nodes[i]) 411 | } 412 | } 413 | query.withFKs = true 414 | query.Where(predicate.Category(func(s *sql.Selector) { 415 | s.Where(sql.InValues(user.AdministeredColumn, fks...)) 416 | })) 417 | neighbors, err := query.All(ctx) 418 | if err != nil { 419 | return err 420 | } 421 | for _, n := range neighbors { 422 | fk := n.category_admin 423 | if fk == nil { 424 | return fmt.Errorf(`foreign-key "category_admin" is nil for node %v`, n.ID) 425 | } 426 | node, ok := nodeids[*fk] 427 | if !ok { 428 | return fmt.Errorf(`unexpected foreign-key "category_admin" returned %v for node %v`, *fk, n.ID) 429 | } 430 | assign(node, n) 431 | } 432 | return nil 433 | } 434 | 435 | func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { 436 | _spec := uq.querySpec() 437 | _spec.Node.Columns = uq.ctx.Fields 438 | if len(uq.ctx.Fields) > 0 { 439 | _spec.Unique = uq.ctx.Unique != nil && *uq.ctx.Unique 440 | } 441 | return sqlgraph.CountNodes(ctx, uq.driver, _spec) 442 | } 443 | 444 | func (uq *UserQuery) querySpec() *sqlgraph.QuerySpec { 445 | _spec := &sqlgraph.QuerySpec{ 446 | Node: &sqlgraph.NodeSpec{ 447 | Table: user.Table, 448 | Columns: user.Columns, 449 | ID: &sqlgraph.FieldSpec{ 450 | Type: field.TypeInt, 451 | Column: user.FieldID, 452 | }, 453 | }, 454 | From: uq.sql, 455 | Unique: true, 456 | } 457 | if unique := uq.ctx.Unique; unique != nil { 458 | _spec.Unique = *unique 459 | } 460 | if fields := uq.ctx.Fields; len(fields) > 0 { 461 | _spec.Node.Columns = make([]string, 0, len(fields)) 462 | _spec.Node.Columns = append(_spec.Node.Columns, user.FieldID) 463 | for i := range fields { 464 | if fields[i] != user.FieldID { 465 | _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) 466 | } 467 | } 468 | } 469 | if ps := uq.predicates; len(ps) > 0 { 470 | _spec.Predicate = func(selector *sql.Selector) { 471 | for i := range ps { 472 | ps[i](selector) 473 | } 474 | } 475 | } 476 | if limit := uq.ctx.Limit; limit != nil { 477 | _spec.Limit = *limit 478 | } 479 | if offset := uq.ctx.Offset; offset != nil { 480 | _spec.Offset = *offset 481 | } 482 | if ps := uq.order; len(ps) > 0 { 483 | _spec.Order = func(selector *sql.Selector) { 484 | for i := range ps { 485 | ps[i](selector) 486 | } 487 | } 488 | } 489 | return _spec 490 | } 491 | 492 | func (uq *UserQuery) sqlQuery(ctx context.Context) *sql.Selector { 493 | builder := sql.Dialect(uq.driver.Dialect()) 494 | t1 := builder.Table(user.Table) 495 | columns := uq.ctx.Fields 496 | if len(columns) == 0 { 497 | columns = user.Columns 498 | } 499 | selector := builder.Select(t1.Columns(columns...)...).From(t1) 500 | if uq.sql != nil { 501 | selector = uq.sql 502 | selector.Select(selector.Columns(columns...)...) 503 | } 504 | if uq.ctx.Unique != nil && *uq.ctx.Unique { 505 | selector.Distinct() 506 | } 507 | for _, p := range uq.predicates { 508 | p(selector) 509 | } 510 | for _, p := range uq.order { 511 | p(selector) 512 | } 513 | if offset := uq.ctx.Offset; offset != nil { 514 | // limit is mandatory for offset clause. We start 515 | // with default value, and override it below if needed. 516 | selector.Offset(*offset).Limit(math.MaxInt32) 517 | } 518 | if limit := uq.ctx.Limit; limit != nil { 519 | selector.Limit(*limit) 520 | } 521 | return selector 522 | } 523 | 524 | // UserGroupBy is the group-by builder for User entities. 525 | type UserGroupBy struct { 526 | selector 527 | build *UserQuery 528 | } 529 | 530 | // Aggregate adds the given aggregation functions to the group-by query. 531 | func (ugb *UserGroupBy) Aggregate(fns ...AggregateFunc) *UserGroupBy { 532 | ugb.fns = append(ugb.fns, fns...) 533 | return ugb 534 | } 535 | 536 | // Scan applies the selector query and scans the result into the given value. 537 | func (ugb *UserGroupBy) Scan(ctx context.Context, v any) error { 538 | ctx = setContextOp(ctx, ugb.build.ctx, "GroupBy") 539 | if err := ugb.build.prepareQuery(ctx); err != nil { 540 | return err 541 | } 542 | return scanWithInterceptors[*UserQuery, *UserGroupBy](ctx, ugb.build, ugb, ugb.build.inters, v) 543 | } 544 | 545 | func (ugb *UserGroupBy) sqlScan(ctx context.Context, root *UserQuery, v any) error { 546 | selector := root.sqlQuery(ctx).Select() 547 | aggregation := make([]string, 0, len(ugb.fns)) 548 | for _, fn := range ugb.fns { 549 | aggregation = append(aggregation, fn(selector)) 550 | } 551 | if len(selector.SelectedColumns()) == 0 { 552 | columns := make([]string, 0, len(*ugb.flds)+len(ugb.fns)) 553 | for _, f := range *ugb.flds { 554 | columns = append(columns, selector.C(f)) 555 | } 556 | columns = append(columns, aggregation...) 557 | selector.Select(columns...) 558 | } 559 | selector.GroupBy(selector.Columns(*ugb.flds...)...) 560 | if err := selector.Err(); err != nil { 561 | return err 562 | } 563 | rows := &sql.Rows{} 564 | query, args := selector.Query() 565 | if err := ugb.build.driver.Query(ctx, query, args, rows); err != nil { 566 | return err 567 | } 568 | defer rows.Close() 569 | return sql.ScanSlice(rows, v) 570 | } 571 | 572 | // UserSelect is the builder for selecting fields of User entities. 573 | type UserSelect struct { 574 | *UserQuery 575 | selector 576 | } 577 | 578 | // Aggregate adds the given aggregation functions to the selector query. 579 | func (us *UserSelect) Aggregate(fns ...AggregateFunc) *UserSelect { 580 | us.fns = append(us.fns, fns...) 581 | return us 582 | } 583 | 584 | // Scan applies the selector query and scans the result into the given value. 585 | func (us *UserSelect) Scan(ctx context.Context, v any) error { 586 | ctx = setContextOp(ctx, us.ctx, "Select") 587 | if err := us.prepareQuery(ctx); err != nil { 588 | return err 589 | } 590 | return scanWithInterceptors[*UserQuery, *UserSelect](ctx, us.UserQuery, us, us.inters, v) 591 | } 592 | 593 | func (us *UserSelect) sqlScan(ctx context.Context, root *UserQuery, v any) error { 594 | selector := root.sqlQuery(ctx) 595 | aggregation := make([]string, 0, len(us.fns)) 596 | for _, fn := range us.fns { 597 | aggregation = append(aggregation, fn(selector)) 598 | } 599 | switch n := len(*us.selector.flds); { 600 | case n == 0 && len(aggregation) > 0: 601 | selector.Select(aggregation...) 602 | case n != 0 && len(aggregation) > 0: 603 | selector.AppendSelect(aggregation...) 604 | } 605 | rows := &sql.Rows{} 606 | query, args := selector.Query() 607 | if err := us.driver.Query(ctx, query, args, rows); err != nil { 608 | return err 609 | } 610 | defer rows.Close() 611 | return sql.ScanSlice(rows, v) 612 | } 613 | -------------------------------------------------------------------------------- /ent/user_update.go: -------------------------------------------------------------------------------- 1 | // Code generated by ent, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | 10 | "entgo.io/ent/dialect/sql" 11 | "entgo.io/ent/dialect/sql/sqlgraph" 12 | "entgo.io/ent/schema/field" 13 | "github.com/rotemtam/ent-grpc-example/ent/category" 14 | "github.com/rotemtam/ent-grpc-example/ent/predicate" 15 | "github.com/rotemtam/ent-grpc-example/ent/user" 16 | ) 17 | 18 | // UserUpdate is the builder for updating User entities. 19 | type UserUpdate struct { 20 | config 21 | hooks []Hook 22 | mutation *UserMutation 23 | } 24 | 25 | // Where appends a list predicates to the UserUpdate builder. 26 | func (uu *UserUpdate) Where(ps ...predicate.User) *UserUpdate { 27 | uu.mutation.Where(ps...) 28 | return uu 29 | } 30 | 31 | // SetName sets the "name" field. 32 | func (uu *UserUpdate) SetName(s string) *UserUpdate { 33 | uu.mutation.SetName(s) 34 | return uu 35 | } 36 | 37 | // SetEmailAddress sets the "email_address" field. 38 | func (uu *UserUpdate) SetEmailAddress(s string) *UserUpdate { 39 | uu.mutation.SetEmailAddress(s) 40 | return uu 41 | } 42 | 43 | // SetAlias sets the "alias" field. 44 | func (uu *UserUpdate) SetAlias(s string) *UserUpdate { 45 | uu.mutation.SetAlias(s) 46 | return uu 47 | } 48 | 49 | // SetNillableAlias sets the "alias" field if the given value is not nil. 50 | func (uu *UserUpdate) SetNillableAlias(s *string) *UserUpdate { 51 | if s != nil { 52 | uu.SetAlias(*s) 53 | } 54 | return uu 55 | } 56 | 57 | // ClearAlias clears the value of the "alias" field. 58 | func (uu *UserUpdate) ClearAlias() *UserUpdate { 59 | uu.mutation.ClearAlias() 60 | return uu 61 | } 62 | 63 | // AddAdministeredIDs adds the "administered" edge to the Category entity by IDs. 64 | func (uu *UserUpdate) AddAdministeredIDs(ids ...int) *UserUpdate { 65 | uu.mutation.AddAdministeredIDs(ids...) 66 | return uu 67 | } 68 | 69 | // AddAdministered adds the "administered" edges to the Category entity. 70 | func (uu *UserUpdate) AddAdministered(c ...*Category) *UserUpdate { 71 | ids := make([]int, len(c)) 72 | for i := range c { 73 | ids[i] = c[i].ID 74 | } 75 | return uu.AddAdministeredIDs(ids...) 76 | } 77 | 78 | // Mutation returns the UserMutation object of the builder. 79 | func (uu *UserUpdate) Mutation() *UserMutation { 80 | return uu.mutation 81 | } 82 | 83 | // ClearAdministered clears all "administered" edges to the Category entity. 84 | func (uu *UserUpdate) ClearAdministered() *UserUpdate { 85 | uu.mutation.ClearAdministered() 86 | return uu 87 | } 88 | 89 | // RemoveAdministeredIDs removes the "administered" edge to Category entities by IDs. 90 | func (uu *UserUpdate) RemoveAdministeredIDs(ids ...int) *UserUpdate { 91 | uu.mutation.RemoveAdministeredIDs(ids...) 92 | return uu 93 | } 94 | 95 | // RemoveAdministered removes "administered" edges to Category entities. 96 | func (uu *UserUpdate) RemoveAdministered(c ...*Category) *UserUpdate { 97 | ids := make([]int, len(c)) 98 | for i := range c { 99 | ids[i] = c[i].ID 100 | } 101 | return uu.RemoveAdministeredIDs(ids...) 102 | } 103 | 104 | // Save executes the query and returns the number of nodes affected by the update operation. 105 | func (uu *UserUpdate) Save(ctx context.Context) (int, error) { 106 | return withHooks[int, UserMutation](ctx, uu.sqlSave, uu.mutation, uu.hooks) 107 | } 108 | 109 | // SaveX is like Save, but panics if an error occurs. 110 | func (uu *UserUpdate) SaveX(ctx context.Context) int { 111 | affected, err := uu.Save(ctx) 112 | if err != nil { 113 | panic(err) 114 | } 115 | return affected 116 | } 117 | 118 | // Exec executes the query. 119 | func (uu *UserUpdate) Exec(ctx context.Context) error { 120 | _, err := uu.Save(ctx) 121 | return err 122 | } 123 | 124 | // ExecX is like Exec, but panics if an error occurs. 125 | func (uu *UserUpdate) ExecX(ctx context.Context) { 126 | if err := uu.Exec(ctx); err != nil { 127 | panic(err) 128 | } 129 | } 130 | 131 | func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { 132 | _spec := &sqlgraph.UpdateSpec{ 133 | Node: &sqlgraph.NodeSpec{ 134 | Table: user.Table, 135 | Columns: user.Columns, 136 | ID: &sqlgraph.FieldSpec{ 137 | Type: field.TypeInt, 138 | Column: user.FieldID, 139 | }, 140 | }, 141 | } 142 | if ps := uu.mutation.predicates; len(ps) > 0 { 143 | _spec.Predicate = func(selector *sql.Selector) { 144 | for i := range ps { 145 | ps[i](selector) 146 | } 147 | } 148 | } 149 | if value, ok := uu.mutation.Name(); ok { 150 | _spec.SetField(user.FieldName, field.TypeString, value) 151 | } 152 | if value, ok := uu.mutation.EmailAddress(); ok { 153 | _spec.SetField(user.FieldEmailAddress, field.TypeString, value) 154 | } 155 | if value, ok := uu.mutation.Alias(); ok { 156 | _spec.SetField(user.FieldAlias, field.TypeString, value) 157 | } 158 | if uu.mutation.AliasCleared() { 159 | _spec.ClearField(user.FieldAlias, field.TypeString) 160 | } 161 | if uu.mutation.AdministeredCleared() { 162 | edge := &sqlgraph.EdgeSpec{ 163 | Rel: sqlgraph.O2M, 164 | Inverse: true, 165 | Table: user.AdministeredTable, 166 | Columns: []string{user.AdministeredColumn}, 167 | Bidi: false, 168 | Target: &sqlgraph.EdgeTarget{ 169 | IDSpec: &sqlgraph.FieldSpec{ 170 | Type: field.TypeInt, 171 | Column: category.FieldID, 172 | }, 173 | }, 174 | } 175 | _spec.Edges.Clear = append(_spec.Edges.Clear, edge) 176 | } 177 | if nodes := uu.mutation.RemovedAdministeredIDs(); len(nodes) > 0 && !uu.mutation.AdministeredCleared() { 178 | edge := &sqlgraph.EdgeSpec{ 179 | Rel: sqlgraph.O2M, 180 | Inverse: true, 181 | Table: user.AdministeredTable, 182 | Columns: []string{user.AdministeredColumn}, 183 | Bidi: false, 184 | Target: &sqlgraph.EdgeTarget{ 185 | IDSpec: &sqlgraph.FieldSpec{ 186 | Type: field.TypeInt, 187 | Column: category.FieldID, 188 | }, 189 | }, 190 | } 191 | for _, k := range nodes { 192 | edge.Target.Nodes = append(edge.Target.Nodes, k) 193 | } 194 | _spec.Edges.Clear = append(_spec.Edges.Clear, edge) 195 | } 196 | if nodes := uu.mutation.AdministeredIDs(); len(nodes) > 0 { 197 | edge := &sqlgraph.EdgeSpec{ 198 | Rel: sqlgraph.O2M, 199 | Inverse: true, 200 | Table: user.AdministeredTable, 201 | Columns: []string{user.AdministeredColumn}, 202 | Bidi: false, 203 | Target: &sqlgraph.EdgeTarget{ 204 | IDSpec: &sqlgraph.FieldSpec{ 205 | Type: field.TypeInt, 206 | Column: category.FieldID, 207 | }, 208 | }, 209 | } 210 | for _, k := range nodes { 211 | edge.Target.Nodes = append(edge.Target.Nodes, k) 212 | } 213 | _spec.Edges.Add = append(_spec.Edges.Add, edge) 214 | } 215 | if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { 216 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 217 | err = &NotFoundError{user.Label} 218 | } else if sqlgraph.IsConstraintError(err) { 219 | err = &ConstraintError{msg: err.Error(), wrap: err} 220 | } 221 | return 0, err 222 | } 223 | uu.mutation.done = true 224 | return n, nil 225 | } 226 | 227 | // UserUpdateOne is the builder for updating a single User entity. 228 | type UserUpdateOne struct { 229 | config 230 | fields []string 231 | hooks []Hook 232 | mutation *UserMutation 233 | } 234 | 235 | // SetName sets the "name" field. 236 | func (uuo *UserUpdateOne) SetName(s string) *UserUpdateOne { 237 | uuo.mutation.SetName(s) 238 | return uuo 239 | } 240 | 241 | // SetEmailAddress sets the "email_address" field. 242 | func (uuo *UserUpdateOne) SetEmailAddress(s string) *UserUpdateOne { 243 | uuo.mutation.SetEmailAddress(s) 244 | return uuo 245 | } 246 | 247 | // SetAlias sets the "alias" field. 248 | func (uuo *UserUpdateOne) SetAlias(s string) *UserUpdateOne { 249 | uuo.mutation.SetAlias(s) 250 | return uuo 251 | } 252 | 253 | // SetNillableAlias sets the "alias" field if the given value is not nil. 254 | func (uuo *UserUpdateOne) SetNillableAlias(s *string) *UserUpdateOne { 255 | if s != nil { 256 | uuo.SetAlias(*s) 257 | } 258 | return uuo 259 | } 260 | 261 | // ClearAlias clears the value of the "alias" field. 262 | func (uuo *UserUpdateOne) ClearAlias() *UserUpdateOne { 263 | uuo.mutation.ClearAlias() 264 | return uuo 265 | } 266 | 267 | // AddAdministeredIDs adds the "administered" edge to the Category entity by IDs. 268 | func (uuo *UserUpdateOne) AddAdministeredIDs(ids ...int) *UserUpdateOne { 269 | uuo.mutation.AddAdministeredIDs(ids...) 270 | return uuo 271 | } 272 | 273 | // AddAdministered adds the "administered" edges to the Category entity. 274 | func (uuo *UserUpdateOne) AddAdministered(c ...*Category) *UserUpdateOne { 275 | ids := make([]int, len(c)) 276 | for i := range c { 277 | ids[i] = c[i].ID 278 | } 279 | return uuo.AddAdministeredIDs(ids...) 280 | } 281 | 282 | // Mutation returns the UserMutation object of the builder. 283 | func (uuo *UserUpdateOne) Mutation() *UserMutation { 284 | return uuo.mutation 285 | } 286 | 287 | // ClearAdministered clears all "administered" edges to the Category entity. 288 | func (uuo *UserUpdateOne) ClearAdministered() *UserUpdateOne { 289 | uuo.mutation.ClearAdministered() 290 | return uuo 291 | } 292 | 293 | // RemoveAdministeredIDs removes the "administered" edge to Category entities by IDs. 294 | func (uuo *UserUpdateOne) RemoveAdministeredIDs(ids ...int) *UserUpdateOne { 295 | uuo.mutation.RemoveAdministeredIDs(ids...) 296 | return uuo 297 | } 298 | 299 | // RemoveAdministered removes "administered" edges to Category entities. 300 | func (uuo *UserUpdateOne) RemoveAdministered(c ...*Category) *UserUpdateOne { 301 | ids := make([]int, len(c)) 302 | for i := range c { 303 | ids[i] = c[i].ID 304 | } 305 | return uuo.RemoveAdministeredIDs(ids...) 306 | } 307 | 308 | // Select allows selecting one or more fields (columns) of the returned entity. 309 | // The default is selecting all fields defined in the entity schema. 310 | func (uuo *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne { 311 | uuo.fields = append([]string{field}, fields...) 312 | return uuo 313 | } 314 | 315 | // Save executes the query and returns the updated User entity. 316 | func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) { 317 | return withHooks[*User, UserMutation](ctx, uuo.sqlSave, uuo.mutation, uuo.hooks) 318 | } 319 | 320 | // SaveX is like Save, but panics if an error occurs. 321 | func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User { 322 | node, err := uuo.Save(ctx) 323 | if err != nil { 324 | panic(err) 325 | } 326 | return node 327 | } 328 | 329 | // Exec executes the query on the entity. 330 | func (uuo *UserUpdateOne) Exec(ctx context.Context) error { 331 | _, err := uuo.Save(ctx) 332 | return err 333 | } 334 | 335 | // ExecX is like Exec, but panics if an error occurs. 336 | func (uuo *UserUpdateOne) ExecX(ctx context.Context) { 337 | if err := uuo.Exec(ctx); err != nil { 338 | panic(err) 339 | } 340 | } 341 | 342 | func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) { 343 | _spec := &sqlgraph.UpdateSpec{ 344 | Node: &sqlgraph.NodeSpec{ 345 | Table: user.Table, 346 | Columns: user.Columns, 347 | ID: &sqlgraph.FieldSpec{ 348 | Type: field.TypeInt, 349 | Column: user.FieldID, 350 | }, 351 | }, 352 | } 353 | id, ok := uuo.mutation.ID() 354 | if !ok { 355 | return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "User.id" for update`)} 356 | } 357 | _spec.Node.ID.Value = id 358 | if fields := uuo.fields; len(fields) > 0 { 359 | _spec.Node.Columns = make([]string, 0, len(fields)) 360 | _spec.Node.Columns = append(_spec.Node.Columns, user.FieldID) 361 | for _, f := range fields { 362 | if !user.ValidColumn(f) { 363 | return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} 364 | } 365 | if f != user.FieldID { 366 | _spec.Node.Columns = append(_spec.Node.Columns, f) 367 | } 368 | } 369 | } 370 | if ps := uuo.mutation.predicates; len(ps) > 0 { 371 | _spec.Predicate = func(selector *sql.Selector) { 372 | for i := range ps { 373 | ps[i](selector) 374 | } 375 | } 376 | } 377 | if value, ok := uuo.mutation.Name(); ok { 378 | _spec.SetField(user.FieldName, field.TypeString, value) 379 | } 380 | if value, ok := uuo.mutation.EmailAddress(); ok { 381 | _spec.SetField(user.FieldEmailAddress, field.TypeString, value) 382 | } 383 | if value, ok := uuo.mutation.Alias(); ok { 384 | _spec.SetField(user.FieldAlias, field.TypeString, value) 385 | } 386 | if uuo.mutation.AliasCleared() { 387 | _spec.ClearField(user.FieldAlias, field.TypeString) 388 | } 389 | if uuo.mutation.AdministeredCleared() { 390 | edge := &sqlgraph.EdgeSpec{ 391 | Rel: sqlgraph.O2M, 392 | Inverse: true, 393 | Table: user.AdministeredTable, 394 | Columns: []string{user.AdministeredColumn}, 395 | Bidi: false, 396 | Target: &sqlgraph.EdgeTarget{ 397 | IDSpec: &sqlgraph.FieldSpec{ 398 | Type: field.TypeInt, 399 | Column: category.FieldID, 400 | }, 401 | }, 402 | } 403 | _spec.Edges.Clear = append(_spec.Edges.Clear, edge) 404 | } 405 | if nodes := uuo.mutation.RemovedAdministeredIDs(); len(nodes) > 0 && !uuo.mutation.AdministeredCleared() { 406 | edge := &sqlgraph.EdgeSpec{ 407 | Rel: sqlgraph.O2M, 408 | Inverse: true, 409 | Table: user.AdministeredTable, 410 | Columns: []string{user.AdministeredColumn}, 411 | Bidi: false, 412 | Target: &sqlgraph.EdgeTarget{ 413 | IDSpec: &sqlgraph.FieldSpec{ 414 | Type: field.TypeInt, 415 | Column: category.FieldID, 416 | }, 417 | }, 418 | } 419 | for _, k := range nodes { 420 | edge.Target.Nodes = append(edge.Target.Nodes, k) 421 | } 422 | _spec.Edges.Clear = append(_spec.Edges.Clear, edge) 423 | } 424 | if nodes := uuo.mutation.AdministeredIDs(); len(nodes) > 0 { 425 | edge := &sqlgraph.EdgeSpec{ 426 | Rel: sqlgraph.O2M, 427 | Inverse: true, 428 | Table: user.AdministeredTable, 429 | Columns: []string{user.AdministeredColumn}, 430 | Bidi: false, 431 | Target: &sqlgraph.EdgeTarget{ 432 | IDSpec: &sqlgraph.FieldSpec{ 433 | Type: field.TypeInt, 434 | Column: category.FieldID, 435 | }, 436 | }, 437 | } 438 | for _, k := range nodes { 439 | edge.Target.Nodes = append(edge.Target.Nodes, k) 440 | } 441 | _spec.Edges.Add = append(_spec.Edges.Add, edge) 442 | } 443 | _node = &User{config: uuo.config} 444 | _spec.Assign = _node.assignValues 445 | _spec.ScanValues = _node.scanValues 446 | if err = sqlgraph.UpdateNode(ctx, uuo.driver, _spec); err != nil { 447 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 448 | err = &NotFoundError{user.Label} 449 | } else if sqlgraph.IsConstraintError(err) { 450 | err = &ConstraintError{msg: err.Error(), wrap: err} 451 | } 452 | return nil, err 453 | } 454 | uuo.mutation.done = true 455 | return _node, nil 456 | } 457 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rotemtam/ent-grpc-example 2 | 3 | go 1.19 4 | 5 | require ( 6 | entgo.io/contrib v0.3.6-0.20230216210532-54eb1286ff70 7 | entgo.io/ent v0.11.6 8 | github.com/mattn/go-sqlite3 v1.14.16 9 | google.golang.org/grpc v1.52.3 10 | google.golang.org/protobuf v1.28.1 11 | ) 12 | 13 | require ( 14 | ariga.io/atlas v0.9.1-0.20230119123307-a3ab6808892b // indirect 15 | github.com/agext/levenshtein v1.2.1 // indirect 16 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 17 | github.com/go-openapi/inflect v0.19.0 // indirect 18 | github.com/golang/protobuf v1.5.2 // indirect 19 | github.com/google/go-cmp v0.5.9 // indirect 20 | github.com/google/uuid v1.3.0 // indirect 21 | github.com/hashicorp/hcl/v2 v2.13.0 // indirect 22 | github.com/jhump/protoreflect v1.10.1 // indirect 23 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect 24 | github.com/mitchellh/mapstructure v1.5.0 // indirect 25 | github.com/zclconf/go-cty v1.8.0 // indirect 26 | go.uber.org/atomic v1.10.0 // indirect 27 | go.uber.org/multierr v1.9.0 // indirect 28 | golang.org/x/mod v0.7.0 // indirect 29 | golang.org/x/net v0.4.0 // indirect 30 | golang.org/x/sys v0.3.0 // indirect 31 | golang.org/x/text v0.5.0 // indirect 32 | golang.org/x/tools v0.4.0 // indirect 33 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | ariga.io/atlas v0.9.1-0.20230119123307-a3ab6808892b h1:f1868Z/5iWzfVMgjOBwjjP/mRCxOSbXtAl+9DAYb4kg= 2 | ariga.io/atlas v0.9.1-0.20230119123307-a3ab6808892b/go.mod h1:T230JFcENj4ZZzMkZrXFDSkv+2kXkUgpJ5FQQ5hMcKU= 3 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 | entgo.io/contrib v0.3.6-0.20230216210532-54eb1286ff70 h1:qW1xZRB5KgIm0BZsXT5sV9tnwAVbGWkpWtjXN2JJ+7w= 5 | entgo.io/contrib v0.3.6-0.20230216210532-54eb1286ff70/go.mod h1:R5HiFszVD8OVOZKFGRbqYogRxK7z1ruzWyEEesjQwE0= 6 | entgo.io/ent v0.11.6 h1:fMQwhuzbPv12AXdrAGyHoOcgh9r0D9F8WEsCRoUWxVc= 7 | entgo.io/ent v0.11.6/go.mod h1:d4yUWiwY3NQtjGvINzAhUyypopfeEKOxcxLN7D5yM7o= 8 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 9 | github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= 10 | github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= 11 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 12 | github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= 13 | github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= 14 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 15 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 16 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 19 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 20 | github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= 21 | github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= 22 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 23 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 24 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 25 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 27 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 28 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 29 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 30 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 31 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 32 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 33 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 34 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 35 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 36 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 37 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 38 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 39 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 40 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 41 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 42 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 43 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 44 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 45 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 46 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 47 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 48 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 49 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 50 | github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= 51 | github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= 52 | github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= 53 | github.com/jhump/protoreflect v1.10.1 h1:iH+UZfsbRE6vpyZH7asAjTPWJf7RJbpZ9j/N3lDlKs0= 54 | github.com/jhump/protoreflect v1.10.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= 55 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 56 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 57 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 58 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 59 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 60 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 61 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= 62 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 63 | github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= 64 | github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 65 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= 66 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 67 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 68 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 69 | github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= 70 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 71 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 72 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 73 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 74 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 75 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 76 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 77 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 78 | github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= 79 | github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 80 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 81 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 82 | github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= 83 | github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= 84 | go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= 85 | go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 86 | go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= 87 | go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= 88 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 89 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 90 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 91 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 92 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 93 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 94 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 95 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 96 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 97 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 98 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 99 | golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= 100 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 101 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 102 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 103 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 104 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 105 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 106 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 107 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 108 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 109 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 110 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 111 | golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= 112 | golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 113 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 114 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 115 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 116 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 117 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 118 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 119 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 120 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 121 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 122 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 123 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 124 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 125 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 126 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 127 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 128 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 129 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 130 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 131 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 132 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 133 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 134 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 135 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 136 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 137 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 138 | golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 139 | golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 140 | golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= 141 | golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= 142 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 143 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 144 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 145 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 146 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 147 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 148 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 149 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 150 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 151 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= 152 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= 153 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 154 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 155 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 156 | google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ= 157 | google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= 158 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 159 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 160 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 161 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 162 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 163 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 164 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 165 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 166 | google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 167 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 168 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 169 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 170 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 171 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 172 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 173 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 174 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 175 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 176 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 177 | -------------------------------------------------------------------------------- /pb_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/rotemtam/ent-grpc-example/ent/proto/entpb" 7 | ) 8 | 9 | func TestUserProto(t *testing.T) { 10 | user := entpb.User{ 11 | Name: "rotemtam", 12 | EmailAddress: "rotemtam@example.com", 13 | } 14 | if user.GetName() != "rotemtam" { 15 | t.Fatal("expected user name to be rotemtam") 16 | } 17 | if user.GetEmailAddress() != "rotemtam@example.com" { 18 | t.Fatal("expected email address to be rotemtam@example.com") 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /service_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | _ "github.com/mattn/go-sqlite3" 8 | 9 | "github.com/rotemtam/ent-grpc-example/ent/category" 10 | "github.com/rotemtam/ent-grpc-example/ent/enttest" 11 | "github.com/rotemtam/ent-grpc-example/ent/proto/entpb" 12 | "github.com/rotemtam/ent-grpc-example/ent/user" 13 | ) 14 | 15 | func TestServiceWithEdges(t *testing.T) { 16 | // start by initializing an ent client connected to an in memory sqlite instance 17 | ctx := context.Background() 18 | client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") 19 | defer client.Close() 20 | 21 | // next, initialize the UserService. Notice we won't be opening an actual port and creating a gRPC server 22 | // and instead we are just calling the library code directly. 23 | svc := entpb.NewUserService(client) 24 | 25 | // next, we create a category directly using the ent client. notice we are initializing it with no relation 26 | // to a User. 27 | cat := client.Category.Create().SetName("cat_1").SaveX(ctx) 28 | 29 | // next, we invoke the User service's `Create` method. Notice we are passing a list of entpb.Category 30 | // instances with only the ID set. 31 | create, err := svc.Create(ctx, &entpb.CreateUserRequest{ 32 | User: &entpb.User{ 33 | Name: "user", 34 | EmailAddress: "user@service.code", 35 | Administered: []*entpb.Category{ 36 | {Id: int64(cat.ID)}, 37 | }, 38 | }, 39 | }) 40 | if err != nil { 41 | t.Fatal("failed creating user using UserService", err) 42 | } 43 | 44 | // to verify everything worked correctly, we query the category table to check we have exactly 45 | // one category which is administered by the created user. 46 | count, err := client.Category. 47 | Query(). 48 | Where( 49 | category.HasAdminWith( 50 | user.ID(int(create.Id)), 51 | ), 52 | ). 53 | Count(ctx) 54 | if err != nil { 55 | t.Fatal("failed counting categories admin by created user", err) 56 | } 57 | if count != 1 { 58 | t.Fatal("expected exactly one group to managed by the created user") 59 | } 60 | } 61 | 62 | func TestGet(t *testing.T) { 63 | // start by initializing an ent client connected to an in memory sqlite instance 64 | ctx := context.Background() 65 | client := enttest.Open(t, "sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") 66 | defer client.Close() 67 | 68 | // next, initialize the UserService. Notice we won't be opening an actual port and creating a gRPC server 69 | // and instead we are just calling the library code directly. 70 | svc := entpb.NewUserService(client) 71 | 72 | // next, create a user, a category and set that user to be the admin of the category 73 | user := client.User.Create(). 74 | SetName("rotemtam"). 75 | SetEmailAddress("r@entgo.io"). 76 | SaveX(ctx) 77 | 78 | client.Category.Create(). 79 | SetName("category"). 80 | SetAdmin(user). 81 | SaveX(ctx) 82 | 83 | // next, retrieve the user without edge information 84 | get, err := svc.Get(ctx, &entpb.GetUserRequest{ 85 | Id: int64(user.ID), 86 | }) 87 | if err != nil { 88 | t.Fatal("failed retrieving the created user", err) 89 | } 90 | if len(get.Administered) != 0 { 91 | t.Fatal("by default edge information is not supposed to be retrieved") 92 | } 93 | 94 | // next, retrieve the user *WITH* edge information 95 | get, err = svc.Get(ctx, &entpb.GetUserRequest{ 96 | Id: int64(user.ID), 97 | View: entpb.GetUserRequest_WITH_EDGE_IDS, 98 | }) 99 | if err != nil { 100 | t.Fatal("failed retrieving the created user", err) 101 | } 102 | if len(get.Administered) != 1 { 103 | t.Fatal("using WITH_EDGE_IDS edges should be returned") 104 | } 105 | } 106 | --------------------------------------------------------------------------------