entc/gen: merge ivanvanderbyl:add-json-type-to-entql (#1112)

* Generate JSON types for entql

* Correctly normalize name of json type in generated code

* Override interface types for entql wheres

* Actually call correct method

* Implement better interface name stringer

* Define JsonP in template

* Cleanup and fix tests

* Remove extra json predicates

* Remove JSON predicates and use BytesP

* Regenerate

* Update entc/gen/template/dialect/sql/entql.tmpl

Co-authored-by: Ariel Mashraki <7413593+a8m@users.noreply.github.com>

* Update entql/internal/gen.go

Co-authored-by: Ariel Mashraki <7413593+a8m@users.noreply.github.com>

* Fix comment

* all: regenerate assets

Co-authored-by: Ivan Vanderbyl <ivanvanderbyl@gmail.com>
Co-authored-by: Ivan Vanderbyl <ivanvanderbyl@users.noreply.github.com>
This commit is contained in:
Ariel Mashraki
2021-01-01 16:06:10 +02:00
committed by GitHub
parent 4a1ac1eef1
commit 069793dd03
13 changed files with 197 additions and 11 deletions

File diff suppressed because one or more lines are too long

View File

@@ -118,7 +118,10 @@ type predicateAdder interface {
{{ range $f := $n.Fields }}
{{ $type := $f.Type.Type.String }}
{{ $iface := print (pascal $type) "P" }}
{{ if $f.IsTime }}{{ $iface = "TimeP" }}{{ else if $f.IsBytes }}{{ $iface = "BytesP" }}{{ else if $f.IsUUID }}{{ $iface = "ValueP" }}{{ end }}
{{- if $f.IsTime }}{{ $iface = "TimeP" }}
{{- else if or $f.IsBytes $f.IsJSON }}{{ $iface = "BytesP" }}
{{- else if $f.IsUUID }}{{ $iface = "ValueP" }}
{{- end }}
// Where{{ $f.StructField }} applies the entql {{ $type }} predicate on the {{ $f.Name }} field.
func (f *{{ $filter }}) Where{{ $f.StructField }}(p entql.{{ $iface }}) {
f.Where(p.Field({{ $n.Package }}.{{ $f.Constant }}))

View File

@@ -60,7 +60,8 @@ var schemaGraph = func() *sqlgraph.Schema {
},
Type: "User",
Fields: map[string]*sqlgraph.FieldSpec{
user.FieldName: {Type: field.TypeString, Column: user.FieldName},
user.FieldName: {Type: field.TypeString, Column: user.FieldName},
user.FieldFoods: {Type: field.TypeJSON, Column: user.FieldFoods},
},
}
graph.MustAddE(
@@ -280,6 +281,11 @@ func (f *UserFilter) WhereName(p entql.StringP) {
f.Where(p.Field(user.FieldName))
}
// WhereFoods applies the entql json.RawMessage predicate on the foods field.
func (f *UserFilter) WhereFoods(p entql.BytesP) {
f.Where(p.Field(user.FieldFoods))
}
// WhereHasTenant applies a predicate to check if query has an edge tenant.
func (f *UserFilter) WhereHasTenant() {
f.Where(entql.HasEdge("tenant"))

View File

@@ -9,4 +9,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
const Schema = `{"Schema":"github.com/facebook/ent/examples/privacytenant/ent/schema","Package":"github.com/facebook/ent/examples/privacytenant/ent","Schemas":[{"name":"Group","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"users","type":"User","ref_name":"groups","inverse":true}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"Tenant","config":{"Table":""},"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"groups","type":"Group"}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1}]}],"Features":["privacy","entql","schema/snapshot"]}`
const Schema = `{"Schema":"github.com/facebook/ent/examples/privacytenant/ent/schema","Package":"github.com/facebook/ent/examples/privacytenant/ent","Schemas":[{"name":"Group","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"users","type":"User","ref_name":"groups","inverse":true}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"Tenant","config":{"Table":""},"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"validators":1,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":false,"MixinIndex":0}]},{"name":"User","config":{"Table":""},"edges":[{"name":"tenant","type":"Tenant","unique":true,"required":true},{"name":"groups","type":"Group"}],"fields":[{"name":"name","type":{"Type":7,"Ident":"","PkgPath":"","Nillable":false,"RType":null},"default":true,"default_value":"Unknown","default_kind":24,"position":{"Index":0,"MixedIn":false,"MixinIndex":0}},{"name":"foods","type":{"Type":3,"Ident":"[]string","PkgPath":"","Nillable":true,"RType":null},"optional":true,"position":{"Index":1,"MixedIn":false,"MixinIndex":0}}],"policy":[{"Index":0,"MixedIn":true,"MixinIndex":0},{"Index":0,"MixedIn":true,"MixinIndex":1}]}],"Features":["privacy","entql","schema/snapshot"]}`

View File

@@ -49,6 +49,7 @@ var (
UsersColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "name", Type: field.TypeString, Default: "Unknown"},
{Name: "foods", Type: field.TypeJSON, Nullable: true},
{Name: "user_tenant", Type: field.TypeInt, Nullable: true},
}
// UsersTable holds the schema information for the "users" table.
@@ -59,7 +60,7 @@ var (
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "users_tenants_tenant",
Columns: []*schema.Column{UsersColumns[2]},
Columns: []*schema.Column{UsersColumns[3]},
RefColumns: []*schema.Column{TenantsColumns[0]},
OnDelete: schema.SetNull,

View File

@@ -780,6 +780,7 @@ type UserMutation struct {
typ string
id *int
name *string
foods *[]string
clearedFields map[string]struct{}
tenant *int
clearedtenant bool
@@ -907,6 +908,56 @@ func (m *UserMutation) ResetName() {
m.name = nil
}
// SetFoods sets the foods field.
func (m *UserMutation) SetFoods(s []string) {
m.foods = &s
}
// Foods returns the foods value in the mutation.
func (m *UserMutation) Foods() (r []string, exists bool) {
v := m.foods
if v == nil {
return
}
return *v, true
}
// OldFoods returns the old foods value of the User.
// If the User object wasn't provided to the builder, the object is fetched
// from the database.
// An error is returned if the mutation operation is not UpdateOne, or database query fails.
func (m *UserMutation) OldFoods(ctx context.Context) (v []string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldFoods is allowed only on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldFoods requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldFoods: %w", err)
}
return oldValue.Foods, nil
}
// ClearFoods clears the value of foods.
func (m *UserMutation) ClearFoods() {
m.foods = nil
m.clearedFields[user.FieldFoods] = struct{}{}
}
// FoodsCleared returns if the field foods was cleared in this mutation.
func (m *UserMutation) FoodsCleared() bool {
_, ok := m.clearedFields[user.FieldFoods]
return ok
}
// ResetFoods reset all changes of the "foods" field.
func (m *UserMutation) ResetFoods() {
m.foods = nil
delete(m.clearedFields, user.FieldFoods)
}
// SetTenantID sets the tenant edge to Tenant by id.
func (m *UserMutation) SetTenantID(id int) {
m.tenant = &id
@@ -1013,10 +1064,13 @@ func (m *UserMutation) Type() string {
// this mutation. Note that, in order to get all numeric
// fields that were in/decremented, call AddedFields().
func (m *UserMutation) Fields() []string {
fields := make([]string, 0, 1)
fields := make([]string, 0, 2)
if m.name != nil {
fields = append(fields, user.FieldName)
}
if m.foods != nil {
fields = append(fields, user.FieldFoods)
}
return fields
}
@@ -1027,6 +1081,8 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) {
switch name {
case user.FieldName:
return m.Name()
case user.FieldFoods:
return m.Foods()
}
return nil, false
}
@@ -1038,6 +1094,8 @@ func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, er
switch name {
case user.FieldName:
return m.OldName(ctx)
case user.FieldFoods:
return m.OldFoods(ctx)
}
return nil, fmt.Errorf("unknown User field %s", name)
}
@@ -1054,6 +1112,13 @@ func (m *UserMutation) SetField(name string, value ent.Value) error {
}
m.SetName(v)
return nil
case user.FieldFoods:
v, ok := value.([]string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetFoods(v)
return nil
}
return fmt.Errorf("unknown User field %s", name)
}
@@ -1083,7 +1148,11 @@ func (m *UserMutation) AddField(name string, value ent.Value) error {
// ClearedFields returns all nullable fields that were cleared
// during this mutation.
func (m *UserMutation) ClearedFields() []string {
return nil
var fields []string
if m.FieldCleared(user.FieldFoods) {
fields = append(fields, user.FieldFoods)
}
return fields
}
// FieldCleared returns a boolean indicates if this field was
@@ -1096,6 +1165,11 @@ func (m *UserMutation) FieldCleared(name string) bool {
// ClearField clears the value for the given name. It returns an
// error if the field is not defined in the schema.
func (m *UserMutation) ClearField(name string) error {
switch name {
case user.FieldFoods:
m.ClearFoods()
return nil
}
return fmt.Errorf("unknown User nullable field %s", name)
}
@@ -1107,6 +1181,9 @@ func (m *UserMutation) ResetField(name string) error {
case user.FieldName:
m.ResetName()
return nil
case user.FieldFoods:
m.ResetFoods()
return nil
}
return fmt.Errorf("unknown User field %s", name)
}

View File

@@ -28,6 +28,8 @@ func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Default("Unknown"),
field.Strings("foods").
Optional(),
}
}

View File

@@ -7,6 +7,7 @@
package ent
import (
"encoding/json"
"fmt"
"strings"
@@ -22,6 +23,8 @@ type User struct {
ID int `json:"id,omitempty"`
// Name holds the value of the "name" field.
Name string `json:"name,omitempty"`
// Foods holds the value of the "foods" field.
Foods []string `json:"foods,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the UserQuery when eager-loading is set.
Edges UserEdges `json:"edges"`
@@ -67,6 +70,8 @@ func (*User) scanValues(columns []string) ([]interface{}, error) {
values := make([]interface{}, len(columns))
for i := range columns {
switch columns[i] {
case user.FieldFoods:
values[i] = &[]byte{}
case user.FieldID:
values[i] = &sql.NullInt64{}
case user.FieldName:
@@ -100,6 +105,15 @@ func (u *User) assignValues(columns []string, values []interface{}) error {
} else if value.Valid {
u.Name = value.String
}
case user.FieldFoods:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field foods", values[i])
} else if value != nil && len(*value) > 0 {
if err := json.Unmarshal(*value, &u.Foods); err != nil {
return fmt.Errorf("unmarshal field foods: %v", err)
}
}
case user.ForeignKeys[0]:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for edge-field user_tenant", value)
@@ -147,6 +161,8 @@ func (u *User) String() string {
builder.WriteString(fmt.Sprintf("id=%v", u.ID))
builder.WriteString(", name=")
builder.WriteString(u.Name)
builder.WriteString(", foods=")
builder.WriteString(fmt.Sprintf("%v", u.Foods))
builder.WriteByte(')')
return builder.String()
}

View File

@@ -17,6 +17,8 @@ const (
FieldID = "id"
// FieldName holds the string denoting the name field in the database.
FieldName = "name"
// FieldFoods holds the string denoting the foods field in the database.
FieldFoods = "foods"
// EdgeTenant holds the string denoting the tenant edge name in mutations.
EdgeTenant = "tenant"
@@ -43,6 +45,7 @@ const (
var Columns = []string{
FieldID,
FieldName,
FieldFoods,
}
// ForeignKeys holds the SQL foreign-keys that are owned by the User type.

View File

@@ -213,6 +213,20 @@ func NameContainsFold(v string) predicate.User {
})
}
// FoodsIsNil applies the IsNil predicate on the "foods" field.
func FoodsIsNil() predicate.User {
return predicate.User(func(s *sql.Selector) {
s.Where(sql.IsNull(s.C(FieldFoods)))
})
}
// FoodsNotNil applies the NotNil predicate on the "foods" field.
func FoodsNotNil() predicate.User {
return predicate.User(func(s *sql.Selector) {
s.Where(sql.NotNull(s.C(FieldFoods)))
})
}
// HasTenant applies the HasEdge predicate on the "tenant" edge.
func HasTenant() predicate.User {
return predicate.User(func(s *sql.Selector) {

View File

@@ -39,6 +39,12 @@ func (uc *UserCreate) SetNillableName(s *string) *UserCreate {
return uc
}
// SetFoods sets the foods field.
func (uc *UserCreate) SetFoods(s []string) *UserCreate {
uc.mutation.SetFoods(s)
return uc
}
// SetTenantID sets the tenant edge to Tenant by id.
func (uc *UserCreate) SetTenantID(id int) *UserCreate {
uc.mutation.SetTenantID(id)
@@ -166,6 +172,14 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
})
_node.Name = value
}
if value, ok := uc.mutation.Foods(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeJSON,
Value: value,
Column: user.FieldFoods,
})
_node.Foods = value
}
if nodes := uc.mutation.TenantIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,

View File

@@ -47,6 +47,18 @@ func (uu *UserUpdate) SetNillableName(s *string) *UserUpdate {
return uu
}
// SetFoods sets the foods field.
func (uu *UserUpdate) SetFoods(s []string) *UserUpdate {
uu.mutation.SetFoods(s)
return uu
}
// ClearFoods clears the value of foods.
func (uu *UserUpdate) ClearFoods() *UserUpdate {
uu.mutation.ClearFoods()
return uu
}
// SetTenantID sets the tenant edge to Tenant by id.
func (uu *UserUpdate) SetTenantID(id int) *UserUpdate {
uu.mutation.SetTenantID(id)
@@ -195,6 +207,19 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
Column: user.FieldName,
})
}
if value, ok := uu.mutation.Foods(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeJSON,
Value: value,
Column: user.FieldFoods,
})
}
if uu.mutation.FoodsCleared() {
_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
Type: field.TypeJSON,
Column: user.FieldFoods,
})
}
if uu.mutation.TenantCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
@@ -316,6 +341,18 @@ func (uuo *UserUpdateOne) SetNillableName(s *string) *UserUpdateOne {
return uuo
}
// SetFoods sets the foods field.
func (uuo *UserUpdateOne) SetFoods(s []string) *UserUpdateOne {
uuo.mutation.SetFoods(s)
return uuo
}
// ClearFoods clears the value of foods.
func (uuo *UserUpdateOne) ClearFoods() *UserUpdateOne {
uuo.mutation.ClearFoods()
return uuo
}
// SetTenantID sets the tenant edge to Tenant by id.
func (uuo *UserUpdateOne) SetTenantID(id int) *UserUpdateOne {
uuo.mutation.SetTenantID(id)
@@ -462,6 +499,19 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
Column: user.FieldName,
})
}
if value, ok := uuo.mutation.Foods(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeJSON,
Value: value,
Column: user.FieldFoods,
})
}
if uuo.mutation.FoodsCleared() {
_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
Type: field.TypeJSON,
Column: user.FieldFoods,
})
}
if uuo.mutation.TenantCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,

View File

@@ -35,8 +35,8 @@ func Example_PrivacyTenant() {
// Output:
// Tenant(id=1, name=GitHub)
// Tenant(id=2, name=GitLab)
// User(id=1, name=a8m)
// User(id=2, name=nati)
// User(id=1, name=a8m, foods=[])
// User(id=2, name=nati, foods=[Sushi Burritos])
// Group(id=1, name=entgo.io)
}
@@ -66,7 +66,7 @@ func Do(ctx context.Context, client *ent.Client) error {
// Create 2 users connected to the 2 tenants we created above (a8m->GitHub, nati->GitLab).
a8m := client.User.Create().SetName("a8m").SetTenant(hub).SaveX(admin)
nati := client.User.Create().SetName("nati").SetTenant(lab).SaveX(admin)
nati := client.User.Create().SetName("nati").SetTenant(lab).SetFoods([]string{"Sushi", "Burritos"}).SaveX(admin)
hubView := viewer.NewContext(ctx, viewer.UserViewer{T: hub})
out := client.User.Query().OnlyX(hubView)