snapshot of https://github.com/ent/ent.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Atlas CI Public / lint (push) Failing after 13s
Atlas CI Public / sync (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-15 22:12:36 +05:00
commit 4b3ad0b611
2680 changed files with 719472 additions and 0 deletions

49
schema/edge/annotation.go Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package edge
import "entgo.io/ent/schema"
// Annotation is a builtin schema annotation for
// configuring the edges' behavior in codegen.
type Annotation struct {
// The StructTag option allows overriding the struct-tag
// of the `Edges` field in the generated entity. For example:
//
// edge.Annotation{
// StructTag: `json:"pet_edges"`
// }
//
StructTag string
}
// Name describes the annotation name.
func (Annotation) Name() string {
return "Edges"
}
// Merge implements the schema.Merger interface.
func (a Annotation) Merge(other schema.Annotation) schema.Annotation {
var ant Annotation
switch other := other.(type) {
case Annotation:
ant = other
case *Annotation:
if other != nil {
ant = *other
}
default:
return a
}
if tag := ant.StructTag; tag != "" {
a.StructTag = tag
}
return a
}
var (
_ schema.Annotation = (*Annotation)(nil)
_ schema.Merger = (*Annotation)(nil)
)

276
schema/edge/edge.go Normal file
View File

@@ -0,0 +1,276 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package edge
import (
"reflect"
"entgo.io/ent/schema"
)
// A Descriptor for edge configuration.
type Descriptor struct {
Tag string // struct tag.
Type string // edge type.
Name string // edge name.
Field string // edge field name (e.g. foreign-key).
RefName string // ref name; inverse only.
Ref *Descriptor // edge reference; to/from of the same type.
Through *struct{ N, T string } // through type and name.
Unique bool // unique edge.
Inverse bool // inverse edge.
Required bool // required on creation.
Immutable bool // create only edge.
StorageKey *StorageKey // optional storage-key configuration.
Annotations []schema.Annotation // edge annotations.
Comment string // edge comment.
}
// To defines an association edge between two vertices.
func To(name string, t any) *assocBuilder {
return &assocBuilder{desc: &Descriptor{Name: name, Type: typ(t)}}
}
// From represents a reversed-edge between two vertices that has a back-reference to its source edge.
func From(name string, t any) *inverseBuilder {
return &inverseBuilder{desc: &Descriptor{Name: name, Type: typ(t), Inverse: true}}
}
func typ(t any) string {
if rt := reflect.TypeOf(t); rt.NumIn() > 0 {
return rt.In(0).Name()
}
return ""
}
// assocBuilder is the builder for assoc edges.
type assocBuilder struct {
desc *Descriptor
}
// Unique sets the edge type to be unique. Basically, it limits the edge to be one of the two:
// one2one or one2many. one2one applied if the inverse-edge is also unique.
func (b *assocBuilder) Unique() *assocBuilder {
b.desc.Unique = true
return b
}
// Required indicates that this edge is a required field on creation.
// Unlike fields, edges are optional by default.
func (b *assocBuilder) Required() *assocBuilder {
b.desc.Required = true
return b
}
// Immutable indicates that this edge cannot be updated.
func (b *assocBuilder) Immutable() *assocBuilder {
b.desc.Immutable = true
return b
}
// StructTag sets the struct tag of the assoc edge.
func (b *assocBuilder) StructTag(s string) *assocBuilder {
b.desc.Tag = s
return b
}
// From creates an inverse-edge with the same type.
func (b *assocBuilder) From(name string) *inverseBuilder {
return &inverseBuilder{desc: &Descriptor{Name: name, Type: b.desc.Type, Inverse: true, Ref: b.desc}}
}
// Field is used to bind an edge (with a foreign-key) to a field in the schema.
//
// field.Int("owner_id").
// Optional()
//
// edge.To("owner", User.Type).
// Field("owner_id").
// Unique(),
func (b *assocBuilder) Field(f string) *assocBuilder {
b.desc.Field = f
return b
}
// Through allows setting an "edge schema" to interact explicitly with M2M edges.
//
// edge.To("friends", User.Type).
// Through("friendships", Friendship.Type)
func (b *assocBuilder) Through(name string, t any) *assocBuilder {
b.desc.Through = &struct{ N, T string }{N: name, T: typ(t)}
return b
}
// Comment used to put annotations on the schema.
func (b *assocBuilder) Comment(c string) *assocBuilder {
b.desc.Comment = c
return b
}
// StorageKey sets the storage key of the edge.
//
// edge.To("groups", Group.Type).
// StorageKey(edge.Table("user_groups"), edge.Columns("user_id", "group_id"))
func (b *assocBuilder) StorageKey(opts ...StorageOption) *assocBuilder {
if b.desc.StorageKey == nil {
b.desc.StorageKey = &StorageKey{}
}
for i := range opts {
opts[i](b.desc.StorageKey)
}
return b
}
// Annotations adds a list of annotations to the edge object to be used by
// codegen extensions.
//
// edge.To("pets", Pet.Type).
// Annotations(entgql.Bind())
func (b *assocBuilder) Annotations(annotations ...schema.Annotation) *assocBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Descriptor interface.
func (b *assocBuilder) Descriptor() *Descriptor {
return b.desc
}
// inverseBuilder is the builder for inverse edges.
type inverseBuilder struct {
desc *Descriptor
}
// Ref sets the referenced-edge of this inverse edge.
func (b *inverseBuilder) Ref(ref string) *inverseBuilder {
b.desc.RefName = ref
return b
}
// Unique sets the edge type to be unique. Basically, it limits the edge to be one of the two:
// one-2-one or one-2-many. one-2-one applied if the inverse-edge is also unique.
func (b *inverseBuilder) Unique() *inverseBuilder {
b.desc.Unique = true
return b
}
// Required indicates that this edge is a required field on creation.
// Unlike fields, edges are optional by default.
func (b *inverseBuilder) Required() *inverseBuilder {
b.desc.Required = true
return b
}
// Immutable indicates that this edge cannot be updated.
func (b *inverseBuilder) Immutable() *inverseBuilder {
b.desc.Immutable = true
return b
}
// StructTag sets the struct tag of the inverse edge.
func (b *inverseBuilder) StructTag(s string) *inverseBuilder {
b.desc.Tag = s
return b
}
// Comment used to put annotations on the schema.
func (b *inverseBuilder) Comment(c string) *inverseBuilder {
b.desc.Comment = c
return b
}
// Field is used to bind an edge (with a foreign-key) to a field in the schema.
//
// field.Int("owner_id").
// Optional()
//
// edge.From("owner", User.Type).
// Ref("pets").
// Field("owner_id").
// Unique(),
func (b *inverseBuilder) Field(f string) *inverseBuilder {
b.desc.Field = f
return b
}
// Through allows setting an "edge schema" to interact explicitly with M2M edges.
//
// edge.From("liked_users", User.Type).
// Ref("liked_tweets").
// Through("likes", TweetLike.Type)
func (b *inverseBuilder) Through(name string, t any) *inverseBuilder {
b.desc.Through = &struct{ N, T string }{N: name, T: typ(t)}
return b
}
// Annotations adds a list of annotations to the edge object to be used by
// codegen extensions.
//
// edge.From("owner", User.Type).
// Ref("pets").
// Unique().
// Annotations(entgql.Bind())
func (b *inverseBuilder) Annotations(annotations ...schema.Annotation) *inverseBuilder {
b.desc.Annotations = append(b.desc.Annotations, annotations...)
return b
}
// Descriptor implements the ent.Descriptor interface.
func (b *inverseBuilder) Descriptor() *Descriptor {
return b.desc
}
// StorageKey holds the configuration for edge storage-key.
type StorageKey struct {
Table string // Table or label.
Symbols []string // Symbols/names of the foreign-key constraints.
Columns []string // Foreign-key columns.
}
// StorageOption allows for setting the storage configuration using functional options.
type StorageOption func(*StorageKey)
// Table sets the table name option for M2M edges.
func Table(name string) StorageOption {
return func(key *StorageKey) {
key.Table = name
}
}
// Symbol sets the symbol/name of the foreign-key constraint for O2O, O2M and M2O edges.
// Note that, for M2M edges (2 columns and 2 constraints), use the edge.Symbols option.
func Symbol(symbol string) StorageOption {
return func(key *StorageKey) {
key.Symbols = []string{symbol}
}
}
// Symbols sets the symbol/name of the foreign-key constraints for M2M edges.
// The 1st column defines the name of the "To" edge, and the 2nd defines
// the name of the "From" edge (inverse edge).
// Note that, for O2O, O2M and M2O edges, use the edge.Symbol option.
func Symbols(to, from string) StorageOption {
return func(key *StorageKey) {
key.Symbols = []string{to, from}
}
}
// Column sets the foreign-key column name option for O2O, O2M and M2O edges.
// Note that, for M2M edges (2 columns), use the edge.Columns option.
func Column(name string) StorageOption {
return func(key *StorageKey) {
key.Columns = []string{name}
}
}
// Columns sets the foreign-key column names option for M2M edges.
// The 1st column defines the name of the "To" edge, and the 2nd defines
// the name of the "From" edge (inverse edge).
// Note that, for O2O, O2M and M2O edges, use the edge.Column option.
func Columns(to, from string) StorageOption {
return func(key *StorageKey) {
key.Columns = []string{to, from}
}
}

129
schema/edge/edge_test.go Normal file
View File

@@ -0,0 +1,129 @@
// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package edge_test
import (
"testing"
"entgo.io/ent"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEdge(t *testing.T) {
assert := assert.New(t)
type User struct{ ent.Schema }
e := edge.To("friends", User.Type).
Required().
Comment("comment").
Descriptor()
assert.False(e.Inverse)
assert.Equal("comment", e.Comment)
assert.Equal("User", e.Type)
assert.Equal("friends", e.Name)
assert.True(e.Required)
type Node struct{ ent.Schema }
e = edge.To("parent", Node.Type).
Unique().
Immutable().
Descriptor()
assert.False(e.Inverse)
assert.True(e.Unique)
assert.Equal("Node", e.Type)
assert.Equal("parent", e.Name)
assert.False(e.Required)
assert.True(e.Immutable)
e = edge.To("children", Node.Type).
From("parent").
Unique().
Comment("comment").
Field("parent_id").
Descriptor()
assert.Equal("parent_id", e.Field)
assert.Equal("comment", e.Comment)
assert.Empty(e.Ref.Field)
t.Log("m2m relation of the same type")
from := edge.To("following", User.Type).
From("followers").
Descriptor()
assert.True(from.Inverse)
assert.False(from.Unique)
assert.Equal("followers", from.Name)
assert.NotNil(from.Ref)
assert.Equal("following", from.Ref.Name)
assert.False(from.Ref.Unique)
t.Log("o2m relation of the same type")
from = edge.To("following", User.Type).
Unique().
From("followers").
Descriptor()
assert.False(from.Unique)
assert.True(from.Ref.Unique)
from = edge.To("following", User.Type).
From("followers").
Unique().
Descriptor()
assert.True(from.Unique)
assert.False(from.Ref.Unique)
t.Log("o2o relation of the same type")
from = edge.To("following", User.Type).
Unique().
From("followers").
Unique().
Descriptor()
assert.True(from.Unique)
assert.True(from.Ref.Unique)
e = edge.To("user", User.Type).
StructTag(`json:"user_name,omitempty"`).
Descriptor()
assert.Equal(`json:"user_name,omitempty"`, e.Tag)
from = edge.To("following", User.Type).
StructTag("following").
StorageKey(edge.Table("user_followers"), edge.Columns("following_id", "followers_id"), edge.Symbol("users_followers")).
From("followers").
StructTag("followers").
Descriptor()
assert.Equal("followers", from.Tag)
assert.Equal("following", from.Ref.Tag)
assert.Equal(edge.StorageKey{Table: "user_followers", Symbols: []string{"users_followers"}, Columns: []string{"following_id", "followers_id"}}, *from.Ref.StorageKey)
}
type GQL struct {
Field string
}
func (GQL) Name() string {
return "GQL"
}
func TestAnnotations(t *testing.T) {
type User struct{ ent.Schema }
to := edge.To("user", User.Type).
Annotations(GQL{Field: "to"}).
Descriptor()
require.Equal(t, []schema.Annotation{GQL{Field: "to"}}, to.Annotations)
from := edge.From("user", User.Type).
Annotations(GQL{Field: "from"}).
Descriptor()
require.Equal(t, []schema.Annotation{GQL{Field: "from"}}, from.Annotations)
bidi := edge.To("following", User.Type).
Annotations(GQL{Field: "to"}).
From("followers").
Annotations(GQL{Field: "from"}).
Descriptor()
require.Equal(t, []schema.Annotation{GQL{Field: "from"}}, bidi.Annotations)
require.Equal(t, []schema.Annotation{GQL{Field: "to"}}, bidi.Ref.Annotations)
}