github.com/woocoos/entcache@v0.0.0-20231206055445-856f0148efa5/gen/template/client.tmpl (about) 1 {{/* 2 Copyright 2019-present Facebook Inc. All rights reserved. 3 This source code is licensed under the Apache 2.0 license found 4 in the LICENSE file in the root directory of this source tree. 5 */}} 6 7 {{/* gotype: entgo.io/ent/entc/gen.Graph */}} 8 9 {{ define "client" }} 10 11 {{ $pkg := base $.Config.Package }} 12 {{ template "header" $ }} 13 14 {{/* Additional dependencies injected to config. */}} 15 {{ $deps := list }}{{ with $.Config.Annotations }}{{ $deps = $.Config.Annotations.Dependencies }}{{ end }} 16 17 import ( 18 "log" 19 20 "{{ $.Config.Package }}/migrate" 21 {{ range $n := $.Nodes }} 22 {{ $n.PackageAlias }} "{{ $n.Config.Package }}/{{ $n.PackageDir }}" 23 {{- end }} 24 {{- range $dep := $deps }} 25 {{ $dep.Type.PkgName }} "{{ $dep.Type.PkgPath }}" 26 {{- end }} 27 "entgo.io/ent/dialect" 28 "github.com/woocoos/entcache" 29 {{ range $import := $.Storage.Imports -}} 30 "{{ $import }}" 31 {{ end -}} 32 {{- template "import/additional" $ }} 33 ) 34 35 {{ template "client/init" $ }} 36 37 type ( 38 // config is the configuration for the client and its builder. 39 config struct { 40 // driver used for executing database requests. 41 driver dialect.Driver 42 // debug enable a debug logging. 43 debug bool 44 // log used for logging on debug mode. 45 log func(...any) 46 // hooks to execute on mutations. 47 hooks *hooks 48 // interceptors to execute on queries. 49 inters *inters 50 {{- /* Additional dependency fields. */}} 51 {{- range $dep := $deps }} 52 {{ $dep.Field }} {{ $dep.Type }} 53 {{- end }} 54 {{- /* Support adding config fields from both global or dialect-specific templates. */}} 55 {{- range $prefix := list "" (printf "dialect/%s/" $.Storage) }} 56 {{- with $tmpls := matchTemplate (print $prefix "config/fields/*") }} 57 {{- range $tmpl := $tmpls }} 58 {{ xtemplate $tmpl $ }} 59 {{- end }} 60 {{- end }} 61 {{- end }} 62 } 63 // Option function to configure the client. 64 Option func(*config) 65 ) 66 67 // newConfig creates a new config for the client. 68 func newConfig(opts ...Option) config { 69 cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}} 70 {{- with $tmpls := matchTemplate "config/init/fields/*" }} 71 {{- range $tmpl := $tmpls }} 72 {{- xtemplate $tmpl $ }} 73 {{- end }} 74 {{- end }} 75 cfg.options(opts...) 76 return cfg 77 } 78 79 // options applies the options on the config object. 80 func (c *config) options(opts ...Option) { 81 for _, opt := range opts { 82 opt(c) 83 } 84 if c.debug { 85 c.driver = dialect.Debug(c.driver, c.log) 86 } 87 } 88 89 // Debug enables debug logging on the ent.Driver. 90 func Debug() Option { 91 return func(c *config) { 92 c.debug = true 93 } 94 } 95 96 // Log sets the logging function for debug mode. 97 func Log(fn func(...any)) Option { 98 return func(c *config) { 99 c.log = fn 100 } 101 } 102 103 // Driver configures the client driver. 104 func Driver(driver dialect.Driver) Option { 105 return func(c *config) { 106 c.driver = driver 107 } 108 } 109 110 {{- /* Additional dependency options reside on the config object. */}} 111 {{- range $dep := $deps }} 112 // {{ $dep.Option }} configures the {{ $dep.Field }}. 113 func {{ $dep.Option }}(v {{ $dep.Type }}) Option { 114 return func(c *config) { 115 c.{{ $dep.Field }} = v 116 } 117 } 118 {{- end }} 119 120 // Open opens a database/sql.DB specified by the driver name and 121 // the data source name, and returns a new client attached to it. 122 // Optional parameters can be added for configuring the client. 123 func Open(driverName, dataSourceName string, options ...Option) (*Client, error) { 124 switch driverName { 125 case {{ join $.Storage.Dialects ", " }}: 126 {{- $tmpl := printf "dialect/%s/client/open" $.Storage -}} 127 {{- xtemplate $tmpl . -}} 128 default: 129 return nil, fmt.Errorf("unsupported driver: %q", driverName) 130 } 131 } 132 133 // ErrTxStarted is returned when trying to start a new transaction from a transactional client. 134 var ErrTxStarted = errors.New("{{ $pkg }}: cannot start a transaction within a transaction") 135 136 // Tx returns a new transactional client. The provided context 137 // is used until the transaction is committed or rolled back. 138 func (c *Client) Tx(ctx context.Context) (*Tx, error) { 139 if _, ok := c.driver.(*txDriver); ok { 140 return nil, ErrTxStarted 141 } 142 tx, err := newTx(ctx, c.driver) 143 if err != nil { 144 return nil, fmt.Errorf("{{ $pkg }}: starting a transaction: %w", err) 145 } 146 cfg := c.config 147 cfg.driver = tx 148 return &Tx{ 149 ctx: ctx, 150 config: cfg, 151 {{- range $n := $.Nodes }} 152 {{ $n.Name }}: New{{ $n.ClientName }}(cfg), 153 {{- end }} 154 }, nil 155 } 156 157 {{- /* If the storage driver supports TxOptions (like SQL) */}} 158 {{- $tmpl = printf "dialect/%s/txoptions" $.Storage }} 159 {{- if hasTemplate $tmpl }} 160 {{- xtemplate $tmpl . }} 161 {{- end }} 162 163 // Debug returns a new debug-client. It's used to get verbose logging on specific operations. 164 // 165 // client.Debug(). 166 // {{ (index $.Nodes 0).Name }}. 167 // Query(). 168 // Count(ctx) 169 // 170 func (c *Client) Debug() *Client { 171 if c.debug { 172 return c 173 } 174 cfg := c.config 175 cfg.driver = dialect.Debug(c.driver, c.log) 176 client := &Client{config: cfg} 177 client.init() 178 return client 179 } 180 181 // Close closes the database connection and prevents new queries from starting. 182 func (c *Client) Close() error { 183 return c.driver.Close() 184 } 185 186 // Use adds the mutation hooks to all the entity clients. 187 // In order to add hooks to a specific client, call: `client.Node.Use(...)`. 188 func (c *Client) Use(hooks ...Hook) { 189 {{- if lt (len $.Nodes) 6 }} 190 {{- range $n := $.Nodes }} 191 c.{{ $n.Name }}.Use(hooks...) 192 {{- end }} 193 {{- /* Use a for-loop for setting hooks in case there are too many nodes. */}} 194 {{- else }} 195 {{- $hooks := slist }} 196 {{- range $n := $.Nodes }}{{ $hooks = append $hooks (printf "c.%s," $n.Name) }}{{ end }} 197 for _, n := range []interface{ Use(...Hook) }{ 198 {{ joinWords $hooks 80 }} 199 }{ 200 n.Use(hooks...) 201 } 202 {{- end }} 203 } 204 205 // Intercept adds the query interceptors to all the entity clients. 206 // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. 207 func (c *Client) Intercept(interceptors ...Interceptor) { 208 {{- if lt (len $.Nodes) 6 }} 209 {{- range $n := $.Nodes }} 210 c.{{ $n.Name }}.Intercept(interceptors...) 211 {{- end }} 212 {{- /* Use a for-loop for setting interceptors in case there are too many nodes. */}} 213 {{- else }} 214 {{- $inters := slist }} 215 {{- range $n := $.Nodes }}{{ $inters = append $inters (printf "c.%s," $n.Name) }}{{ end }} 216 for _, n := range []interface{ Intercept(...Interceptor) }{ 217 {{ joinWords $inters 80 }} 218 }{ 219 n.Intercept(interceptors...) 220 } 221 {{- end }} 222 } 223 224 {{- with $tmpls := matchTemplate "client/additional/*" "client/additional/*/*" }} 225 {{- range $tmpl := $tmpls }} 226 {{- xtemplate $tmpl $ }} 227 {{- end }} 228 {{- end }} 229 230 231 // Mutate implements the ent.Mutator interface. 232 func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { 233 switch m := m.(type) { 234 {{- range $n := $.Nodes }} 235 case *{{ $n.MutationName }}: 236 return c.{{ $n.Name }}.mutate(ctx, m) 237 {{- end }} 238 default: 239 return nil, fmt.Errorf("{{ $pkg }}: unknown mutation type %T", m) 240 } 241 } 242 243 {{ range $n := $.Nodes }} 244 {{ $client := $n.ClientName }} 245 // {{ $client }} is a client for the {{ $n.Name }} schema. 246 type {{ $client }} struct { 247 config 248 } 249 250 {{ $rec := $n.Receiver }}{{ if eq $rec "c" }}{{ $rec = printf "%.2s" $n.Name | lower }}{{ end }} 251 252 // New{{ $client }} returns a client for the {{ $n.Name }} from the given config. 253 func New{{ $client }}(c config) *{{ $client }} { 254 return &{{ $client }}{config: c} 255 } 256 257 // Use adds a list of mutation hooks to the hooks stack. 258 // A call to `Use(f, g, h)` equals to `{{ $n.Package }}.Hooks(f(g(h())))`. 259 func (c *{{ $client }}) Use(hooks ...Hook) { 260 c.hooks.{{ $n.Name }} = append(c.hooks.{{ $n.Name }}, hooks...) 261 } 262 263 // Intercept adds a list of query interceptors to the interceptors stack. 264 // A call to `Intercept(f, g, h)` equals to `{{ $n.Package }}.Intercept(f(g(h())))`. 265 func (c *{{ $client }}) Intercept(interceptors ...Interceptor) { 266 c.inters.{{ $n.Name }} = append(c.inters.{{ $n.Name }}, interceptors...) 267 } 268 269 // Create returns a builder for creating a {{ $n.Name }} entity. 270 func (c *{{ $client }}) Create() *{{ $n.CreateName }} { 271 mutation := new{{ $n.MutationName }}(c.config, OpCreate) 272 return &{{ $n.CreateName }}{config: c.config, hooks: c.Hooks(), mutation: mutation} 273 } 274 275 // CreateBulk returns a builder for creating a bulk of {{ $n.Name }} entities. 276 func (c *{{ $client }}) CreateBulk(builders ...*{{ $n.CreateName }}) *{{ $n.CreateBulkName }} { 277 return &{{ $n.CreateBulkName }}{config: c.config, builders: builders} 278 } 279 280 // MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates 281 // a builder and applies setFunc on it. 282 func (c *{{ $client }}) MapCreateBulk(slice any, setFunc func(*{{ $n.CreateName }}, int)) *{{ $n.CreateBulkName }} { 283 {{- /* This check should be replaced with type-parameter method, if it will be supported by Go. */}} 284 rv := reflect.ValueOf(slice) 285 if rv.Kind() != reflect.Slice { 286 return &{{ $n.CreateBulkName }}{err: fmt.Errorf("calling to {{ $client }}.MapCreateBulk with wrong type %T, need slice", slice)} 287 } 288 builders := make([]*{{ $n.CreateName }}, rv.Len()) 289 for i := 0; i < rv.Len(); i++ { 290 builders[i] = c.Create() 291 setFunc(builders[i], i) 292 } 293 return &{{ $n.CreateBulkName }}{config: c.config, builders: builders} 294 } 295 296 // Update returns an update builder for {{ $n.Name }}. 297 func (c *{{ $client }}) Update() *{{ $n.UpdateName }} { 298 mutation := new{{ $n.MutationName }}(c.config, OpUpdate) 299 return &{{ $n.UpdateName }}{config: c.config, hooks: c.Hooks(), mutation: mutation} 300 } 301 302 // UpdateOne returns an update builder for the given entity. 303 func (c *{{ $client }}) UpdateOne({{ $rec }} *{{ $n.Name }}) *{{ $n.UpdateOneName }} { 304 {{- if $n.HasOneFieldID }} 305 mutation := new{{ $n.MutationName }}(c.config, OpUpdateOne, {{ print "with" $n.Name }}({{ $rec }})) 306 {{- else }} 307 mutation := new{{ $n.MutationName }}(c.config, OpUpdateOne) 308 {{- range $id := $n.EdgeSchema.ID }} 309 mutation.{{ $id.BuilderField }} = &{{ $rec }}.{{ $id.StructField }} 310 {{- end }} 311 {{- end }} 312 return &{{ $n.UpdateOneName }}{config: c.config, hooks: c.Hooks(), mutation: mutation} 313 } 314 315 {{ with $n.HasOneFieldID }} 316 // UpdateOneID returns an update builder for the given id. 317 func (c *{{ $client }}) UpdateOneID(id {{ $n.ID.Type }}) *{{ $n.UpdateOneName }} { 318 mutation := new{{ $n.MutationName }}(c.config, OpUpdateOne, {{ print "with" $n.Name "ID" }}(id)) 319 return &{{ $n.UpdateOneName }}{config: c.config, hooks: c.Hooks(), mutation: mutation} 320 } 321 {{ end }} 322 323 // Delete returns a delete builder for {{ $n.Name }}. 324 func (c *{{ $client }}) Delete() *{{ $n.DeleteName }} { 325 mutation := new{{ $n.MutationName }}(c.config, OpDelete) 326 return &{{ $n.DeleteName }}{config: c.config, hooks: c.Hooks(), mutation: mutation} 327 } 328 329 {{ with $n.HasOneFieldID }} 330 // DeleteOne returns a builder for deleting the given entity. 331 func (c *{{ $client }}) DeleteOne({{ $rec }} *{{ $n.Name }}) *{{ $n.DeleteOneName }} { 332 return c.DeleteOneID({{ $rec }}.ID) 333 } 334 335 // DeleteOneID returns a builder for deleting the given entity by its id. 336 func (c *{{ $client }}) DeleteOneID(id {{ $n.ID.Type }}) *{{ $n.DeleteOneName }} { 337 {{- $builder := "builder" }}{{ if eq $n.Package $builder }}{{ $builder = "builderC" }}{{ end }} 338 {{ $builder }} := c.Delete().Where({{ $n.Package }}.ID(id)) 339 {{ $builder }}.mutation.id = &id 340 {{ $builder }}.mutation.op = OpDeleteOne 341 return &{{ $n.DeleteOneName }}{ {{ $builder }} } 342 } 343 {{ end }} 344 345 // Query returns a query builder for {{ $n.Name }}. 346 func (c *{{ $client }}) Query() *{{ $n.QueryName }} { 347 return &{{ $n.QueryName }}{ 348 config: c.config, 349 ctx: &QueryContext{Type: {{ $n.TypeName }} }, 350 inters: c.Interceptors(), 351 {{- with $tmpls := matchTemplate (printf "dialect/%s/query/fields/init/*" $.Storage) }} 352 {{- range $tmpl := $tmpls }} 353 {{- xtemplate $tmpl $n }} 354 {{- end }} 355 {{- end }} 356 } 357 } 358 359 {{ with $n.HasOneFieldID }} 360 // Get returns a {{ $n.Name }} entity by its id. 361 func (c *{{ $client }}) Get(ctx context.Context, id {{ $n.ID.Type }}) (*{{ $n.Name }}, error) { 362 return c.Query().Where({{ $n.Package }}.ID(id)).Only(entcache.WithEntryKey(ctx, "{{ $n.Name }}",id)) 363 } 364 365 // GetX is like Get, but panics if an error occurs. 366 func (c *{{ $client }}) GetX(ctx context.Context, id {{ $n.ID.Type }}) *{{ $n.Name }} { 367 obj, err := c.Get(ctx, id) 368 if err != nil { 369 panic(err) 370 } 371 return obj 372 } 373 {{ end }} 374 375 {{ range $e := $n.Edges }} 376 {{ $builder := $e.Type.QueryName }} 377 {{ $arg := $rec }}{{ if eq $arg "id" }}{{ $arg = "node" }}{{ end }} 378 {{ $func := print "Query" (pascal $e.Name) }} 379 // Query{{ pascal $e.Name }} queries the {{ $e.Name }} edge of a {{ $n.Name }}. 380 func (c *{{ $client }}) {{ $func }}({{ $arg }} *{{ $n.Name }}) *{{ $builder }} { 381 {{- if $n.HasOneFieldID }} 382 query := (&{{ $e.Type.ClientName }}{config: c.config}).Query() 383 query.path = func(context.Context) (fromV {{ $.Storage.Builder }}, _ error) { 384 {{- with extend $n "Receiver" $arg "Edge" $e "Ident" "fromV" }} 385 {{ $tmpl := printf "dialect/%s/query/from" $.Storage }} 386 {{- xtemplate $tmpl . -}} 387 {{- end -}} 388 return fromV, nil 389 } 390 return query 391 {{- else }} 392 {{- /* For edge schema, we use the predicate-based approach. */}} 393 return c.Query(). 394 Where({{ range $id := $n.EdgeSchema.ID }}{{ $n.Package }}.{{ $id.StructField }}({{ $arg }}.{{ $id.StructField }}),{{ end }}). 395 {{ $func }}() 396 {{- end }} 397 } 398 {{ end }} 399 400 // Hooks returns the client hooks. 401 func (c *{{ $client }}) Hooks() []Hook { 402 {{- if or $n.NumHooks $n.NumPolicy }} 403 hooks := c.hooks.{{ $n.Name }} 404 return append(hooks[:len(hooks):len(hooks)], {{ $n.Package }}.Hooks[:]...) 405 {{- else }} 406 return c.hooks.{{ $n.Name }} 407 {{- end }} 408 } 409 410 // Interceptors returns the client interceptors. 411 func (c *{{ $client }}) Interceptors() []Interceptor { 412 {{- if $n.NumInterceptors }} 413 inters := c.inters.{{ $n.Name }} 414 return append(inters[:len(inters):len(inters)], {{ $n.Package }}.Interceptors[:]...) 415 {{- else }} 416 return c.inters.{{ $n.Name }} 417 {{- end }} 418 } 419 420 func (c *{{ $client }}) mutate(ctx context.Context, m *{{ $n.MutationName }}) (Value, error) { 421 switch m.Op() { 422 case OpCreate: 423 return (&{{ $n.CreateName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 424 case OpUpdate: 425 return (&{{ $n.UpdateName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 426 case OpUpdateOne: 427 return (&{{ $n.UpdateOneName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) 428 case OpDelete, OpDeleteOne: 429 return (&{{ $n.DeleteName }}{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) 430 default: 431 return nil, fmt.Errorf("{{ $pkg }}: unknown {{ $n.Name }} mutation op: %q", m.Op()) 432 } 433 } 434 {{ end }} 435 436 // hooks and interceptors per client, for fast access. 437 type ( 438 {{- $leni := len $.Nodes | add -1 }} 439 {{- $hooks := slist }} 440 {{- $inters := slist }} 441 {{- range $i, $n := $.Nodes }} 442 {{- if eq $i $leni }} 443 {{- $hooks = append $hooks (printf "%s []ent.Hook" $n.Name) }} 444 {{- $inters = append $inters (printf "%s []ent.Interceptor" $n.Name) }} 445 {{- else }} 446 {{- $hooks = append $hooks (print $n.Name ",") }} 447 {{- $inters = append $inters (print $n.Name ",") }} 448 {{- end }} 449 {{- end }} 450 hooks struct { 451 {{ joinWords $hooks 80 }} 452 } 453 inters struct { 454 {{ joinWords $inters 80 }} 455 } 456 ) 457 458 {{- /* Support adding config options from both global or dialect-specific templates. */}} 459 {{- range $prefix := list "" (printf "dialect/%s/" $.Storage) }} 460 {{- with $tmpls := matchTemplate (print $prefix "config/options/*") }} 461 {{- range $tmpl := $tmpls }} 462 {{ xtemplate $tmpl $ }} 463 {{- end }} 464 {{- end }} 465 {{- end }} 466 467 {{- with $tmpls := matchTemplate "config/additional/*" "config/additional/*/*" }} 468 {{- range $tmpl := $tmpls }} 469 {{- xtemplate $tmpl $ }} 470 {{- end }} 471 {{- end }} 472 {{ end }}