From ffec5027ac7b874830b0557ded6c61da579ecb91 Mon Sep 17 00:00:00 2001 From: Barnaby Keene <1636971+Southclaws@users.noreply.github.com> Date: Wed, 11 Sep 2024 21:46:23 +0100 Subject: [PATCH] add mentions in content and notify members when mentioned in threads (todo: replies) --- api/openapi.yaml | 2 +- app/resources/account/account.go | 5 +- .../account/account_writer/account_writer.go | 4 +- app/resources/account/mapping.go | 4 +- app/resources/account/notification/event.go | 7 +- .../notification/notification_enum_gen.go | 9 +- app/resources/asset/asset.go | 5 +- app/resources/asset/db.go | 5 +- .../{content => datagraph}/content.go | 133 +++- .../{content => datagraph}/content_test.go | 30 +- app/resources/datagraph/indexable.go | 32 +- app/resources/datagraph/ref.go | 61 ++ app/resources/library/library.go | 4 +- app/resources/library/mapping.go | 5 +- app/resources/library/node.go | 9 +- app/resources/library/node_traversal/db.go | 5 +- app/resources/mq/message_types.go | 5 + app/resources/post/post.go | 22 +- app/resources/post/post_writer/post_writer.go | 4 +- app/resources/post/reply/reply.go | 5 +- app/resources/post/reply/repo.go | 4 +- app/resources/post/thread/repo.go | 4 +- app/resources/post/thread/thread.go | 4 +- app/resources/post/thread_writer/writer.go | 4 +- app/resources/profile/profile.go | 22 +- app/resources/seed/post.go | 41 +- app/services/asset/analyse/pdf.go | 14 +- app/services/asset/asset_upload/uploader.go | 3 +- app/services/library/node_mutate/node.go | 6 +- app/services/link/scrape/postprocess.go | 20 +- app/services/link/scrape/scraper.go | 5 +- app/services/mention/mention_job/job.go | 43 ++ app/services/mention/mention_job/mention.go | 33 + app/services/mention/mention_job/provider.go | 22 + app/services/mention/mentioner/mentioner.go | 32 + app/services/reply/service.go | 6 +- app/services/services.go | 2 + app/services/thread/create.go | 3 + app/services/thread/service.go | 10 +- app/transports/http/bindings/nodes.go | 7 +- app/transports/http/bindings/posts.go | 4 +- app/transports/http/bindings/replies.go | 4 +- app/transports/http/bindings/threads.go | 7 +- app/transports/http/bindings/utils.go | 4 +- app/transports/http/openapi/server_gen.go | 298 ++++---- internal/ent/account.go | 30 +- internal/ent/account/account.go | 30 + internal/ent/account/where.go | 23 + internal/ent/account_create.go | 32 + internal/ent/account_query.go | 76 +- internal/ent/account_update.go | 163 ++++ internal/ent/client.go | 223 +++++- internal/ent/ent.go | 2 + internal/ent/er.html | 8 + internal/ent/hook/hook.go | 12 + internal/ent/mentionprofile.go | 175 +++++ internal/ent/mentionprofile/mentionprofile.go | 122 +++ internal/ent/mentionprofile/where.go | 313 ++++++++ internal/ent/mentionprofile_create.go | 642 ++++++++++++++++ internal/ent/mentionprofile_delete.go | 88 +++ internal/ent/mentionprofile_query.go | 703 +++++++++++++++++ internal/ent/mentionprofile_update.go | 438 +++++++++++ internal/ent/migrate/schema.go | 37 + internal/ent/mutation.go | 720 +++++++++++++++++- internal/ent/post.go | 26 +- internal/ent/post/post.go | 30 + internal/ent/post/where.go | 23 + internal/ent/post_create.go | 32 + internal/ent/post_query.go | 76 +- internal/ent/post_update.go | 163 ++++ internal/ent/predicate/predicate.go | 3 + internal/ent/runtime.go | 32 + internal/ent/schema/account.go | 3 + internal/ent/schema/mention_profile.go | 48 ++ internal/ent/schema/post.go | 3 + internal/ent/tx.go | 3 + internal/integration/fx.go | 3 +- .../api/openapi-schema/notificationEvent.ts | 1 + 78 files changed, 4872 insertions(+), 369 deletions(-) rename app/resources/{content => datagraph}/content.go (54%) rename app/resources/{content => datagraph}/content_test.go (58%) create mode 100644 app/resources/datagraph/ref.go create mode 100644 app/services/mention/mention_job/job.go create mode 100644 app/services/mention/mention_job/mention.go create mode 100644 app/services/mention/mention_job/provider.go create mode 100644 app/services/mention/mentioner/mentioner.go create mode 100644 internal/ent/mentionprofile.go create mode 100644 internal/ent/mentionprofile/mentionprofile.go create mode 100644 internal/ent/mentionprofile/where.go create mode 100644 internal/ent/mentionprofile_create.go create mode 100644 internal/ent/mentionprofile_delete.go create mode 100644 internal/ent/mentionprofile_query.go create mode 100644 internal/ent/mentionprofile_update.go create mode 100644 internal/ent/schema/mention_profile.go diff --git a/api/openapi.yaml b/api/openapi.yaml index 9ea13d7df..82c03692f 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -3069,7 +3069,7 @@ components: The kind of event that triggered the notification. Identical to the `notification.Event` enumerated type. type: string - enum: [thread_reply, post_like, follow] + enum: [thread_reply, post_like, follow, profile_mention] NotificationStatus: type: string diff --git a/app/resources/account/account.go b/app/resources/account/account.go index e486a80e6..832264858 100644 --- a/app/resources/account/account.go +++ b/app/resources/account/account.go @@ -7,9 +7,8 @@ import ( "github.com/Southclaws/fault" "github.com/Southclaws/fault/ftag" "github.com/Southclaws/opt" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/rs/xid" - - "github.com/Southclaws/storyden/app/resources/content" ) var errSuspended = fault.Wrap(fault.New("suspended"), ftag.With(ftag.PermissionDenied)) @@ -22,7 +21,7 @@ type Account struct { ID AccountID Handle string Name string - Bio content.Rich + Bio datagraph.Content Admin bool Followers int Following int diff --git a/app/resources/account/account_writer/account_writer.go b/app/resources/account/account_writer/account_writer.go index 88351abd7..68120831f 100644 --- a/app/resources/account/account_writer/account_writer.go +++ b/app/resources/account/account_writer/account_writer.go @@ -13,7 +13,7 @@ import ( "go.uber.org/fx" "github.com/Southclaws/storyden/app/resources/account" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/internal/ent" "github.com/Southclaws/storyden/internal/ent/schema" ) @@ -47,7 +47,7 @@ func WithName(name string) Option { } } -func WithBio(v content.Rich) Option { +func WithBio(v datagraph.Content) Option { return func(a *account.Account) { a.Bio = v } diff --git a/app/resources/account/mapping.go b/app/resources/account/mapping.go index 4afd007f7..158fbbc00 100644 --- a/app/resources/account/mapping.go +++ b/app/resources/account/mapping.go @@ -8,7 +8,7 @@ import ( "github.com/Southclaws/fault" "github.com/Southclaws/opt" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/internal/ent" "github.com/Southclaws/storyden/internal/ent/schema" ) @@ -18,7 +18,7 @@ func MapAccount(a *ent.Account) (*Account, error) { return a.Service }) - bio, err := content.NewRichText(a.Bio) + bio, err := datagraph.NewRichText(a.Bio) if err != nil { return nil, err } diff --git a/app/resources/account/notification/event.go b/app/resources/account/notification/event.go index 85263284d..66de243ae 100644 --- a/app/resources/account/notification/event.go +++ b/app/resources/account/notification/event.go @@ -5,7 +5,8 @@ package notification type eventEnum string const ( - eventThreadReply eventEnum = "thread_reply" - eventPostLike eventEnum = "post_like" - eventFollow eventEnum = "follow" + eventThreadReply eventEnum = "thread_reply" + eventPostLike eventEnum = "post_like" + eventFollow eventEnum = "follow" + eventProfileMention eventEnum = "profile_mention" ) diff --git a/app/resources/account/notification/notification_enum_gen.go b/app/resources/account/notification/notification_enum_gen.go index 0c939bbbc..b52804738 100644 --- a/app/resources/account/notification/notification_enum_gen.go +++ b/app/resources/account/notification/notification_enum_gen.go @@ -12,9 +12,10 @@ type Event struct { } var ( - EventThreadReply = Event{eventThreadReply} - EventPostLike = Event{eventPostLike} - EventFollow = Event{eventFollow} + EventThreadReply = Event{eventThreadReply} + EventPostLike = Event{eventPostLike} + EventFollow = Event{eventFollow} + EventProfileMention = Event{eventProfileMention} ) func (r Event) Format(f fmt.State, verb rune) { @@ -60,6 +61,8 @@ func NewEvent(__iNpUt__ string) (Event, error) { return EventPostLike, nil case string(eventFollow): return EventFollow, nil + case string(eventProfileMention): + return EventProfileMention, nil default: return Event{}, fmt.Errorf("invalid value for type 'Event': '%s'", __iNpUt__) } diff --git a/app/resources/asset/asset.go b/app/resources/asset/asset.go index 7620e00c9..0958afbc2 100644 --- a/app/resources/asset/asset.go +++ b/app/resources/asset/asset.go @@ -7,7 +7,6 @@ import ( "github.com/Southclaws/opt" "github.com/rs/xid" - "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/internal/ent" ) @@ -15,7 +14,7 @@ var errInvalidFormat = fault.New("invalid format") type Repository interface { Add(ctx context.Context, - owner account.AccountID, + owner xid.ID, filename Filename, size int, ) (*Asset, error) @@ -23,7 +22,7 @@ type Repository interface { Get(ctx context.Context, id Filename) (*Asset, error) GetByID(ctx context.Context, id AssetID) (*Asset, error) - Remove(ctx context.Context, owner account.AccountID, id Filename) error + Remove(ctx context.Context, owner xid.ID, id Filename) error } type AssetID = xid.ID diff --git a/app/resources/asset/db.go b/app/resources/asset/db.go index d44df2da8..fd04422b1 100644 --- a/app/resources/asset/db.go +++ b/app/resources/asset/db.go @@ -8,7 +8,6 @@ import ( "github.com/Southclaws/fault/ftag" "github.com/rs/xid" - "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/internal/ent" "github.com/Southclaws/storyden/internal/ent/asset" ) @@ -22,7 +21,7 @@ func New(db *ent.Client) Repository { } func (d *database) Add(ctx context.Context, - accountID account.AccountID, + accountID xid.ID, filename Filename, size int, ) (*Asset, error) { @@ -68,7 +67,7 @@ func (d *database) GetByID(ctx context.Context, id AssetID) (*Asset, error) { return FromModel(asset), nil } -func (d *database) Remove(ctx context.Context, accountID account.AccountID, id Filename) error { +func (d *database) Remove(ctx context.Context, accountID xid.ID, id Filename) error { q := d.db.Asset. Delete().Where( asset.Filename(id.name), diff --git a/app/resources/content/content.go b/app/resources/datagraph/content.go similarity index 54% rename from app/resources/content/content.go rename to app/resources/datagraph/content.go index ece0071b9..5481c1036 100644 --- a/app/resources/content/content.go +++ b/app/resources/datagraph/content.go @@ -1,21 +1,44 @@ -package content +package datagraph import ( "bytes" "fmt" + "io" "math" "net/url" + "regexp" "strings" "unicode" "github.com/Southclaws/fault" + "github.com/cixtor/readability" "github.com/microcosm-cc/bluemonday" "github.com/samber/lo" + "go.uber.org/zap" "golang.org/x/net/html" "golang.org/x/net/html/atom" ) -var policy = bluemonday.UGCPolicy() +// RefScheme is used as a scheme for URIs to reference resources. +// These can be used in content to refer to profiles, posts, nodes, etc. +const RefScheme = "sdr" + +var policy = func() *bluemonday.Policy { + p := bluemonday.UGCPolicy() + + p.AllowURLSchemes( + "mailto", + "http", + "https", + RefScheme, + ) + + p.AllowDataAttributes() + + return p +}() + +var spaces = regexp.MustCompile(`\s+`) // MaxSummaryLength is the maximum length of the short summary text const MaxSummaryLength = 128 @@ -24,14 +47,15 @@ const MaxSummaryLength = 128 // string but on the read path, it'll get turned into this either way. const EmptyState = `` -type Rich struct { +type Content struct { html *html.Node short string links []string + sdrs RefList } -func (r Rich) HTML() string { - if r.html == nil { +func (r Content) HTML() string { + if r.html == nil || r.html.FirstChild == nil { return EmptyState } @@ -45,34 +69,72 @@ func (r Rich) HTML() string { return w.String() } -func (r Rich) HTMLTree() *html.Node { +func (r Content) HTMLTree() *html.Node { return r.html } -func (r Rich) Short() string { +func (r Content) Short() string { return r.short } -func (r Rich) Links() []string { +func (r Content) Links() []string { return r.links } +func (r Content) References() RefList { + return r.sdrs +} + +type options struct { + baseURL string +} +type option func(*options) + // NewRichText will pull out any meaningful structured information from markdown // document this includes a summary of the text and all link URLs for hydrating. -func NewRichText(raw string) (Rich, error) { - sanitised := policy.Sanitize(raw) - htmlTree, err := html.Parse(strings.NewReader(sanitised)) +func NewRichText(raw string) (Content, error) { + return NewRichTextFromReader(strings.NewReader(raw)) +} + +func NewRichTextFromReader(r io.Reader, opts ...option) (Content, error) { + o := options{baseURL: "ignore:"} + for _, opt := range opts { + opt(&o) + } + + buf, err := io.ReadAll(r) + if err != nil { + return Content{}, fault.Wrap(err) + } + + sanitised := policy.SanitizeBytes(buf) + + htmlTree, err := html.Parse(bytes.NewReader(sanitised)) + if err != nil { + return Content{}, fault.Wrap(err) + } + + result, err := readability.New().Parse(bytes.NewReader(sanitised), o.baseURL) if err != nil { - return Rich{}, fault.Wrap(err) + return Content{}, fault.Wrap(err) } - return NewRichTextFromHTML(htmlTree) + short := getSummary(result) + + bodyTree, links, refs := extractReferences(htmlTree) + + return Content{ + html: bodyTree, + short: short, + links: links, + sdrs: refs, + }, nil } -func NewRichTextFromHTML(htmlTree *html.Node) (Rich, error) { +func extractReferences(htmlTree *html.Node) (*html.Node, []string, RefList) { bodyTree := &html.Node{} - textonly := strings.Builder{} links := []string{} + sdrs := []url.URL{} if htmlTree.DataAtom == atom.Body { bodyTree = htmlTree @@ -89,18 +151,14 @@ func NewRichTextFromHTML(htmlTree *html.Node) (Rich, error) { if hasHref { if parsed, err := url.Parse(href.Val); err == nil { - links = append(links, parsed.String()) + switch parsed.Scheme { + case "http", "https": + links = append(links, parsed.String()) + case RefScheme: + sdrs = append(sdrs, *parsed) + } } } - - case atom.P: - if n.Type == html.TextNode && len(n.Data) > 0 { - - oneline := strings.ReplaceAll(n.Data, "\n", " ") - textonly.Write([]byte(oneline)) - textonly.WriteByte(' ') - return - } } } @@ -115,7 +173,24 @@ func NewRichTextFromHTML(htmlTree *html.Node) (Rich, error) { } walk(htmlTree) - paragraphs := []rune(strings.TrimSpace(textonly.String())) + var refs RefList + for _, v := range sdrs { + r, err := NewRefFromSDR(v) + if err != nil { + zap.L().Warn("invalid SDR in content", zap.Error(err)) + continue + } + refs = append(refs, r) + } + + return bodyTree, links, refs +} + +func getSummary(article readability.Article) string { + trimmed := strings.TrimSpace(article.TextContent) + collapsed := spaces.ReplaceAllString(trimmed, " ") + + paragraphs := []rune(collapsed) end := int(math.Min(float64(len(paragraphs)-1), MaxSummaryLength)) var short string @@ -147,9 +222,5 @@ func NewRichTextFromHTML(htmlTree *html.Node) (Rich, error) { short = string(paragraphs) } - return Rich{ - html: bodyTree, - short: short, - links: links, - }, nil + return short } diff --git a/app/resources/content/content_test.go b/app/resources/datagraph/content_test.go similarity index 58% rename from app/resources/content/content_test.go rename to app/resources/datagraph/content_test.go index 46af93365..47bf06fd3 100644 --- a/app/resources/content/content_test.go +++ b/app/resources/datagraph/content_test.go @@ -1,23 +1,25 @@ -package content +package datagraph import ( "testing" + "github.com/Southclaws/storyden/internal/utils" + "github.com/rs/xid" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func check(t *testing.T, want Rich) func(got Rich, err error) { - return func(got Rich, err error) { +func check(t *testing.T, want Content) func(got Content, err error) { + return func(got Content, err error) { + require.NoError(t, err) assert.Equal(t, want.short, got.short) assert.Equal(t, want.links, got.links) } } func TestNewRichText(t *testing.T) { - // NOTE: Not using table tests here for easy debugging of individual cases. - t.Run("simple_html", func(t *testing.T) { - check(t, Rich{ + check(t, Content{ short: `Here's a paragraph. It's pretty neat. Here's the rest of the text. neat photo right? This is quite a long post, the summary...`, links: []string{}, })(NewRichText(`

heading

@@ -34,8 +36,8 @@ func TestNewRichText(t *testing.T) { }) t.Run("pull_links", func(t *testing.T) { - check(t, Rich{ - short: `Here's a paragraph. It's pretty neat.`, + check(t, Content{ + short: `Here's a paragraph. It's pretty neat. here are my favourite ovens here are my favourite trees`, links: []string{"https://ao.com/cooking/ovens", "https://tre.ee/trees/favs"}, })(NewRichText(`

heading

@@ -45,4 +47,16 @@ func TestNewRichText(t *testing.T) { here are my favourite trees `)) }) + + t.Run("with_uris", func(t *testing.T) { + mention := utils.Must(xid.FromString("cn2h3gfljatbqvjqctdg")) + + check(t, Content{ + short: `hey @southclaws!`, + links: []string{}, + sdrs: RefList{ + {Kind: KindProfile, ID: mention}, + }, + })(NewRichText(`

heading

hey @southclaws!

`)) + }) } diff --git a/app/resources/datagraph/indexable.go b/app/resources/datagraph/indexable.go index bbd654472..bd4a033ac 100644 --- a/app/resources/datagraph/indexable.go +++ b/app/resources/datagraph/indexable.go @@ -3,16 +3,18 @@ package datagraph import ( "github.com/rs/xid" + "github.com/Southclaws/fault" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" ) +var ErrInvalidReferenceScheme = fault.New("invalid reference scheme") + type ( Identifiable interface{ GetID() xid.ID } // Has a unique ID Slugged interface{ GetSlug() string } // Has a URL slug for web browser access Named interface{ GetName() string } // Has a renderable display name Described interface{ GetDesc() string } // Has a short description of some sort - WithContent interface{ GetContent() content.Rich } // Has long-form rich-text content + WithContent interface{ GetContent() Content } // Has long-form rich-text content WithProps interface{ GetProps() map[string]any } // Has arbitrary metadata WithAssets interface{ GetAssets() []*asset.Asset } // Has media assets ) @@ -51,29 +53,3 @@ type ItemRef interface { } type ItemList []Item - -// Ref is a non-hydrated type to express a result type from semdex operations -// such as searching or recommendations. It can be hydrated into an Item using -// the Kind field to select a relevant resource querier to find the full object. -type Ref struct { - ID xid.ID - Kind Kind - Relevance float64 -} - -func (r *Ref) GetID() xid.ID { - return r.ID -} - -func (r *Ref) GetKind() Kind { - return r.Kind -} - -type RefList []*Ref - -func NewRef(i Item) *Ref { - return &Ref{ - ID: i.GetID(), - Kind: i.GetKind(), - } -} diff --git a/app/resources/datagraph/ref.go b/app/resources/datagraph/ref.go new file mode 100644 index 000000000..2b81ec3e7 --- /dev/null +++ b/app/resources/datagraph/ref.go @@ -0,0 +1,61 @@ +package datagraph + +import ( + "net/url" + "path" + "strings" + + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/ftag" + "github.com/rs/xid" +) + +// Ref is a non-hydrated type to express a result type from semdex operations +// such as searching or recommendations. It can be hydrated into an Item using +// the Kind field to select a relevant resource querier to find the full object. +type Ref struct { + ID xid.ID + Kind Kind + Relevance float64 +} + +func (r *Ref) GetID() xid.ID { + return r.ID +} + +func (r *Ref) GetKind() Kind { + return r.Kind +} + +type RefList []*Ref + +func NewRef(i Item) *Ref { + return &Ref{ + ID: i.GetID(), + Kind: i.GetKind(), + } +} + +func NewRefFromSDR(u url.URL) (*Ref, error) { + if u.Scheme != RefScheme { + return nil, fault.Wrap(ErrInvalidReferenceScheme, ftag.With(ftag.InvalidArgument)) + } + + resourcePath, identifier := path.Split(u.Opaque) + resource := strings.Trim(resourcePath, "/") + + id, err := xid.FromString(identifier) + if err != nil { + return nil, err + } + + k, err := NewKind(resource) + if err != nil { + return nil, fault.Wrap(err, ftag.With(ftag.InvalidArgument)) + } + + return &Ref{ + ID: id, + Kind: k, + }, nil +} diff --git a/app/resources/library/library.go b/app/resources/library/library.go index ecaca70f0..8a7625bf7 100644 --- a/app/resources/library/library.go +++ b/app/resources/library/library.go @@ -7,7 +7,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/visibility" "github.com/Southclaws/storyden/internal/ent" "github.com/Southclaws/storyden/internal/ent/node" @@ -81,7 +81,7 @@ func WithContentLinks(ids ...xid.ID) Option { } } -func WithContent(v content.Rich) Option { +func WithContent(v datagraph.Content) Option { return func(c *ent.NodeMutation) { c.SetContent(v.HTML()) c.SetDescription(v.Short()) diff --git a/app/resources/library/mapping.go b/app/resources/library/mapping.go index c62093e20..58ae5250c 100644 --- a/app/resources/library/mapping.go +++ b/app/resources/library/mapping.go @@ -6,7 +6,8 @@ import ( "github.com/Southclaws/opt" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" + "github.com/Southclaws/storyden/app/resources/link/link_ref" "github.com/Southclaws/storyden/app/resources/profile" "github.com/Southclaws/storyden/app/resources/visibility" @@ -47,7 +48,7 @@ func NodeFromModel(c *ent.Node) (*Node, error) { assets := dt.Map(c.Edges.Assets, asset.FromModel) - richContent, err := opt.MapErr(opt.NewPtr(c.Content), content.NewRichText) + richContent, err := opt.MapErr(opt.NewPtr(c.Content), datagraph.NewRichText) if err != nil { return nil, err } diff --git a/app/resources/library/node.go b/app/resources/library/node.go index a620fb8d1..c4baaf9ae 100644 --- a/app/resources/library/node.go +++ b/app/resources/library/node.go @@ -9,7 +9,6 @@ import ( "github.com/rs/xid" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/visibility" ) @@ -39,7 +38,7 @@ type Node struct { Slug string Assets []*asset.Asset WebLink opt.Optional[link_ref.LinkRef] - Content opt.Optional[content.Rich] + Content opt.Optional[datagraph.Content] Description opt.Optional[string] Owner profile.Public Parent opt.Optional[Node] @@ -68,6 +67,6 @@ func (c *Node) GetDesc() string { return "" } -func (c *Node) GetContent() content.Rich { return c.Content.OrZero() } -func (c *Node) GetProps() map[string]any { return c.Metadata } -func (c *Node) GetAssets() []*asset.Asset { return c.Assets } +func (c *Node) GetContent() datagraph.Content { return c.Content.OrZero() } +func (c *Node) GetProps() map[string]any { return c.Metadata } +func (c *Node) GetAssets() []*asset.Asset { return c.Assets } diff --git a/app/resources/library/node_traversal/db.go b/app/resources/library/node_traversal/db.go index cbd24062f..dbf2883d1 100644 --- a/app/resources/library/node_traversal/db.go +++ b/app/resources/library/node_traversal/db.go @@ -11,6 +11,7 @@ import ( "github.com/Southclaws/fault" "github.com/Southclaws/fault/fctx" "github.com/Southclaws/opt" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/profile" "github.com/jmoiron/sqlx" "github.com/rs/xid" @@ -18,7 +19,7 @@ import ( account_repo "github.com/Southclaws/storyden/app/resources/account" asset_repo "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/library" "github.com/Southclaws/storyden/app/resources/visibility" "github.com/Southclaws/storyden/internal/ent" @@ -146,7 +147,7 @@ type subtreeRow struct { } func fromRow(r subtreeRow) (*library.Node, error) { - bio, err := opt.MapErr(opt.NewPtr(r.OwnerBio), content.NewRichText) + bio, err := opt.MapErr(opt.NewPtr(r.OwnerBio), datagraph.NewRichText) if err != nil { return nil, err } diff --git a/app/resources/mq/message_types.go b/app/resources/mq/message_types.go index ec20ceb4b..b5a31f42f 100644 --- a/app/resources/mq/message_types.go +++ b/app/resources/mq/message_types.go @@ -53,3 +53,8 @@ type Notification struct { Item *datagraph.Ref TargetID account.AccountID } + +type Mention struct { + Source datagraph.Ref + Item datagraph.Ref +} diff --git a/app/resources/post/post.go b/app/resources/post/post.go index 63bf399b1..cd71fb734 100644 --- a/app/resources/post/post.go +++ b/app/resources/post/post.go @@ -9,7 +9,7 @@ import ( "github.com/rs/xid" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/like" "github.com/Southclaws/storyden/app/resources/link/link_ref" @@ -29,7 +29,7 @@ type Post struct { Title string Slug string - Content content.Rich + Content datagraph.Content Author profile.Public Likes like.Status Reacts []*react.React @@ -42,14 +42,14 @@ type Post struct { DeletedAt opt.Optional[time.Time] } -func (p *Post) GetID() xid.ID { return xid.ID(p.ID) } -func (p *Post) GetKind() datagraph.Kind { return datagraph.KindPost } -func (p *Post) GetName() string { return p.Title } -func (p *Post) GetSlug() string { return p.Slug } -func (p *Post) GetContent() content.Rich { return p.Content } -func (p *Post) GetDesc() string { return p.Content.Short() } -func (p *Post) GetProps() map[string]any { return p.Meta } -func (p *Post) GetAssets() []*asset.Asset { return p.Assets } +func (p *Post) GetID() xid.ID { return xid.ID(p.ID) } +func (p *Post) GetKind() datagraph.Kind { return datagraph.KindPost } +func (p *Post) GetName() string { return p.Title } +func (p *Post) GetSlug() string { return p.Slug } +func (p *Post) GetContent() datagraph.Content { return p.Content } +func (p *Post) GetDesc() string { return p.Content.Short() } +func (p *Post) GetProps() map[string]any { return p.Meta } +func (p *Post) GetAssets() []*asset.Asset { return p.Assets } func Map(in *ent.Post) (*Post, error) { rootID, title, slug := func() (ID, string, string) { @@ -70,7 +70,7 @@ func Map(in *ent.Post) (*Post, error) { return nil, fault.Wrap(err) } - content, err := content.NewRichText(in.Body) + content, err := datagraph.NewRichText(in.Body) if err != nil { return nil, fault.Wrap(err) } diff --git a/app/resources/post/post_writer/post_writer.go b/app/resources/post/post_writer/post_writer.go index 9d8d90bfb..242548f8b 100644 --- a/app/resources/post/post_writer/post_writer.go +++ b/app/resources/post/post_writer/post_writer.go @@ -8,7 +8,7 @@ import ( "github.com/Southclaws/fault/ftag" "github.com/rs/xid" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/internal/ent" ent_post "github.com/Southclaws/storyden/internal/ent/post" @@ -28,7 +28,7 @@ func New(db *ent.Client) *PostWriter { type Option func(*ent.PostMutation) -func WithContent(v content.Rich) Option { +func WithContent(v datagraph.Content) Option { return func(pm *ent.PostMutation) { pm.SetBody(v.HTML()) pm.SetShort(v.Short()) diff --git a/app/resources/post/reply/reply.go b/app/resources/post/reply/reply.go index d5f9692f9..f7bb8db52 100644 --- a/app/resources/post/reply/reply.go +++ b/app/resources/post/reply/reply.go @@ -6,12 +6,13 @@ import ( "github.com/Southclaws/dt" "github.com/Southclaws/fault" "github.com/Southclaws/opt" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/link/link_ref" "github.com/Southclaws/storyden/app/resources/profile" "github.com/rs/xid" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/app/resources/react" "github.com/Southclaws/storyden/internal/ent" @@ -69,7 +70,7 @@ func FromModel(ls post.PostLikesMap) func(m *ent.Post) (*Reply, error) { return nil, fault.Wrap(err) } - content, err := content.NewRichText(m.Body) + content, err := datagraph.NewRichText(m.Body) if err != nil { return nil, fault.Wrap(err) } diff --git a/app/resources/post/reply/repo.go b/app/resources/post/reply/repo.go index d4345ea2e..35769e42a 100644 --- a/app/resources/post/reply/repo.go +++ b/app/resources/post/reply/repo.go @@ -7,7 +7,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/internal/ent" ) @@ -35,7 +35,7 @@ func WithID(id post.ID) Option { } } -func WithContent(v content.Rich) Option { +func WithContent(v datagraph.Content) Option { return func(pm *ent.PostMutation) { pm.SetBody(v.HTML()) pm.SetShort(v.Short()) diff --git a/app/resources/post/thread/repo.go b/app/resources/post/thread/repo.go index 2f4a6912d..bb96db88a 100644 --- a/app/resources/post/thread/repo.go +++ b/app/resources/post/thread/repo.go @@ -9,7 +9,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/app/resources/post/category" "github.com/Southclaws/storyden/app/resources/visibility" @@ -71,7 +71,7 @@ func WithTitle(v string) Option { } } -func WithContent(v content.Rich) Option { +func WithContent(v datagraph.Content) Option { return func(pm *ent.PostMutation) { pm.SetBody(v.HTML()) pm.SetShort(v.Short()) diff --git a/app/resources/post/thread/thread.go b/app/resources/post/thread/thread.go index 9ab9b8a32..3dad66104 100644 --- a/app/resources/post/thread/thread.go +++ b/app/resources/post/thread/thread.go @@ -7,7 +7,7 @@ import ( "github.com/Southclaws/storyden/app/resources/asset" "github.com/Southclaws/storyden/app/resources/collection" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/link/link_ref" "github.com/Southclaws/storyden/app/resources/post" @@ -83,7 +83,7 @@ func FromModel(ls post.PostLikesMap, rs post.PostRepliesMap) func(m *ent.Post) ( return *link_ref.Map(&in) }) - content, err := content.NewRichText(m.Body) + content, err := datagraph.NewRichText(m.Body) if err != nil { return nil, fault.Wrap(err) } diff --git a/app/resources/post/thread_writer/writer.go b/app/resources/post/thread_writer/writer.go index 8846dfdbe..901aa00e8 100644 --- a/app/resources/post/thread_writer/writer.go +++ b/app/resources/post/thread_writer/writer.go @@ -7,7 +7,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/app/resources/post/category" "github.com/Southclaws/storyden/app/resources/post/thread" @@ -45,7 +45,7 @@ func WithTitle(v string) Option { } } -func WithContent(v content.Rich) Option { +func WithContent(v datagraph.Content) Option { return func(pm *ent.PostMutation) { pm.SetBody(v.HTML()) pm.SetShort(v.Short()) diff --git a/app/resources/profile/profile.go b/app/resources/profile/profile.go index 0e4039191..d94e7f797 100644 --- a/app/resources/profile/profile.go +++ b/app/resources/profile/profile.go @@ -9,7 +9,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/tag" "github.com/Southclaws/storyden/internal/ent" @@ -22,7 +22,7 @@ type Public struct { Handle string Name string - Bio content.Rich + Bio datagraph.Content Admin bool Followers int Following int @@ -32,14 +32,14 @@ type Public struct { Metadata map[string]any } -func (p *Public) GetID() xid.ID { return xid.ID(p.ID) } -func (p *Public) GetKind() datagraph.Kind { return datagraph.KindProfile } -func (p *Public) GetName() string { return p.Name } -func (p *Public) GetSlug() string { return p.Handle } -func (p *Public) GetDesc() string { return p.Bio.Short() } -func (p *Public) GetContent() content.Rich { return p.Bio } -func (p *Public) GetProps() map[string]any { return p.Metadata } -func (p *Public) GetAssets() []*asset.Asset { return []*asset.Asset{} } +func (p *Public) GetID() xid.ID { return xid.ID(p.ID) } +func (p *Public) GetKind() datagraph.Kind { return datagraph.KindProfile } +func (p *Public) GetName() string { return p.Name } +func (p *Public) GetSlug() string { return p.Handle } +func (p *Public) GetDesc() string { return p.Bio.Short() } +func (p *Public) GetContent() datagraph.Content { return p.Bio } +func (p *Public) GetProps() map[string]any { return p.Metadata } +func (p *Public) GetAssets() []*asset.Asset { return []*asset.Asset{} } func ProfileFromModel(a *ent.Account) (*Public, error) { interests := dt.Map(a.Edges.Tags, func(t *ent.Tag) *tag.Tag { @@ -49,7 +49,7 @@ func ProfileFromModel(a *ent.Account) (*Public, error) { } }) - bio, err := content.NewRichText(a.Bio) + bio, err := datagraph.NewRichText(a.Bio) if err != nil { return nil, err } diff --git a/app/resources/seed/post.go b/app/resources/seed/post.go index 6ec975043..64538878f 100644 --- a/app/resources/seed/post.go +++ b/app/resources/seed/post.go @@ -9,12 +9,13 @@ import ( "strings" "time" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/profile" "github.com/minimaxir/big-list-of-naughty-strings/naughtystrings" "github.com/rs/xid" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/app/resources/post/reply" "github.com/Southclaws/storyden/app/resources/post/thread" @@ -29,7 +30,7 @@ var ( Post: post.Post{ ID: post.ID(id("00000000000000000010")), Author: profile.Public{ID: Account_001_Odin.ID}, - Content: utils.Must(content.NewRichText(`Storyden is a platform for building communities. + Content: utils.Must(datagraph.NewRichText(`Storyden is a platform for building communities. But not just another chat app or another forum site. Storyden is a modern take on oldschool bulletin board forums you may remember from the earlier days of the internet. @@ -69,7 +70,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000001010")), Author: profile.Public{ID: Account_004_Loki.ID}, - Content: utils.Must(content.NewRichText("first ๐Ÿ˜")), + Content: utils.Must(datagraph.NewRichText("first ๐Ÿ˜")), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -77,7 +78,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000002010")), Author: profile.Public{ID: Account_002_Frigg.ID}, - Content: utils.Must(content.NewRichText("Nice! One question: what kind of formatting can you use in posts? Is it like the old days with [b]tags[/b] and [color=red]cool stuff[/color] like that?")), + Content: utils.Must(datagraph.NewRichText("Nice! One question: what kind of formatting can you use in posts? Is it like the old days with [b]tags[/b] and [color=red]cool stuff[/color] like that?")), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -85,7 +86,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000003010")), Author: profile.Public{ID: Account_001_Odin.ID}, - Content: utils.Must(content.NewRichText("Good question @frigg, we're probably going to use Markdown with some basic extensions but nothing is set in stone yet.")), + Content: utils.Must(datagraph.NewRichText("Good question @frigg, we're probably going to use Markdown with some basic extensions but nothing is set in stone yet.")), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -93,7 +94,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000004010")), Author: profile.Public{ID: Account_008_Heimdallr.ID}, - Content: utils.Must(content.NewRichText("What about images and stuff?")), + Content: utils.Must(datagraph.NewRichText("What about images and stuff?")), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -101,7 +102,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000005010")), Author: profile.Public{ID: Account_004_Loki.ID}, - Content: utils.Must(content.NewRichText(`oh you can do that like this: + Content: utils.Must(datagraph.NewRichText(`oh you can do that like this: ![https://i.imgur.com/gl39KB7.png](https://i.imgur.com/gl39KB7.png) `)), @@ -112,7 +113,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000006010")), Author: profile.Public{ID: Account_005_รžรณrr.ID}, - Content: utils.Must(content.NewRichText(`how did you do that??`)), + Content: utils.Must(datagraph.NewRichText(`how did you do that??`)), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -120,7 +121,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000007010")), Author: profile.Public{ID: Account_004_Loki.ID}, - Content: utils.Must(content.NewRichText(`haha secret ๐Ÿ˜‰`)), + Content: utils.Must(datagraph.NewRichText(`haha secret ๐Ÿ˜‰`)), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -128,7 +129,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000008010")), Author: profile.Public{ID: Account_002_Frigg.ID}, - Content: utils.Must(content.NewRichText(`It was mentioned above, use markdown: + Content: utils.Must(datagraph.NewRichText(`It was mentioned above, use markdown: https://daringfireball.net/markdown `)), @@ -139,7 +140,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000009010")), Author: profile.Public{ID: Account_008_Heimdallr.ID}, - Content: utils.Must(content.NewRichText("Thanks guys!")), + Content: utils.Must(datagraph.NewRichText("Thanks guys!")), }, RootPostID: post.ID(id("00000000000000000010")), }, @@ -149,7 +150,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000000020")), Author: profile.Public{ID: Account_001_Odin.ID}, - Content: utils.Must(content.NewRichText(`This post contains a list of resources for those of you who wish to contribute to Storyden. + Content: utils.Must(datagraph.NewRichText(`This post contains a list of resources for those of you who wish to contribute to Storyden. What does contribution mean? Anything, large or small! Even if you spot a typo in the home page or in this demo data you can report it or even take a swing at fixing it! @@ -176,7 +177,7 @@ Storyden is still in development so please give the repository a watch if you're { Post: post.Post{ ID: post.ID(id("00000000000000001020")), - Content: utils.Must(content.NewRichText("Is there a wiki?")), + Content: utils.Must(datagraph.NewRichText("Is there a wiki?")), Author: profile.Public{ID: Account_006_Freyja.ID}, }, RootPostID: post.ID(id("00000000000000000020")), @@ -184,7 +185,7 @@ Storyden is still in development so please give the repository a watch if you're { Post: post.Post{ ID: post.ID(id("00000000000000002020")), - Content: utils.Must(content.NewRichText("Not yet but they're working on it!")), + Content: utils.Must(datagraph.NewRichText("Not yet but they're working on it!")), Author: profile.Public{ID: Account_002_Frigg.ID}, }, RootPostID: post.ID(id("00000000000000000020")), @@ -196,7 +197,7 @@ Storyden is still in development so please give the repository a watch if you're Post: post.Post{ ID: post.ID(id("00000000000000000030")), Author: profile.Public{ID: Account_005_รžรณrr.ID}, - Content: utils.Must(content.NewRichText(`In this thread: + Content: utils.Must(datagraph.NewRichText(`In this thread: Try to break storyden with large amounts of text, hacky strings, etc! GO!`)), }, @@ -206,7 +207,7 @@ Storyden is still in development so please give the repository a watch if you're { Post: post.Post{ ID: post.ID(id("00000000000000001030")), - Content: utils.Must(content.NewRichText("ooh fun! my favourite tool for this is: https://jaspervdj.be/lorem-markdownum/\n\n" + markdownTest01)), + Content: utils.Must(datagraph.NewRichText("ooh fun! my favourite tool for this is: https://jaspervdj.be/lorem-markdownum/\n\n" + markdownTest01)), Author: profile.Public{ID: Account_006_Freyja.ID}, }, RootPostID: post.ID(id("00000000000000000030")), @@ -214,7 +215,7 @@ Storyden is still in development so please give the repository a watch if you're { Post: post.Post{ ID: post.ID(id("00000000000000002030")), - Content: utils.Must(content.NewRichText(markdownTest03)), + Content: utils.Must(datagraph.NewRichText(markdownTest03)), Author: profile.Public{ID: Account_002_Frigg.ID}, }, RootPostID: post.ID(id("00000000000000000030")), @@ -222,7 +223,7 @@ Storyden is still in development so please give the repository a watch if you're { Post: post.Post{ ID: post.ID(id("00000000000000003030")), - Content: utils.Must(content.NewRichText("That's pretty useful, here's mine:\n\n" + markdownTest02)), + Content: utils.Must(datagraph.NewRichText("That's pretty useful, here's mine:\n\n" + markdownTest02)), Author: profile.Public{ID: Account_007_Freyr.ID}, }, RootPostID: post.ID(id("00000000000000000030")), @@ -230,7 +231,7 @@ Storyden is still in development so please give the repository a watch if you're { Post: post.Post{ ID: post.ID(id("00000000000000004030")), - Content: utils.Must(content.NewRichText("nah that's useless, you guys need some real hacky stuff to properly test:\n\n" + strings.Join(naughtystrings.Unencoded(), "\n\n"))), + Content: utils.Must(datagraph.NewRichText("nah that's useless, you guys need some real hacky stuff to properly test:\n\n" + strings.Join(naughtystrings.Unencoded(), "\n\n"))), Author: profile.Public{ID: Account_004_Loki.ID}, }, RootPostID: post.ID(id("00000000000000000030")), @@ -253,7 +254,7 @@ func threads(tr thread.Repository, pr reply.Repository, rr react.Repository, ar for i, a := range t.Assets { id := fmt.Sprintf("%s-asset-%d", t.ID, i) - a, err := ar.Add(ctx, t.Author.ID, asset.NewFilename(id), a.Size) + a, err := ar.Add(ctx, xid.ID(t.Author.ID), asset.NewFilename(id), a.Size) if err != nil { panic(err) } diff --git a/app/services/asset/analyse/pdf.go b/app/services/asset/analyse/pdf.go index fdf7537f4..efbf63499 100644 --- a/app/services/asset/analyse/pdf.go +++ b/app/services/asset/analyse/pdf.go @@ -2,12 +2,16 @@ package analyse import ( "context" + "io" "github.com/Southclaws/fault" "github.com/Southclaws/fault/fctx" "github.com/Southclaws/opt" + "golang.org/x/net/html" + "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" + "github.com/Southclaws/storyden/app/resources/library" "github.com/Southclaws/storyden/app/services/library/node_mutate" ) @@ -28,7 +32,13 @@ func (a *Analyser) analysePDF(ctx context.Context, buf []byte, fillrule opt.Opti return fault.Wrap(err, fctx.With(ctx)) } - rich, err := content.NewRichTextFromHTML(result.HTML) + pr, pw := io.Pipe() + err = html.Render(pw, result.HTML) + if err != nil { + return fault.Wrap(err, fctx.With(ctx)) + } + + rich, err := datagraph.NewRichTextFromReader(pr) if err != nil { return fault.Wrap(err, fctx.With(ctx)) } diff --git a/app/services/asset/asset_upload/uploader.go b/app/services/asset/asset_upload/uploader.go index 34a994f5f..d8ccbd18e 100644 --- a/app/services/asset/asset_upload/uploader.go +++ b/app/services/asset/asset_upload/uploader.go @@ -7,6 +7,7 @@ import ( "github.com/Southclaws/fault" "github.com/Southclaws/fault/fctx" "github.com/Southclaws/opt" + "github.com/rs/xid" "go.uber.org/zap" "github.com/Southclaws/storyden/app/resources/asset" @@ -58,7 +59,7 @@ func (s *Uploader) Upload(ctx context.Context, r io.Reader, size int64, name ass return nil, fault.Wrap(err, fctx.With(ctx)) } - a, err := s.assets.Add(ctx, accountID, name, int(size)) + a, err := s.assets.Add(ctx, xid.ID(accountID), name, int(size)) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx)) } diff --git a/app/services/library/node_mutate/node.go b/app/services/library/node_mutate/node.go index 8b5de0364..532b99ba3 100644 --- a/app/services/library/node_mutate/node.go +++ b/app/services/library/node_mutate/node.go @@ -16,7 +16,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/account/account_querier" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/library" "github.com/Southclaws/storyden/app/resources/library/node_children" "github.com/Southclaws/storyden/app/resources/mq" @@ -43,7 +43,7 @@ type Partial struct { Name opt.Optional[string] Slug opt.Optional[string] URL opt.Optional[url.URL] - Content opt.Optional[content.Rich] + Content opt.Optional[datagraph.Content] Parent opt.Optional[library.NodeSlug] Visibility opt.Optional[visibility.Visibility] Metadata opt.Optional[map[string]any] @@ -60,7 +60,7 @@ type DeleteOptions struct { func (p Partial) Opts() (opts []library.Option) { p.Name.Call(func(value string) { opts = append(opts, library.WithName(value)) }) p.Slug.Call(func(value string) { opts = append(opts, library.WithSlug(value)) }) - p.Content.Call(func(value content.Rich) { opts = append(opts, library.WithContent(value)) }) + p.Content.Call(func(value datagraph.Content) { opts = append(opts, library.WithContent(value)) }) p.Metadata.Call(func(value map[string]any) { opts = append(opts, library.WithMetadata(value)) }) p.AssetsAdd.Call(func(value []asset.AssetID) { opts = append(opts, library.WithAssets(value)) }) p.AssetsRemove.Call(func(value []asset.AssetID) { opts = append(opts, library.WithAssetsRemoved(value)) }) diff --git a/app/services/link/scrape/postprocess.go b/app/services/link/scrape/postprocess.go index 9522e36ae..475fd0804 100644 --- a/app/services/link/scrape/postprocess.go +++ b/app/services/link/scrape/postprocess.go @@ -10,10 +10,9 @@ import ( "github.com/Southclaws/dt" "github.com/Southclaws/fault" "github.com/Southclaws/fault/fctx" - "github.com/cixtor/readability" "golang.org/x/net/html" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" ) func (s *webScraper) postprocess(ctx context.Context, addr url.URL, r io.Reader) (*WebContent, error) { @@ -28,11 +27,13 @@ func (s *webScraper) postprocess(ctx context.Context, addr url.URL, r io.Reader) } t := metatable(doc) - rc, text, err := getArticleContent(bytes.NewReader(buf), addr) + rc, err := getArticleContent(bytes.NewReader(buf), addr) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx)) } + text := rc.Short() + withBaseURL := func(urlOrPath string) string { if urlOrPath == "" { return "" @@ -62,18 +63,13 @@ func (s *webScraper) postprocess(ctx context.Context, addr url.URL, r io.Reader) return wc, nil } -func getArticleContent(r io.Reader, pageURL url.URL) (content.Rich, string, error) { - result, err := readability.New().Parse(r, pageURL.String()) - if err != nil { - return content.Rich{}, "", fault.Wrap(err) - } - - rc, err := content.NewRichTextFromHTML(result.Node) +func getArticleContent(r io.Reader, pageURL url.URL) (datagraph.Content, error) { + rc, err := datagraph.NewRichTextFromReader(r) if err != nil { - return content.Rich{}, result.TextContent, nil + return datagraph.Content{}, nil } - return rc, result.TextContent, nil + return rc, nil } func metatable(doc *goquery.Document) map[string]string { diff --git a/app/services/link/scrape/scraper.go b/app/services/link/scrape/scraper.go index 95e672476..fc6aceec1 100644 --- a/app/services/link/scrape/scraper.go +++ b/app/services/link/scrape/scraper.go @@ -7,8 +7,7 @@ import ( "github.com/Southclaws/fault" "github.com/Southclaws/fault/fctx" - - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" ) var errFailedToScrape = fault.New("failed to scrape") @@ -23,7 +22,7 @@ type WebContent struct { Text string Favicon string Image string - Content content.Rich + Content datagraph.Content } type webScraper struct{} diff --git a/app/services/mention/mention_job/job.go b/app/services/mention/mention_job/job.go new file mode 100644 index 000000000..8f42a2f4c --- /dev/null +++ b/app/services/mention/mention_job/job.go @@ -0,0 +1,43 @@ +package mention_job + +import ( + "context" + + "go.uber.org/fx" + "go.uber.org/zap" + + "github.com/Southclaws/storyden/app/resources/mq" + "github.com/Southclaws/storyden/app/services/authentication/session" + "github.com/Southclaws/storyden/internal/infrastructure/pubsub" +) + +func runMentionConsumer( + ctx context.Context, + lc fx.Lifecycle, + l *zap.Logger, + + queue pubsub.Topic[mq.Mention], + + ic *mentionConsumer, +) { + lc.Append(fx.StartHook(func(_ context.Context) error { + channel, err := queue.Subscribe(ctx) + if err != nil { + panic(err) + } + + go func() { + for msg := range channel { + ctx = session.GetSessionFromMessage(ctx, msg) + + if err := ic.mention(ctx, msg.Payload.Source, msg.Payload.Item); err != nil { + l.Error("failed to record mention", zap.Error(err)) + } + + msg.Ack() + } + }() + + return nil + })) +} diff --git a/app/services/mention/mention_job/mention.go b/app/services/mention/mention_job/mention.go new file mode 100644 index 000000000..299491167 --- /dev/null +++ b/app/services/mention/mention_job/mention.go @@ -0,0 +1,33 @@ +package mention_job + +import ( + "context" + + "github.com/Southclaws/storyden/app/resources/account" + "github.com/Southclaws/storyden/app/resources/account/notification" + "github.com/Southclaws/storyden/app/resources/datagraph" + "github.com/Southclaws/storyden/app/services/notification/notify" +) + +type mentionConsumer struct { + notifySender *notify.Notifier +} + +func newMentionConsumer( + notifySender *notify.Notifier, +) *mentionConsumer { + return &mentionConsumer{ + notifySender: notifySender, + } +} + +func (s *mentionConsumer) mention(ctx context.Context, source datagraph.Ref, item datagraph.Ref) error { + switch item.Kind { + case datagraph.KindProfile: + s.notifySender.Send(ctx, account.AccountID(item.ID), notification.EventProfileMention, &source) + + // TODO: Store mention in db + } + + return nil +} diff --git a/app/services/mention/mention_job/provider.go b/app/services/mention/mention_job/provider.go new file mode 100644 index 000000000..d231561e1 --- /dev/null +++ b/app/services/mention/mention_job/provider.go @@ -0,0 +1,22 @@ +package mention_job + +import ( + "go.uber.org/fx" + + "github.com/Southclaws/storyden/app/resources/mq" + "github.com/Southclaws/storyden/app/services/mention/mentioner" + "github.com/Southclaws/storyden/internal/infrastructure/pubsub/queue" +) + +func Build() fx.Option { + return fx.Options( + fx.Provide( + queue.New[mq.Mention], + ), + + fx.Provide(newMentionConsumer), + fx.Invoke(runMentionConsumer), + + fx.Provide(mentioner.New), + ) +} diff --git a/app/services/mention/mentioner/mentioner.go b/app/services/mention/mentioner/mentioner.go new file mode 100644 index 000000000..44bf4c999 --- /dev/null +++ b/app/services/mention/mentioner/mentioner.go @@ -0,0 +1,32 @@ +package mentioner + +import ( + "context" + + "go.uber.org/zap" + + "github.com/Southclaws/storyden/app/resources/datagraph" + "github.com/Southclaws/storyden/app/resources/mq" + "github.com/Southclaws/storyden/internal/infrastructure/pubsub" +) + +type Mentioner struct { + l *zap.Logger + q pubsub.Topic[mq.Mention] +} + +func New(l *zap.Logger, q pubsub.Topic[mq.Mention]) *Mentioner { + return &Mentioner{l: l, q: q} +} + +func (n *Mentioner) Send(ctx context.Context, source datagraph.Ref, items ...*datagraph.Ref) { + for _, i := range items { + err := n.q.Publish(ctx, mq.Mention{ + Source: source, + Item: *i, + }) + if err != nil { + n.l.Error("failed to publish mention message", zap.Error(err)) + } + } +} diff --git a/app/services/reply/service.go b/app/services/reply/service.go index 0ef07dc2c..c19f2b1de 100644 --- a/app/services/reply/service.go +++ b/app/services/reply/service.go @@ -10,7 +10,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/account/account_querier" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/mq" "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/app/resources/post/reply" @@ -35,13 +35,13 @@ type Service interface { } type Partial struct { - Content opt.Optional[content.Rich] + Content opt.Optional[datagraph.Content] ReplyTo opt.Optional[post.ID] Meta opt.Optional[map[string]any] } func (p Partial) Opts() (opts []reply.Option) { - p.Content.Call(func(v content.Rich) { opts = append(opts, reply.WithContent(v)) }) + p.Content.Call(func(v datagraph.Content) { opts = append(opts, reply.WithContent(v)) }) p.ReplyTo.Call(func(v post.ID) { opts = append(opts, reply.WithReplyTo(v)) }) p.Meta.Call(func(v map[string]any) { opts = append(opts, reply.WithMeta(v)) }) return diff --git a/app/services/services.go b/app/services/services.go index a6cefc9fd..6858120c4 100644 --- a/app/services/services.go +++ b/app/services/services.go @@ -18,6 +18,7 @@ import ( "github.com/Southclaws/storyden/app/services/library/nodetree" "github.com/Southclaws/storyden/app/services/like/post_liker" "github.com/Southclaws/storyden/app/services/link" + "github.com/Southclaws/storyden/app/services/mention/mention_job" "github.com/Southclaws/storyden/app/services/notification/notify_job" "github.com/Southclaws/storyden/app/services/onboarding" "github.com/Southclaws/storyden/app/services/profile/following" @@ -50,6 +51,7 @@ func Build() fx.Option { collection.Build(), link.Build(), notify_job.Build(), + mention_job.Build(), semdexer.Build(), index_job.Build(), summarise_job.Build(), diff --git a/app/services/thread/create.go b/app/services/thread/create.go index 74823cee0..940c649b9 100644 --- a/app/services/thread/create.go +++ b/app/services/thread/create.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" "github.com/Southclaws/storyden/app/resources/account" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/mq" "github.com/Southclaws/storyden/app/resources/post/category" "github.com/Southclaws/storyden/app/resources/post/thread" @@ -72,5 +73,7 @@ func (s *service) Create(ctx context.Context, s.fetcher.HydrateContentURLs(ctx, thr) + s.mentioner.Send(ctx, *datagraph.NewRef(thr), thr.Content.References()...) + return thr, nil } diff --git a/app/services/thread/service.go b/app/services/thread/service.go index 36575f7be..fcc493dbc 100644 --- a/app/services/thread/service.go +++ b/app/services/thread/service.go @@ -13,7 +13,7 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/account/account_querier" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/datagraph/semdex" "github.com/Southclaws/storyden/app/resources/mq" "github.com/Southclaws/storyden/app/resources/post" @@ -22,6 +22,7 @@ import ( "github.com/Southclaws/storyden/app/resources/rbac" "github.com/Southclaws/storyden/app/resources/visibility" "github.com/Southclaws/storyden/app/services/link/fetcher" + "github.com/Southclaws/storyden/app/services/mention/mentioner" "github.com/Southclaws/storyden/internal/infrastructure/pubsub" ) @@ -57,7 +58,7 @@ type Service interface { type Partial struct { Title opt.Optional[string] - Content opt.Optional[content.Rich] + Content opt.Optional[datagraph.Content] Tags opt.Optional[[]xid.ID] Category opt.Optional[xid.ID] Visibility opt.Optional[visibility.Visibility] @@ -67,7 +68,7 @@ type Partial struct { func (p Partial) Opts() (opts []thread.Option) { p.Title.Call(func(v string) { opts = append(opts, thread.WithTitle(v)) }) - p.Content.Call(func(v content.Rich) { opts = append(opts, thread.WithContent(v)) }) + p.Content.Call(func(v datagraph.Content) { opts = append(opts, thread.WithContent(v)) }) p.Tags.Call(func(v []xid.ID) { opts = append(opts, thread.WithTags(v)) }) p.Category.Call(func(v xid.ID) { opts = append(opts, thread.WithCategory(xid.ID(v))) }) p.Visibility.Call(func(v visibility.Visibility) { opts = append(opts, thread.WithVisibility(v)) }) @@ -88,6 +89,7 @@ type service struct { fetcher *fetcher.Fetcher recommender semdex.Recommender indexQueue pubsub.Topic[mq.IndexPost] + mentioner *mentioner.Mentioner } func New( @@ -99,6 +101,7 @@ func New( fetcher *fetcher.Fetcher, recommender semdex.Recommender, indexQueue pubsub.Topic[mq.IndexPost], + mentioner *mentioner.Mentioner, ) Service { return &service{ l: l.With(zap.String("service", "thread")), @@ -108,5 +111,6 @@ func New( fetcher: fetcher, recommender: recommender, indexQueue: indexQueue, + mentioner: mentioner, } } diff --git a/app/transports/http/bindings/nodes.go b/app/transports/http/bindings/nodes.go index a0a73e6bc..422a81c77 100644 --- a/app/transports/http/bindings/nodes.go +++ b/app/transports/http/bindings/nodes.go @@ -15,7 +15,8 @@ import ( "github.com/Southclaws/storyden/app/resources/account" "github.com/Southclaws/storyden/app/resources/account/account_querier" "github.com/Southclaws/storyden/app/resources/asset" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" + "github.com/Southclaws/storyden/app/resources/library" "github.com/Southclaws/storyden/app/resources/library/node_traversal" "github.com/Southclaws/storyden/app/resources/visibility" @@ -65,7 +66,7 @@ func (c *Nodes) NodeCreate(ctx context.Context, request openapi.NodeCreateReques return nil, fault.Wrap(err, fctx.With(ctx)) } - richContent, err := opt.MapErr(opt.NewPtr(request.Body.Content), content.NewRichText) + richContent, err := opt.MapErr(opt.NewPtr(request.Body.Content), datagraph.NewRichText) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument)) } @@ -202,7 +203,7 @@ func (c *Nodes) NodeGet(ctx context.Context, request openapi.NodeGetRequestObjec } func (c *Nodes) NodeUpdate(ctx context.Context, request openapi.NodeUpdateRequestObject) (openapi.NodeUpdateResponseObject, error) { - richContent, err := opt.MapErr(opt.NewPtr(request.Body.Content), content.NewRichText) + richContent, err := opt.MapErr(opt.NewPtr(request.Body.Content), datagraph.NewRichText) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument)) } diff --git a/app/transports/http/bindings/posts.go b/app/transports/http/bindings/posts.go index 8c15a450b..81efdc6d5 100644 --- a/app/transports/http/bindings/posts.go +++ b/app/transports/http/bindings/posts.go @@ -9,7 +9,7 @@ import ( "github.com/Southclaws/fault/ftag" "github.com/Southclaws/opt" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" reply_service "github.com/Southclaws/storyden/app/services/reply" "github.com/Southclaws/storyden/app/services/search" "github.com/Southclaws/storyden/app/services/thread_mark" @@ -40,7 +40,7 @@ func (p *Posts) PostUpdate(ctx context.Context, request openapi.PostUpdateReques return nil, fault.Wrap(err, fctx.With(ctx)) } - richContent, err := opt.MapErr(opt.NewPtr(request.Body.Body), content.NewRichText) + richContent, err := opt.MapErr(opt.NewPtr(request.Body.Body), datagraph.NewRichText) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument)) } diff --git a/app/transports/http/bindings/replies.go b/app/transports/http/bindings/replies.go index b0f166d12..16e4116f8 100644 --- a/app/transports/http/bindings/replies.go +++ b/app/transports/http/bindings/replies.go @@ -8,7 +8,7 @@ import ( "github.com/Southclaws/fault/ftag" "github.com/Southclaws/opt" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/services/authentication/session" reply_service "github.com/Southclaws/storyden/app/services/reply" "github.com/Southclaws/storyden/app/services/search" @@ -45,7 +45,7 @@ func (p *Replies) ReplyCreate(ctx context.Context, request openapi.ReplyCreateRe return nil, fault.Wrap(err, fctx.With(ctx)) } - richContent, err := content.NewRichText(request.Body.Body) + richContent, err := datagraph.NewRichText(request.Body.Body) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument)) } diff --git a/app/transports/http/bindings/threads.go b/app/transports/http/bindings/threads.go index 7297ad050..5457a47fd 100644 --- a/app/transports/http/bindings/threads.go +++ b/app/transports/http/bindings/threads.go @@ -13,7 +13,8 @@ import ( "github.com/rs/xid" "github.com/Southclaws/storyden/app/resources/account/account_querier" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" + "github.com/Southclaws/storyden/app/resources/post/category" "github.com/Southclaws/storyden/app/resources/react" "github.com/Southclaws/storyden/app/resources/visibility" @@ -55,7 +56,7 @@ func (i *Threads) ThreadCreate(ctx context.Context, request openapi.ThreadCreate tags := opt.NewPtr(request.Body.Tags) - richContent, err := content.NewRichText(request.Body.Body) + richContent, err := datagraph.NewRichText(request.Body.Body) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument)) } @@ -103,7 +104,7 @@ func (i *Threads) ThreadUpdate(ctx context.Context, request openapi.ThreadUpdate return nil, fault.Wrap(err, fctx.With(ctx)) } - richContent, err := opt.MapErr(opt.NewPtr(request.Body.Body), content.NewRichText) + richContent, err := opt.MapErr(opt.NewPtr(request.Body.Body), datagraph.NewRichText) if err != nil { return nil, fault.Wrap(err, fctx.With(ctx), ftag.With(ftag.InvalidArgument)) } diff --git a/app/transports/http/bindings/utils.go b/app/transports/http/bindings/utils.go index 495e4d7b8..dfc6a8f71 100644 --- a/app/transports/http/bindings/utils.go +++ b/app/transports/http/bindings/utils.go @@ -8,7 +8,7 @@ import ( "github.com/rs/xid" "github.com/Southclaws/storyden/app/resources/account" - "github.com/Southclaws/storyden/app/resources/content" + "github.com/Southclaws/storyden/app/resources/datagraph" "github.com/Southclaws/storyden/app/resources/post" "github.com/Southclaws/storyden/app/resources/post/category" "github.com/Southclaws/storyden/app/resources/post/post_search" @@ -80,7 +80,7 @@ func serialiseThreadReference(t *thread.Thread) openapi.ThreadReference { } } -func serialiseContentHTML(c content.Rich) string { +func serialiseContentHTML(c datagraph.Content) string { return c.HTML() } diff --git a/app/transports/http/openapi/server_gen.go b/app/transports/http/openapi/server_gen.go index 033e4e82c..6d4adfe88 100644 --- a/app/transports/http/openapi/server_gen.go +++ b/app/transports/http/openapi/server_gen.go @@ -75,9 +75,10 @@ const ( // Defines values for NotificationEvent. const ( - Follow NotificationEvent = "follow" - PostLike NotificationEvent = "post_like" - ThreadReply NotificationEvent = "thread_reply" + Follow NotificationEvent = "follow" + PostLike NotificationEvent = "post_like" + ProfileMention NotificationEvent = "profile_mention" + ThreadReply NotificationEvent = "thread_reply" ) // Defines values for NotificationStatus. @@ -22036,151 +22037,152 @@ var swaggerSpec = []string{ "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", + "AAD//6Gx6XEKiQEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/ent/account.go b/internal/ent/account.go index 6c4c50d71..0f764abd0 100644 --- a/internal/ent/account.go +++ b/internal/ent/account.go @@ -62,6 +62,8 @@ type AccountEdges struct { Reacts []*React `json:"reacts,omitempty"` // Likes holds the value of the likes edge. Likes []*LikePost `json:"likes,omitempty"` + // Mentions holds the value of the mentions edge. + Mentions []*MentionProfile `json:"mentions,omitempty"` // Roles holds the value of the roles edge. Roles []*Role `json:"roles,omitempty"` // Authentication holds the value of the authentication edge. @@ -76,7 +78,7 @@ type AccountEdges struct { Assets []*Asset `json:"assets,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [14]bool + loadedTypes [15]bool } // EmailsOrErr returns the Emails value or an error if the edge @@ -151,10 +153,19 @@ func (e AccountEdges) LikesOrErr() ([]*LikePost, error) { return nil, &NotLoadedError{edge: "likes"} } +// MentionsOrErr returns the Mentions value or an error if the edge +// was not loaded in eager-loading. +func (e AccountEdges) MentionsOrErr() ([]*MentionProfile, error) { + if e.loadedTypes[8] { + return e.Mentions, nil + } + return nil, &NotLoadedError{edge: "mentions"} +} + // RolesOrErr returns the Roles value or an error if the edge // was not loaded in eager-loading. func (e AccountEdges) RolesOrErr() ([]*Role, error) { - if e.loadedTypes[8] { + if e.loadedTypes[9] { return e.Roles, nil } return nil, &NotLoadedError{edge: "roles"} @@ -163,7 +174,7 @@ func (e AccountEdges) RolesOrErr() ([]*Role, error) { // AuthenticationOrErr returns the Authentication value or an error if the edge // was not loaded in eager-loading. func (e AccountEdges) AuthenticationOrErr() ([]*Authentication, error) { - if e.loadedTypes[9] { + if e.loadedTypes[10] { return e.Authentication, nil } return nil, &NotLoadedError{edge: "authentication"} @@ -172,7 +183,7 @@ func (e AccountEdges) AuthenticationOrErr() ([]*Authentication, error) { // TagsOrErr returns the Tags value or an error if the edge // was not loaded in eager-loading. func (e AccountEdges) TagsOrErr() ([]*Tag, error) { - if e.loadedTypes[10] { + if e.loadedTypes[11] { return e.Tags, nil } return nil, &NotLoadedError{edge: "tags"} @@ -181,7 +192,7 @@ func (e AccountEdges) TagsOrErr() ([]*Tag, error) { // CollectionsOrErr returns the Collections value or an error if the edge // was not loaded in eager-loading. func (e AccountEdges) CollectionsOrErr() ([]*Collection, error) { - if e.loadedTypes[11] { + if e.loadedTypes[12] { return e.Collections, nil } return nil, &NotLoadedError{edge: "collections"} @@ -190,7 +201,7 @@ func (e AccountEdges) CollectionsOrErr() ([]*Collection, error) { // NodesOrErr returns the Nodes value or an error if the edge // was not loaded in eager-loading. func (e AccountEdges) NodesOrErr() ([]*Node, error) { - if e.loadedTypes[12] { + if e.loadedTypes[13] { return e.Nodes, nil } return nil, &NotLoadedError{edge: "nodes"} @@ -199,7 +210,7 @@ func (e AccountEdges) NodesOrErr() ([]*Node, error) { // AssetsOrErr returns the Assets value or an error if the edge // was not loaded in eager-loading. func (e AccountEdges) AssetsOrErr() ([]*Asset, error) { - if e.loadedTypes[13] { + if e.loadedTypes[14] { return e.Assets, nil } return nil, &NotLoadedError{edge: "assets"} @@ -353,6 +364,11 @@ func (a *Account) QueryLikes() *LikePostQuery { return NewAccountClient(a.config).QueryLikes(a) } +// QueryMentions queries the "mentions" edge of the Account entity. +func (a *Account) QueryMentions() *MentionProfileQuery { + return NewAccountClient(a.config).QueryMentions(a) +} + // QueryRoles queries the "roles" edge of the Account entity. func (a *Account) QueryRoles() *RoleQuery { return NewAccountClient(a.config).QueryRoles(a) diff --git a/internal/ent/account/account.go b/internal/ent/account/account.go index 79de9f56b..90a99a8aa 100644 --- a/internal/ent/account/account.go +++ b/internal/ent/account/account.go @@ -49,6 +49,8 @@ const ( EdgeReacts = "reacts" // EdgeLikes holds the string denoting the likes edge name in mutations. EdgeLikes = "likes" + // EdgeMentions holds the string denoting the mentions edge name in mutations. + EdgeMentions = "mentions" // EdgeRoles holds the string denoting the roles edge name in mutations. EdgeRoles = "roles" // EdgeAuthentication holds the string denoting the authentication edge name in mutations. @@ -119,6 +121,13 @@ const ( LikesInverseTable = "like_posts" // LikesColumn is the table column denoting the likes relation/edge. LikesColumn = "account_id" + // MentionsTable is the table that holds the mentions relation/edge. + MentionsTable = "mention_profiles" + // MentionsInverseTable is the table name for the MentionProfile entity. + // It exists in this package in order to avoid circular dependency with the "mentionprofile" package. + MentionsInverseTable = "mention_profiles" + // MentionsColumn is the table column denoting the mentions relation/edge. + MentionsColumn = "account_id" // RolesTable is the table that holds the roles relation/edge. The primary key declared below. RolesTable = "role_accounts" // RolesInverseTable is the table name for the Role entity. @@ -366,6 +375,20 @@ func ByLikes(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { } } +// ByMentionsCount orders the results by mentions count. +func ByMentionsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newMentionsStep(), opts...) + } +} + +// ByMentions orders the results by mentions terms. +func ByMentions(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newMentionsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + // ByRolesCount orders the results by roles count. func ByRolesCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { @@ -505,6 +528,13 @@ func newLikesStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, LikesTable, LikesColumn), ) } +func newMentionsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(MentionsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, MentionsTable, MentionsColumn), + ) +} func newRolesStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), diff --git a/internal/ent/account/where.go b/internal/ent/account/where.go index ff45dfca9..cfb5099f7 100644 --- a/internal/ent/account/where.go +++ b/internal/ent/account/where.go @@ -640,6 +640,29 @@ func HasLikesWith(preds ...predicate.LikePost) predicate.Account { }) } +// HasMentions applies the HasEdge predicate on the "mentions" edge. +func HasMentions() predicate.Account { + return predicate.Account(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, MentionsTable, MentionsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasMentionsWith applies the HasEdge predicate on the "mentions" edge with a given conditions (other predicates). +func HasMentionsWith(preds ...predicate.MentionProfile) predicate.Account { + return predicate.Account(func(s *sql.Selector) { + step := newMentionsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // HasRoles applies the HasEdge predicate on the "roles" edge. func HasRoles() predicate.Account { return predicate.Account(func(s *sql.Selector) { diff --git a/internal/ent/account_create.go b/internal/ent/account_create.go index 7c0039650..639f6aafd 100644 --- a/internal/ent/account_create.go +++ b/internal/ent/account_create.go @@ -19,6 +19,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/collection" "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -265,6 +266,21 @@ func (ac *AccountCreate) AddLikes(l ...*LikePost) *AccountCreate { return ac.AddLikeIDs(ids...) } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by IDs. +func (ac *AccountCreate) AddMentionIDs(ids ...xid.ID) *AccountCreate { + ac.mutation.AddMentionIDs(ids...) + return ac +} + +// AddMentions adds the "mentions" edges to the MentionProfile entity. +func (ac *AccountCreate) AddMentions(m ...*MentionProfile) *AccountCreate { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return ac.AddMentionIDs(ids...) +} + // AddRoleIDs adds the "roles" edge to the Role entity by IDs. func (ac *AccountCreate) AddRoleIDs(ids ...xid.ID) *AccountCreate { ac.mutation.AddRoleIDs(ids...) @@ -640,6 +656,22 @@ func (ac *AccountCreate) createSpec() (*Account, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := ac.mutation.MentionsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } if nodes := ac.mutation.RolesIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, diff --git a/internal/ent/account_query.go b/internal/ent/account_query.go index dc99a3791..8cbda7534 100644 --- a/internal/ent/account_query.go +++ b/internal/ent/account_query.go @@ -18,6 +18,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/collection" "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -43,6 +44,7 @@ type AccountQuery struct { withPosts *PostQuery withReacts *ReactQuery withLikes *LikePostQuery + withMentions *MentionProfileQuery withRoles *RoleQuery withAuthentication *AuthenticationQuery withTags *TagQuery @@ -262,6 +264,28 @@ func (aq *AccountQuery) QueryLikes() *LikePostQuery { return query } +// QueryMentions chains the current query on the "mentions" edge. +func (aq *AccountQuery) QueryMentions() *MentionProfileQuery { + query := (&MentionProfileClient{config: aq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := aq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := aq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(account.Table, account.FieldID, selector), + sqlgraph.To(mentionprofile.Table, mentionprofile.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, account.MentionsTable, account.MentionsColumn), + ) + fromU = sqlgraph.SetNeighbors(aq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // QueryRoles chains the current query on the "roles" edge. func (aq *AccountQuery) QueryRoles() *RoleQuery { query := (&RoleClient{config: aq.config}).Query() @@ -594,6 +618,7 @@ func (aq *AccountQuery) Clone() *AccountQuery { withPosts: aq.withPosts.Clone(), withReacts: aq.withReacts.Clone(), withLikes: aq.withLikes.Clone(), + withMentions: aq.withMentions.Clone(), withRoles: aq.withRoles.Clone(), withAuthentication: aq.withAuthentication.Clone(), withTags: aq.withTags.Clone(), @@ -694,6 +719,17 @@ func (aq *AccountQuery) WithLikes(opts ...func(*LikePostQuery)) *AccountQuery { return aq } +// WithMentions tells the query-builder to eager-load the nodes that are connected to +// the "mentions" edge. The optional arguments are used to configure the query builder of the edge. +func (aq *AccountQuery) WithMentions(opts ...func(*MentionProfileQuery)) *AccountQuery { + query := (&MentionProfileClient{config: aq.config}).Query() + for _, opt := range opts { + opt(query) + } + aq.withMentions = query + return aq +} + // WithRoles tells the query-builder to eager-load the nodes that are connected to // the "roles" edge. The optional arguments are used to configure the query builder of the edge. func (aq *AccountQuery) WithRoles(opts ...func(*RoleQuery)) *AccountQuery { @@ -838,7 +874,7 @@ func (aq *AccountQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Acco var ( nodes = []*Account{} _spec = aq.querySpec() - loadedTypes = [14]bool{ + loadedTypes = [15]bool{ aq.withEmails != nil, aq.withNotifications != nil, aq.withTriggeredNotifications != nil, @@ -847,6 +883,7 @@ func (aq *AccountQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Acco aq.withPosts != nil, aq.withReacts != nil, aq.withLikes != nil, + aq.withMentions != nil, aq.withRoles != nil, aq.withAuthentication != nil, aq.withTags != nil, @@ -934,6 +971,13 @@ func (aq *AccountQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Acco return nil, err } } + if query := aq.withMentions; query != nil { + if err := aq.loadMentions(ctx, query, nodes, + func(n *Account) { n.Edges.Mentions = []*MentionProfile{} }, + func(n *Account, e *MentionProfile) { n.Edges.Mentions = append(n.Edges.Mentions, e) }); err != nil { + return nil, err + } + } if query := aq.withRoles; query != nil { if err := aq.loadRoles(ctx, query, nodes, func(n *Account) { n.Edges.Roles = []*Role{} }, @@ -1226,6 +1270,36 @@ func (aq *AccountQuery) loadLikes(ctx context.Context, query *LikePostQuery, nod } return nil } +func (aq *AccountQuery) loadMentions(ctx context.Context, query *MentionProfileQuery, nodes []*Account, init func(*Account), assign func(*Account, *MentionProfile)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[xid.ID]*Account) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(mentionprofile.FieldAccountID) + } + query.Where(predicate.MentionProfile(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(account.MentionsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.AccountID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "account_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (aq *AccountQuery) loadRoles(ctx context.Context, query *RoleQuery, nodes []*Account, init func(*Account), assign func(*Account, *Role)) error { edgeIDs := make([]driver.Value, len(nodes)) byID := make(map[xid.ID]*Account) diff --git a/internal/ent/account_update.go b/internal/ent/account_update.go index 27b10906c..e46e6d633 100644 --- a/internal/ent/account_update.go +++ b/internal/ent/account_update.go @@ -19,6 +19,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/collection" "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -282,6 +283,21 @@ func (au *AccountUpdate) AddLikes(l ...*LikePost) *AccountUpdate { return au.AddLikeIDs(ids...) } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by IDs. +func (au *AccountUpdate) AddMentionIDs(ids ...xid.ID) *AccountUpdate { + au.mutation.AddMentionIDs(ids...) + return au +} + +// AddMentions adds the "mentions" edges to the MentionProfile entity. +func (au *AccountUpdate) AddMentions(m ...*MentionProfile) *AccountUpdate { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return au.AddMentionIDs(ids...) +} + // AddRoleIDs adds the "roles" edge to the Role entity by IDs. func (au *AccountUpdate) AddRoleIDs(ids ...xid.ID) *AccountUpdate { au.mutation.AddRoleIDs(ids...) @@ -545,6 +561,27 @@ func (au *AccountUpdate) RemoveLikes(l ...*LikePost) *AccountUpdate { return au.RemoveLikeIDs(ids...) } +// ClearMentions clears all "mentions" edges to the MentionProfile entity. +func (au *AccountUpdate) ClearMentions() *AccountUpdate { + au.mutation.ClearMentions() + return au +} + +// RemoveMentionIDs removes the "mentions" edge to MentionProfile entities by IDs. +func (au *AccountUpdate) RemoveMentionIDs(ids ...xid.ID) *AccountUpdate { + au.mutation.RemoveMentionIDs(ids...) + return au +} + +// RemoveMentions removes "mentions" edges to MentionProfile entities. +func (au *AccountUpdate) RemoveMentions(m ...*MentionProfile) *AccountUpdate { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return au.RemoveMentionIDs(ids...) +} + // ClearRoles clears all "roles" edges to the Role entity. func (au *AccountUpdate) ClearRoles() *AccountUpdate { au.mutation.ClearRoles() @@ -1141,6 +1178,51 @@ func (au *AccountUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if au.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := au.mutation.RemovedMentionsIDs(); len(nodes) > 0 && !au.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := au.mutation.MentionsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if au.mutation.RolesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, @@ -1671,6 +1753,21 @@ func (auo *AccountUpdateOne) AddLikes(l ...*LikePost) *AccountUpdateOne { return auo.AddLikeIDs(ids...) } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by IDs. +func (auo *AccountUpdateOne) AddMentionIDs(ids ...xid.ID) *AccountUpdateOne { + auo.mutation.AddMentionIDs(ids...) + return auo +} + +// AddMentions adds the "mentions" edges to the MentionProfile entity. +func (auo *AccountUpdateOne) AddMentions(m ...*MentionProfile) *AccountUpdateOne { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return auo.AddMentionIDs(ids...) +} + // AddRoleIDs adds the "roles" edge to the Role entity by IDs. func (auo *AccountUpdateOne) AddRoleIDs(ids ...xid.ID) *AccountUpdateOne { auo.mutation.AddRoleIDs(ids...) @@ -1934,6 +2031,27 @@ func (auo *AccountUpdateOne) RemoveLikes(l ...*LikePost) *AccountUpdateOne { return auo.RemoveLikeIDs(ids...) } +// ClearMentions clears all "mentions" edges to the MentionProfile entity. +func (auo *AccountUpdateOne) ClearMentions() *AccountUpdateOne { + auo.mutation.ClearMentions() + return auo +} + +// RemoveMentionIDs removes the "mentions" edge to MentionProfile entities by IDs. +func (auo *AccountUpdateOne) RemoveMentionIDs(ids ...xid.ID) *AccountUpdateOne { + auo.mutation.RemoveMentionIDs(ids...) + return auo +} + +// RemoveMentions removes "mentions" edges to MentionProfile entities. +func (auo *AccountUpdateOne) RemoveMentions(m ...*MentionProfile) *AccountUpdateOne { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return auo.RemoveMentionIDs(ids...) +} + // ClearRoles clears all "roles" edges to the Role entity. func (auo *AccountUpdateOne) ClearRoles() *AccountUpdateOne { auo.mutation.ClearRoles() @@ -2560,6 +2678,51 @@ func (auo *AccountUpdateOne) sqlSave(ctx context.Context) (_node *Account, err e } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if auo.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := auo.mutation.RemovedMentionsIDs(); len(nodes) > 0 && !auo.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := auo.mutation.MentionsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.MentionsTable, + Columns: []string{account.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if auo.mutation.RolesCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, diff --git a/internal/ent/client.go b/internal/ent/client.go index a97927c80..27d09e489 100644 --- a/internal/ent/client.go +++ b/internal/ent/client.go @@ -27,6 +27,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -65,6 +66,8 @@ type Client struct { LikePost *LikePostClient // Link is the client for interacting with the Link builders. Link *LinkClient + // MentionProfile is the client for interacting with the MentionProfile builders. + MentionProfile *MentionProfileClient // Node is the client for interacting with the Node builders. Node *NodeClient // Notification is the client for interacting with the Notification builders. @@ -101,6 +104,7 @@ func (c *Client) init() { c.Email = NewEmailClient(c.config) c.LikePost = NewLikePostClient(c.config) c.Link = NewLinkClient(c.config) + c.MentionProfile = NewMentionProfileClient(c.config) c.Node = NewNodeClient(c.config) c.Notification = NewNotificationClient(c.config) c.Post = NewPostClient(c.config) @@ -211,6 +215,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Email: NewEmailClient(cfg), LikePost: NewLikePostClient(cfg), Link: NewLinkClient(cfg), + MentionProfile: NewMentionProfileClient(cfg), Node: NewNodeClient(cfg), Notification: NewNotificationClient(cfg), Post: NewPostClient(cfg), @@ -248,6 +253,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Email: NewEmailClient(cfg), LikePost: NewLikePostClient(cfg), Link: NewLinkClient(cfg), + MentionProfile: NewMentionProfileClient(cfg), Node: NewNodeClient(cfg), Notification: NewNotificationClient(cfg), Post: NewPostClient(cfg), @@ -285,8 +291,9 @@ func (c *Client) Close() error { func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ c.Account, c.AccountFollow, c.Asset, c.Authentication, c.Category, c.Collection, - c.CollectionNode, c.CollectionPost, c.Email, c.LikePost, c.Link, c.Node, - c.Notification, c.Post, c.React, c.Role, c.Setting, c.Tag, + c.CollectionNode, c.CollectionPost, c.Email, c.LikePost, c.Link, + c.MentionProfile, c.Node, c.Notification, c.Post, c.React, c.Role, c.Setting, + c.Tag, } { n.Use(hooks...) } @@ -297,8 +304,9 @@ func (c *Client) Use(hooks ...Hook) { func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ c.Account, c.AccountFollow, c.Asset, c.Authentication, c.Category, c.Collection, - c.CollectionNode, c.CollectionPost, c.Email, c.LikePost, c.Link, c.Node, - c.Notification, c.Post, c.React, c.Role, c.Setting, c.Tag, + c.CollectionNode, c.CollectionPost, c.Email, c.LikePost, c.Link, + c.MentionProfile, c.Node, c.Notification, c.Post, c.React, c.Role, c.Setting, + c.Tag, } { n.Intercept(interceptors...) } @@ -329,6 +337,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.LikePost.mutate(ctx, m) case *LinkMutation: return c.Link.mutate(ctx, m) + case *MentionProfileMutation: + return c.MentionProfile.mutate(ctx, m) case *NodeMutation: return c.Node.mutate(ctx, m) case *NotificationMutation: @@ -584,6 +594,22 @@ func (c *AccountClient) QueryLikes(a *Account) *LikePostQuery { return query } +// QueryMentions queries the mentions edge of a Account. +func (c *AccountClient) QueryMentions(a *Account) *MentionProfileQuery { + query := (&MentionProfileClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := a.ID + step := sqlgraph.NewStep( + sqlgraph.From(account.Table, account.FieldID, id), + sqlgraph.To(mentionprofile.Table, mentionprofile.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, account.MentionsTable, account.MentionsColumn), + ) + fromV = sqlgraph.Neighbors(a.driver.Dialect(), step) + return fromV, nil + } + return query +} + // QueryRoles queries the roles edge of a Account. func (c *AccountClient) QueryRoles(a *Account) *RoleQuery { query := (&RoleClient{config: c.config}).Query() @@ -2401,6 +2427,171 @@ func (c *LinkClient) mutate(ctx context.Context, m *LinkMutation) (Value, error) } } +// MentionProfileClient is a client for the MentionProfile schema. +type MentionProfileClient struct { + config +} + +// NewMentionProfileClient returns a client for the MentionProfile from the given config. +func NewMentionProfileClient(c config) *MentionProfileClient { + return &MentionProfileClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `mentionprofile.Hooks(f(g(h())))`. +func (c *MentionProfileClient) Use(hooks ...Hook) { + c.hooks.MentionProfile = append(c.hooks.MentionProfile, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `mentionprofile.Intercept(f(g(h())))`. +func (c *MentionProfileClient) Intercept(interceptors ...Interceptor) { + c.inters.MentionProfile = append(c.inters.MentionProfile, interceptors...) +} + +// Create returns a builder for creating a MentionProfile entity. +func (c *MentionProfileClient) Create() *MentionProfileCreate { + mutation := newMentionProfileMutation(c.config, OpCreate) + return &MentionProfileCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of MentionProfile entities. +func (c *MentionProfileClient) CreateBulk(builders ...*MentionProfileCreate) *MentionProfileCreateBulk { + return &MentionProfileCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *MentionProfileClient) MapCreateBulk(slice any, setFunc func(*MentionProfileCreate, int)) *MentionProfileCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &MentionProfileCreateBulk{err: fmt.Errorf("calling to MentionProfileClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*MentionProfileCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &MentionProfileCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for MentionProfile. +func (c *MentionProfileClient) Update() *MentionProfileUpdate { + mutation := newMentionProfileMutation(c.config, OpUpdate) + return &MentionProfileUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *MentionProfileClient) UpdateOne(mp *MentionProfile) *MentionProfileUpdateOne { + mutation := newMentionProfileMutation(c.config, OpUpdateOne, withMentionProfile(mp)) + return &MentionProfileUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *MentionProfileClient) UpdateOneID(id xid.ID) *MentionProfileUpdateOne { + mutation := newMentionProfileMutation(c.config, OpUpdateOne, withMentionProfileID(id)) + return &MentionProfileUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for MentionProfile. +func (c *MentionProfileClient) Delete() *MentionProfileDelete { + mutation := newMentionProfileMutation(c.config, OpDelete) + return &MentionProfileDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *MentionProfileClient) DeleteOne(mp *MentionProfile) *MentionProfileDeleteOne { + return c.DeleteOneID(mp.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *MentionProfileClient) DeleteOneID(id xid.ID) *MentionProfileDeleteOne { + builder := c.Delete().Where(mentionprofile.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &MentionProfileDeleteOne{builder} +} + +// Query returns a query builder for MentionProfile. +func (c *MentionProfileClient) Query() *MentionProfileQuery { + return &MentionProfileQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeMentionProfile}, + inters: c.Interceptors(), + } +} + +// Get returns a MentionProfile entity by its id. +func (c *MentionProfileClient) Get(ctx context.Context, id xid.ID) (*MentionProfile, error) { + return c.Query().Where(mentionprofile.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *MentionProfileClient) GetX(ctx context.Context, id xid.ID) *MentionProfile { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryAccount queries the account edge of a MentionProfile. +func (c *MentionProfileClient) QueryAccount(mp *MentionProfile) *AccountQuery { + query := (&AccountClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := mp.ID + step := sqlgraph.NewStep( + sqlgraph.From(mentionprofile.Table, mentionprofile.FieldID, id), + sqlgraph.To(account.Table, account.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, mentionprofile.AccountTable, mentionprofile.AccountColumn), + ) + fromV = sqlgraph.Neighbors(mp.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryPost queries the Post edge of a MentionProfile. +func (c *MentionProfileClient) QueryPost(mp *MentionProfile) *PostQuery { + query := (&PostClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := mp.ID + step := sqlgraph.NewStep( + sqlgraph.From(mentionprofile.Table, mentionprofile.FieldID, id), + sqlgraph.To(post.Table, post.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, mentionprofile.PostTable, mentionprofile.PostColumn), + ) + fromV = sqlgraph.Neighbors(mp.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *MentionProfileClient) Hooks() []Hook { + return c.hooks.MentionProfile +} + +// Interceptors returns the client interceptors. +func (c *MentionProfileClient) Interceptors() []Interceptor { + return c.inters.MentionProfile +} + +func (c *MentionProfileClient) mutate(ctx context.Context, m *MentionProfileMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&MentionProfileCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&MentionProfileUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&MentionProfileUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&MentionProfileDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown MentionProfile mutation op: %q", m.Op()) + } +} + // NodeClient is a client for the Node schema. type NodeClient struct { config @@ -3095,6 +3286,22 @@ func (c *PostClient) QueryLikes(po *Post) *LikePostQuery { return query } +// QueryMentions queries the mentions edge of a Post. +func (c *PostClient) QueryMentions(po *Post) *MentionProfileQuery { + query := (&MentionProfileClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := po.ID + step := sqlgraph.NewStep( + sqlgraph.From(post.Table, post.FieldID, id), + sqlgraph.To(mentionprofile.Table, mentionprofile.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, post.MentionsTable, post.MentionsColumn), + ) + fromV = sqlgraph.Neighbors(po.driver.Dialect(), step) + return fromV, nil + } + return query +} + // QueryAssets queries the assets edge of a Post. func (c *PostClient) QueryAssets(po *Post) *AssetQuery { query := (&AssetClient{config: c.config}).Query() @@ -3816,13 +4023,13 @@ func (c *TagClient) mutate(ctx context.Context, m *TagMutation) (Value, error) { type ( hooks struct { Account, AccountFollow, Asset, Authentication, Category, Collection, - CollectionNode, CollectionPost, Email, LikePost, Link, Node, Notification, - Post, React, Role, Setting, Tag []ent.Hook + CollectionNode, CollectionPost, Email, LikePost, Link, MentionProfile, Node, + Notification, Post, React, Role, Setting, Tag []ent.Hook } inters struct { Account, AccountFollow, Asset, Authentication, Category, Collection, - CollectionNode, CollectionPost, Email, LikePost, Link, Node, Notification, - Post, React, Role, Setting, Tag []ent.Interceptor + CollectionNode, CollectionPost, Email, LikePost, Link, MentionProfile, Node, + Notification, Post, React, Role, Setting, Tag []ent.Interceptor } ) diff --git a/internal/ent/ent.go b/internal/ent/ent.go index f45480592..dea310fb2 100644 --- a/internal/ent/ent.go +++ b/internal/ent/ent.go @@ -23,6 +23,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -101,6 +102,7 @@ func checkColumn(table, column string) error { email.Table: email.ValidColumn, likepost.Table: likepost.ValidColumn, link.Table: link.ValidColumn, + mentionprofile.Table: mentionprofile.ValidColumn, node.Table: node.ValidColumn, notification.Table: notification.ValidColumn, post.Table: post.ValidColumn, diff --git a/internal/ent/er.html b/internal/ent/er.html index 4126be13c..db44de2ff 100644 --- a/internal/ent/er.html +++ b/internal/ent/er.html @@ -100,6 +100,12 @@ xidDOTID primary_asset_id xidDOTID favicon_asset_id } + MentionProfile { + xidDOTID id + timeDOTTime created_at + xidDOTID account_id + xidDOTID post_id + } Node { xidDOTID id timeDOTTime created_at @@ -175,6 +181,7 @@ Account |o--o{ Post : "posts/author" Account |o--o{ React : "reacts/account" Account |o--o{ LikePost : "likes/account" + Account |o--o{ MentionProfile : "mentions/account" Account |o--o{ Authentication : "authentication/account" Account }o--o{ Tag : "tags/accounts" Account |o--o{ Collection : "collections/owner" @@ -201,6 +208,7 @@ Post |o--o{ Post : "replies/replyTo" Post |o--o{ React : "reacts/Post" Post |o--o{ LikePost : "likes/Post" + Post |o--o{ MentionProfile : "mentions/Post" Post }o--o{ Asset : "assets/posts" Role }o--o{ Account : "accounts/roles" Tag }o--o{ Post : "posts/tags" diff --git a/internal/ent/hook/hook.go b/internal/ent/hook/hook.go index c20d9d5b0..c1c19851c 100644 --- a/internal/ent/hook/hook.go +++ b/internal/ent/hook/hook.go @@ -141,6 +141,18 @@ func (f LinkFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.LinkMutation", m) } +// The MentionProfileFunc type is an adapter to allow the use of ordinary +// function as MentionProfile mutator. +type MentionProfileFunc func(context.Context, *ent.MentionProfileMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f MentionProfileFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.MentionProfileMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MentionProfileMutation", m) +} + // The NodeFunc type is an adapter to allow the use of ordinary // function as Node mutator. type NodeFunc func(context.Context, *ent.NodeMutation) (ent.Value, error) diff --git a/internal/ent/mentionprofile.go b/internal/ent/mentionprofile.go new file mode 100644 index 000000000..578bcbe24 --- /dev/null +++ b/internal/ent/mentionprofile.go @@ -0,0 +1,175 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/Southclaws/storyden/internal/ent/account" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" + "github.com/Southclaws/storyden/internal/ent/post" + "github.com/rs/xid" +) + +// MentionProfile is the model entity for the MentionProfile schema. +type MentionProfile struct { + config `json:"-"` + // ID of the ent. + ID xid.ID `json:"id,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // AccountID holds the value of the "account_id" field. + AccountID xid.ID `json:"account_id,omitempty"` + // PostID holds the value of the "post_id" field. + PostID xid.ID `json:"post_id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the MentionProfileQuery when eager-loading is set. + Edges MentionProfileEdges `json:"edges"` + selectValues sql.SelectValues +} + +// MentionProfileEdges holds the relations/edges for other nodes in the graph. +type MentionProfileEdges struct { + // Account holds the value of the account edge. + Account *Account `json:"account,omitempty"` + // Post holds the value of the Post edge. + Post *Post `json:"Post,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// AccountOrErr returns the Account value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e MentionProfileEdges) AccountOrErr() (*Account, error) { + if e.Account != nil { + return e.Account, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: account.Label} + } + return nil, &NotLoadedError{edge: "account"} +} + +// PostOrErr returns the Post value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e MentionProfileEdges) PostOrErr() (*Post, error) { + if e.Post != nil { + return e.Post, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: post.Label} + } + return nil, &NotLoadedError{edge: "Post"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*MentionProfile) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case mentionprofile.FieldCreatedAt: + values[i] = new(sql.NullTime) + case mentionprofile.FieldID, mentionprofile.FieldAccountID, mentionprofile.FieldPostID: + values[i] = new(xid.ID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the MentionProfile fields. +func (mp *MentionProfile) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case mentionprofile.FieldID: + if value, ok := values[i].(*xid.ID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + mp.ID = *value + } + case mentionprofile.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + mp.CreatedAt = value.Time + } + case mentionprofile.FieldAccountID: + if value, ok := values[i].(*xid.ID); !ok { + return fmt.Errorf("unexpected type %T for field account_id", values[i]) + } else if value != nil { + mp.AccountID = *value + } + case mentionprofile.FieldPostID: + if value, ok := values[i].(*xid.ID); !ok { + return fmt.Errorf("unexpected type %T for field post_id", values[i]) + } else if value != nil { + mp.PostID = *value + } + default: + mp.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the MentionProfile. +// This includes values selected through modifiers, order, etc. +func (mp *MentionProfile) Value(name string) (ent.Value, error) { + return mp.selectValues.Get(name) +} + +// QueryAccount queries the "account" edge of the MentionProfile entity. +func (mp *MentionProfile) QueryAccount() *AccountQuery { + return NewMentionProfileClient(mp.config).QueryAccount(mp) +} + +// QueryPost queries the "Post" edge of the MentionProfile entity. +func (mp *MentionProfile) QueryPost() *PostQuery { + return NewMentionProfileClient(mp.config).QueryPost(mp) +} + +// Update returns a builder for updating this MentionProfile. +// Note that you need to call MentionProfile.Unwrap() before calling this method if this MentionProfile +// was returned from a transaction, and the transaction was committed or rolled back. +func (mp *MentionProfile) Update() *MentionProfileUpdateOne { + return NewMentionProfileClient(mp.config).UpdateOne(mp) +} + +// Unwrap unwraps the MentionProfile entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (mp *MentionProfile) Unwrap() *MentionProfile { + _tx, ok := mp.config.driver.(*txDriver) + if !ok { + panic("ent: MentionProfile is not a transactional entity") + } + mp.config.driver = _tx.drv + return mp +} + +// String implements the fmt.Stringer. +func (mp *MentionProfile) String() string { + var builder strings.Builder + builder.WriteString("MentionProfile(") + builder.WriteString(fmt.Sprintf("id=%v, ", mp.ID)) + builder.WriteString("created_at=") + builder.WriteString(mp.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("account_id=") + builder.WriteString(fmt.Sprintf("%v", mp.AccountID)) + builder.WriteString(", ") + builder.WriteString("post_id=") + builder.WriteString(fmt.Sprintf("%v", mp.PostID)) + builder.WriteByte(')') + return builder.String() +} + +// MentionProfiles is a parsable slice of MentionProfile. +type MentionProfiles []*MentionProfile diff --git a/internal/ent/mentionprofile/mentionprofile.go b/internal/ent/mentionprofile/mentionprofile.go new file mode 100644 index 000000000..f39c64a7d --- /dev/null +++ b/internal/ent/mentionprofile/mentionprofile.go @@ -0,0 +1,122 @@ +// Code generated by ent, DO NOT EDIT. + +package mentionprofile + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/rs/xid" +) + +const ( + // Label holds the string label denoting the mentionprofile type in the database. + Label = "mention_profile" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldAccountID holds the string denoting the account_id field in the database. + FieldAccountID = "account_id" + // FieldPostID holds the string denoting the post_id field in the database. + FieldPostID = "post_id" + // EdgeAccount holds the string denoting the account edge name in mutations. + EdgeAccount = "account" + // EdgePost holds the string denoting the post edge name in mutations. + EdgePost = "Post" + // Table holds the table name of the mentionprofile in the database. + Table = "mention_profiles" + // AccountTable is the table that holds the account relation/edge. + AccountTable = "mention_profiles" + // AccountInverseTable is the table name for the Account entity. + // It exists in this package in order to avoid circular dependency with the "account" package. + AccountInverseTable = "accounts" + // AccountColumn is the table column denoting the account relation/edge. + AccountColumn = "account_id" + // PostTable is the table that holds the Post relation/edge. + PostTable = "mention_profiles" + // PostInverseTable is the table name for the Post entity. + // It exists in this package in order to avoid circular dependency with the "post" package. + PostInverseTable = "posts" + // PostColumn is the table column denoting the Post relation/edge. + PostColumn = "post_id" +) + +// Columns holds all SQL columns for mentionprofile fields. +var Columns = []string{ + FieldID, + FieldCreatedAt, + FieldAccountID, + FieldPostID, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() xid.ID + // IDValidator is a validator for the "id" field. It is called by the builders before save. + IDValidator func(string) error +) + +// OrderOption defines the ordering options for the MentionProfile queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByAccountID orders the results by the account_id field. +func ByAccountID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAccountID, opts...).ToFunc() +} + +// ByPostID orders the results by the post_id field. +func ByPostID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPostID, opts...).ToFunc() +} + +// ByAccountField orders the results by account field. +func ByAccountField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newAccountStep(), sql.OrderByField(field, opts...)) + } +} + +// ByPostField orders the results by Post field. +func ByPostField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newPostStep(), sql.OrderByField(field, opts...)) + } +} +func newAccountStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(AccountInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, AccountTable, AccountColumn), + ) +} +func newPostStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(PostInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, PostTable, PostColumn), + ) +} diff --git a/internal/ent/mentionprofile/where.go b/internal/ent/mentionprofile/where.go new file mode 100644 index 000000000..30315f119 --- /dev/null +++ b/internal/ent/mentionprofile/where.go @@ -0,0 +1,313 @@ +// Code generated by ent, DO NOT EDIT. + +package mentionprofile + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/Southclaws/storyden/internal/ent/predicate" + "github.com/rs/xid" +) + +// ID filters vertices based on their ID field. +func ID(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLTE(FieldID, id)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldCreatedAt, v)) +} + +// AccountID applies equality check predicate on the "account_id" field. It's identical to AccountIDEQ. +func AccountID(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldAccountID, v)) +} + +// PostID applies equality check predicate on the "post_id" field. It's identical to PostIDEQ. +func PostID(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldPostID, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLTE(FieldCreatedAt, v)) +} + +// AccountIDEQ applies the EQ predicate on the "account_id" field. +func AccountIDEQ(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldAccountID, v)) +} + +// AccountIDNEQ applies the NEQ predicate on the "account_id" field. +func AccountIDNEQ(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNEQ(FieldAccountID, v)) +} + +// AccountIDIn applies the In predicate on the "account_id" field. +func AccountIDIn(vs ...xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldIn(FieldAccountID, vs...)) +} + +// AccountIDNotIn applies the NotIn predicate on the "account_id" field. +func AccountIDNotIn(vs ...xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNotIn(FieldAccountID, vs...)) +} + +// AccountIDGT applies the GT predicate on the "account_id" field. +func AccountIDGT(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGT(FieldAccountID, v)) +} + +// AccountIDGTE applies the GTE predicate on the "account_id" field. +func AccountIDGTE(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGTE(FieldAccountID, v)) +} + +// AccountIDLT applies the LT predicate on the "account_id" field. +func AccountIDLT(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLT(FieldAccountID, v)) +} + +// AccountIDLTE applies the LTE predicate on the "account_id" field. +func AccountIDLTE(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLTE(FieldAccountID, v)) +} + +// AccountIDContains applies the Contains predicate on the "account_id" field. +func AccountIDContains(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldContains(FieldAccountID, vc)) +} + +// AccountIDHasPrefix applies the HasPrefix predicate on the "account_id" field. +func AccountIDHasPrefix(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldHasPrefix(FieldAccountID, vc)) +} + +// AccountIDHasSuffix applies the HasSuffix predicate on the "account_id" field. +func AccountIDHasSuffix(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldHasSuffix(FieldAccountID, vc)) +} + +// AccountIDEqualFold applies the EqualFold predicate on the "account_id" field. +func AccountIDEqualFold(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldEqualFold(FieldAccountID, vc)) +} + +// AccountIDContainsFold applies the ContainsFold predicate on the "account_id" field. +func AccountIDContainsFold(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldContainsFold(FieldAccountID, vc)) +} + +// PostIDEQ applies the EQ predicate on the "post_id" field. +func PostIDEQ(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldEQ(FieldPostID, v)) +} + +// PostIDNEQ applies the NEQ predicate on the "post_id" field. +func PostIDNEQ(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNEQ(FieldPostID, v)) +} + +// PostIDIn applies the In predicate on the "post_id" field. +func PostIDIn(vs ...xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldIn(FieldPostID, vs...)) +} + +// PostIDNotIn applies the NotIn predicate on the "post_id" field. +func PostIDNotIn(vs ...xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldNotIn(FieldPostID, vs...)) +} + +// PostIDGT applies the GT predicate on the "post_id" field. +func PostIDGT(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGT(FieldPostID, v)) +} + +// PostIDGTE applies the GTE predicate on the "post_id" field. +func PostIDGTE(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldGTE(FieldPostID, v)) +} + +// PostIDLT applies the LT predicate on the "post_id" field. +func PostIDLT(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLT(FieldPostID, v)) +} + +// PostIDLTE applies the LTE predicate on the "post_id" field. +func PostIDLTE(v xid.ID) predicate.MentionProfile { + return predicate.MentionProfile(sql.FieldLTE(FieldPostID, v)) +} + +// PostIDContains applies the Contains predicate on the "post_id" field. +func PostIDContains(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldContains(FieldPostID, vc)) +} + +// PostIDHasPrefix applies the HasPrefix predicate on the "post_id" field. +func PostIDHasPrefix(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldHasPrefix(FieldPostID, vc)) +} + +// PostIDHasSuffix applies the HasSuffix predicate on the "post_id" field. +func PostIDHasSuffix(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldHasSuffix(FieldPostID, vc)) +} + +// PostIDEqualFold applies the EqualFold predicate on the "post_id" field. +func PostIDEqualFold(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldEqualFold(FieldPostID, vc)) +} + +// PostIDContainsFold applies the ContainsFold predicate on the "post_id" field. +func PostIDContainsFold(v xid.ID) predicate.MentionProfile { + vc := v.String() + return predicate.MentionProfile(sql.FieldContainsFold(FieldPostID, vc)) +} + +// HasAccount applies the HasEdge predicate on the "account" edge. +func HasAccount() predicate.MentionProfile { + return predicate.MentionProfile(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, AccountTable, AccountColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasAccountWith applies the HasEdge predicate on the "account" edge with a given conditions (other predicates). +func HasAccountWith(preds ...predicate.Account) predicate.MentionProfile { + return predicate.MentionProfile(func(s *sql.Selector) { + step := newAccountStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasPost applies the HasEdge predicate on the "Post" edge. +func HasPost() predicate.MentionProfile { + return predicate.MentionProfile(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, PostTable, PostColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasPostWith applies the HasEdge predicate on the "Post" edge with a given conditions (other predicates). +func HasPostWith(preds ...predicate.Post) predicate.MentionProfile { + return predicate.MentionProfile(func(s *sql.Selector) { + step := newPostStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.MentionProfile) predicate.MentionProfile { + return predicate.MentionProfile(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.MentionProfile) predicate.MentionProfile { + return predicate.MentionProfile(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.MentionProfile) predicate.MentionProfile { + return predicate.MentionProfile(sql.NotPredicates(p)) +} diff --git a/internal/ent/mentionprofile_create.go b/internal/ent/mentionprofile_create.go new file mode 100644 index 000000000..b7eb4b718 --- /dev/null +++ b/internal/ent/mentionprofile_create.go @@ -0,0 +1,642 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/Southclaws/storyden/internal/ent/account" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" + "github.com/Southclaws/storyden/internal/ent/post" + "github.com/rs/xid" +) + +// MentionProfileCreate is the builder for creating a MentionProfile entity. +type MentionProfileCreate struct { + config + mutation *MentionProfileMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetCreatedAt sets the "created_at" field. +func (mpc *MentionProfileCreate) SetCreatedAt(t time.Time) *MentionProfileCreate { + mpc.mutation.SetCreatedAt(t) + return mpc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (mpc *MentionProfileCreate) SetNillableCreatedAt(t *time.Time) *MentionProfileCreate { + if t != nil { + mpc.SetCreatedAt(*t) + } + return mpc +} + +// SetAccountID sets the "account_id" field. +func (mpc *MentionProfileCreate) SetAccountID(x xid.ID) *MentionProfileCreate { + mpc.mutation.SetAccountID(x) + return mpc +} + +// SetPostID sets the "post_id" field. +func (mpc *MentionProfileCreate) SetPostID(x xid.ID) *MentionProfileCreate { + mpc.mutation.SetPostID(x) + return mpc +} + +// SetID sets the "id" field. +func (mpc *MentionProfileCreate) SetID(x xid.ID) *MentionProfileCreate { + mpc.mutation.SetID(x) + return mpc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (mpc *MentionProfileCreate) SetNillableID(x *xid.ID) *MentionProfileCreate { + if x != nil { + mpc.SetID(*x) + } + return mpc +} + +// SetAccount sets the "account" edge to the Account entity. +func (mpc *MentionProfileCreate) SetAccount(a *Account) *MentionProfileCreate { + return mpc.SetAccountID(a.ID) +} + +// SetPost sets the "Post" edge to the Post entity. +func (mpc *MentionProfileCreate) SetPost(p *Post) *MentionProfileCreate { + return mpc.SetPostID(p.ID) +} + +// Mutation returns the MentionProfileMutation object of the builder. +func (mpc *MentionProfileCreate) Mutation() *MentionProfileMutation { + return mpc.mutation +} + +// Save creates the MentionProfile in the database. +func (mpc *MentionProfileCreate) Save(ctx context.Context) (*MentionProfile, error) { + mpc.defaults() + return withHooks(ctx, mpc.sqlSave, mpc.mutation, mpc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (mpc *MentionProfileCreate) SaveX(ctx context.Context) *MentionProfile { + v, err := mpc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (mpc *MentionProfileCreate) Exec(ctx context.Context) error { + _, err := mpc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (mpc *MentionProfileCreate) ExecX(ctx context.Context) { + if err := mpc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (mpc *MentionProfileCreate) defaults() { + if _, ok := mpc.mutation.CreatedAt(); !ok { + v := mentionprofile.DefaultCreatedAt() + mpc.mutation.SetCreatedAt(v) + } + if _, ok := mpc.mutation.ID(); !ok { + v := mentionprofile.DefaultID() + mpc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (mpc *MentionProfileCreate) check() error { + if _, ok := mpc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "MentionProfile.created_at"`)} + } + if _, ok := mpc.mutation.AccountID(); !ok { + return &ValidationError{Name: "account_id", err: errors.New(`ent: missing required field "MentionProfile.account_id"`)} + } + if _, ok := mpc.mutation.PostID(); !ok { + return &ValidationError{Name: "post_id", err: errors.New(`ent: missing required field "MentionProfile.post_id"`)} + } + if v, ok := mpc.mutation.ID(); ok { + if err := mentionprofile.IDValidator(v.String()); err != nil { + return &ValidationError{Name: "id", err: fmt.Errorf(`ent: validator failed for field "MentionProfile.id": %w`, err)} + } + } + if _, ok := mpc.mutation.AccountID(); !ok { + return &ValidationError{Name: "account", err: errors.New(`ent: missing required edge "MentionProfile.account"`)} + } + if _, ok := mpc.mutation.PostID(); !ok { + return &ValidationError{Name: "Post", err: errors.New(`ent: missing required edge "MentionProfile.Post"`)} + } + return nil +} + +func (mpc *MentionProfileCreate) sqlSave(ctx context.Context) (*MentionProfile, error) { + if err := mpc.check(); err != nil { + return nil, err + } + _node, _spec := mpc.createSpec() + if err := sqlgraph.CreateNode(ctx, mpc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*xid.ID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + mpc.mutation.id = &_node.ID + mpc.mutation.done = true + return _node, nil +} + +func (mpc *MentionProfileCreate) createSpec() (*MentionProfile, *sqlgraph.CreateSpec) { + var ( + _node = &MentionProfile{config: mpc.config} + _spec = sqlgraph.NewCreateSpec(mentionprofile.Table, sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString)) + ) + _spec.OnConflict = mpc.conflict + if id, ok := mpc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := mpc.mutation.CreatedAt(); ok { + _spec.SetField(mentionprofile.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if nodes := mpc.mutation.AccountIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.AccountTable, + Columns: []string{mentionprofile.AccountColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(account.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.AccountID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := mpc.mutation.PostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.PostTable, + Columns: []string{mentionprofile.PostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(post.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.PostID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MentionProfile.Create(). +// SetCreatedAt(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MentionProfileUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (mpc *MentionProfileCreate) OnConflict(opts ...sql.ConflictOption) *MentionProfileUpsertOne { + mpc.conflict = opts + return &MentionProfileUpsertOne{ + create: mpc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MentionProfile.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (mpc *MentionProfileCreate) OnConflictColumns(columns ...string) *MentionProfileUpsertOne { + mpc.conflict = append(mpc.conflict, sql.ConflictColumns(columns...)) + return &MentionProfileUpsertOne{ + create: mpc, + } +} + +type ( + // MentionProfileUpsertOne is the builder for "upsert"-ing + // one MentionProfile node. + MentionProfileUpsertOne struct { + create *MentionProfileCreate + } + + // MentionProfileUpsert is the "OnConflict" setter. + MentionProfileUpsert struct { + *sql.UpdateSet + } +) + +// SetAccountID sets the "account_id" field. +func (u *MentionProfileUpsert) SetAccountID(v xid.ID) *MentionProfileUpsert { + u.Set(mentionprofile.FieldAccountID, v) + return u +} + +// UpdateAccountID sets the "account_id" field to the value that was provided on create. +func (u *MentionProfileUpsert) UpdateAccountID() *MentionProfileUpsert { + u.SetExcluded(mentionprofile.FieldAccountID) + return u +} + +// SetPostID sets the "post_id" field. +func (u *MentionProfileUpsert) SetPostID(v xid.ID) *MentionProfileUpsert { + u.Set(mentionprofile.FieldPostID, v) + return u +} + +// UpdatePostID sets the "post_id" field to the value that was provided on create. +func (u *MentionProfileUpsert) UpdatePostID() *MentionProfileUpsert { + u.SetExcluded(mentionprofile.FieldPostID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.MentionProfile.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(mentionprofile.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MentionProfileUpsertOne) UpdateNewValues() *MentionProfileUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(mentionprofile.FieldID) + } + if _, exists := u.create.mutation.CreatedAt(); exists { + s.SetIgnore(mentionprofile.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MentionProfile.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MentionProfileUpsertOne) Ignore() *MentionProfileUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MentionProfileUpsertOne) DoNothing() *MentionProfileUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MentionProfileCreate.OnConflict +// documentation for more info. +func (u *MentionProfileUpsertOne) Update(set func(*MentionProfileUpsert)) *MentionProfileUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MentionProfileUpsert{UpdateSet: update}) + })) + return u +} + +// SetAccountID sets the "account_id" field. +func (u *MentionProfileUpsertOne) SetAccountID(v xid.ID) *MentionProfileUpsertOne { + return u.Update(func(s *MentionProfileUpsert) { + s.SetAccountID(v) + }) +} + +// UpdateAccountID sets the "account_id" field to the value that was provided on create. +func (u *MentionProfileUpsertOne) UpdateAccountID() *MentionProfileUpsertOne { + return u.Update(func(s *MentionProfileUpsert) { + s.UpdateAccountID() + }) +} + +// SetPostID sets the "post_id" field. +func (u *MentionProfileUpsertOne) SetPostID(v xid.ID) *MentionProfileUpsertOne { + return u.Update(func(s *MentionProfileUpsert) { + s.SetPostID(v) + }) +} + +// UpdatePostID sets the "post_id" field to the value that was provided on create. +func (u *MentionProfileUpsertOne) UpdatePostID() *MentionProfileUpsertOne { + return u.Update(func(s *MentionProfileUpsert) { + s.UpdatePostID() + }) +} + +// Exec executes the query. +func (u *MentionProfileUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MentionProfileCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MentionProfileUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *MentionProfileUpsertOne) ID(ctx context.Context) (id xid.ID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: MentionProfileUpsertOne.ID is not supported by MySQL driver. Use MentionProfileUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *MentionProfileUpsertOne) IDX(ctx context.Context) xid.ID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// MentionProfileCreateBulk is the builder for creating many MentionProfile entities in bulk. +type MentionProfileCreateBulk struct { + config + err error + builders []*MentionProfileCreate + conflict []sql.ConflictOption +} + +// Save creates the MentionProfile entities in the database. +func (mpcb *MentionProfileCreateBulk) Save(ctx context.Context) ([]*MentionProfile, error) { + if mpcb.err != nil { + return nil, mpcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(mpcb.builders)) + nodes := make([]*MentionProfile, len(mpcb.builders)) + mutators := make([]Mutator, len(mpcb.builders)) + for i := range mpcb.builders { + func(i int, root context.Context) { + builder := mpcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MentionProfileMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, mpcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = mpcb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, mpcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, mpcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (mpcb *MentionProfileCreateBulk) SaveX(ctx context.Context) []*MentionProfile { + v, err := mpcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (mpcb *MentionProfileCreateBulk) Exec(ctx context.Context) error { + _, err := mpcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (mpcb *MentionProfileCreateBulk) ExecX(ctx context.Context) { + if err := mpcb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MentionProfile.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MentionProfileUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (mpcb *MentionProfileCreateBulk) OnConflict(opts ...sql.ConflictOption) *MentionProfileUpsertBulk { + mpcb.conflict = opts + return &MentionProfileUpsertBulk{ + create: mpcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MentionProfile.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (mpcb *MentionProfileCreateBulk) OnConflictColumns(columns ...string) *MentionProfileUpsertBulk { + mpcb.conflict = append(mpcb.conflict, sql.ConflictColumns(columns...)) + return &MentionProfileUpsertBulk{ + create: mpcb, + } +} + +// MentionProfileUpsertBulk is the builder for "upsert"-ing +// a bulk of MentionProfile nodes. +type MentionProfileUpsertBulk struct { + create *MentionProfileCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.MentionProfile.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(mentionprofile.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MentionProfileUpsertBulk) UpdateNewValues() *MentionProfileUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(mentionprofile.FieldID) + } + if _, exists := b.mutation.CreatedAt(); exists { + s.SetIgnore(mentionprofile.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MentionProfile.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MentionProfileUpsertBulk) Ignore() *MentionProfileUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MentionProfileUpsertBulk) DoNothing() *MentionProfileUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MentionProfileCreateBulk.OnConflict +// documentation for more info. +func (u *MentionProfileUpsertBulk) Update(set func(*MentionProfileUpsert)) *MentionProfileUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MentionProfileUpsert{UpdateSet: update}) + })) + return u +} + +// SetAccountID sets the "account_id" field. +func (u *MentionProfileUpsertBulk) SetAccountID(v xid.ID) *MentionProfileUpsertBulk { + return u.Update(func(s *MentionProfileUpsert) { + s.SetAccountID(v) + }) +} + +// UpdateAccountID sets the "account_id" field to the value that was provided on create. +func (u *MentionProfileUpsertBulk) UpdateAccountID() *MentionProfileUpsertBulk { + return u.Update(func(s *MentionProfileUpsert) { + s.UpdateAccountID() + }) +} + +// SetPostID sets the "post_id" field. +func (u *MentionProfileUpsertBulk) SetPostID(v xid.ID) *MentionProfileUpsertBulk { + return u.Update(func(s *MentionProfileUpsert) { + s.SetPostID(v) + }) +} + +// UpdatePostID sets the "post_id" field to the value that was provided on create. +func (u *MentionProfileUpsertBulk) UpdatePostID() *MentionProfileUpsertBulk { + return u.Update(func(s *MentionProfileUpsert) { + s.UpdatePostID() + }) +} + +// Exec executes the query. +func (u *MentionProfileUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the MentionProfileCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MentionProfileCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MentionProfileUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/internal/ent/mentionprofile_delete.go b/internal/ent/mentionprofile_delete.go new file mode 100644 index 000000000..abfe491a0 --- /dev/null +++ b/internal/ent/mentionprofile_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" + "github.com/Southclaws/storyden/internal/ent/predicate" +) + +// MentionProfileDelete is the builder for deleting a MentionProfile entity. +type MentionProfileDelete struct { + config + hooks []Hook + mutation *MentionProfileMutation +} + +// Where appends a list predicates to the MentionProfileDelete builder. +func (mpd *MentionProfileDelete) Where(ps ...predicate.MentionProfile) *MentionProfileDelete { + mpd.mutation.Where(ps...) + return mpd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (mpd *MentionProfileDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, mpd.sqlExec, mpd.mutation, mpd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (mpd *MentionProfileDelete) ExecX(ctx context.Context) int { + n, err := mpd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (mpd *MentionProfileDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(mentionprofile.Table, sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString)) + if ps := mpd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, mpd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + mpd.mutation.done = true + return affected, err +} + +// MentionProfileDeleteOne is the builder for deleting a single MentionProfile entity. +type MentionProfileDeleteOne struct { + mpd *MentionProfileDelete +} + +// Where appends a list predicates to the MentionProfileDelete builder. +func (mpdo *MentionProfileDeleteOne) Where(ps ...predicate.MentionProfile) *MentionProfileDeleteOne { + mpdo.mpd.mutation.Where(ps...) + return mpdo +} + +// Exec executes the deletion query. +func (mpdo *MentionProfileDeleteOne) Exec(ctx context.Context) error { + n, err := mpdo.mpd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{mentionprofile.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (mpdo *MentionProfileDeleteOne) ExecX(ctx context.Context) { + if err := mpdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/internal/ent/mentionprofile_query.go b/internal/ent/mentionprofile_query.go new file mode 100644 index 000000000..d70b3cead --- /dev/null +++ b/internal/ent/mentionprofile_query.go @@ -0,0 +1,703 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/Southclaws/storyden/internal/ent/account" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" + "github.com/Southclaws/storyden/internal/ent/post" + "github.com/Southclaws/storyden/internal/ent/predicate" + "github.com/rs/xid" +) + +// MentionProfileQuery is the builder for querying MentionProfile entities. +type MentionProfileQuery struct { + config + ctx *QueryContext + order []mentionprofile.OrderOption + inters []Interceptor + predicates []predicate.MentionProfile + withAccount *AccountQuery + withPost *PostQuery + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the MentionProfileQuery builder. +func (mpq *MentionProfileQuery) Where(ps ...predicate.MentionProfile) *MentionProfileQuery { + mpq.predicates = append(mpq.predicates, ps...) + return mpq +} + +// Limit the number of records to be returned by this query. +func (mpq *MentionProfileQuery) Limit(limit int) *MentionProfileQuery { + mpq.ctx.Limit = &limit + return mpq +} + +// Offset to start from. +func (mpq *MentionProfileQuery) Offset(offset int) *MentionProfileQuery { + mpq.ctx.Offset = &offset + return mpq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (mpq *MentionProfileQuery) Unique(unique bool) *MentionProfileQuery { + mpq.ctx.Unique = &unique + return mpq +} + +// Order specifies how the records should be ordered. +func (mpq *MentionProfileQuery) Order(o ...mentionprofile.OrderOption) *MentionProfileQuery { + mpq.order = append(mpq.order, o...) + return mpq +} + +// QueryAccount chains the current query on the "account" edge. +func (mpq *MentionProfileQuery) QueryAccount() *AccountQuery { + query := (&AccountClient{config: mpq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := mpq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := mpq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(mentionprofile.Table, mentionprofile.FieldID, selector), + sqlgraph.To(account.Table, account.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, mentionprofile.AccountTable, mentionprofile.AccountColumn), + ) + fromU = sqlgraph.SetNeighbors(mpq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryPost chains the current query on the "Post" edge. +func (mpq *MentionProfileQuery) QueryPost() *PostQuery { + query := (&PostClient{config: mpq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := mpq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := mpq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(mentionprofile.Table, mentionprofile.FieldID, selector), + sqlgraph.To(post.Table, post.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, mentionprofile.PostTable, mentionprofile.PostColumn), + ) + fromU = sqlgraph.SetNeighbors(mpq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first MentionProfile entity from the query. +// Returns a *NotFoundError when no MentionProfile was found. +func (mpq *MentionProfileQuery) First(ctx context.Context) (*MentionProfile, error) { + nodes, err := mpq.Limit(1).All(setContextOp(ctx, mpq.ctx, "First")) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{mentionprofile.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (mpq *MentionProfileQuery) FirstX(ctx context.Context) *MentionProfile { + node, err := mpq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first MentionProfile ID from the query. +// Returns a *NotFoundError when no MentionProfile ID was found. +func (mpq *MentionProfileQuery) FirstID(ctx context.Context) (id xid.ID, err error) { + var ids []xid.ID + if ids, err = mpq.Limit(1).IDs(setContextOp(ctx, mpq.ctx, "FirstID")); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{mentionprofile.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (mpq *MentionProfileQuery) FirstIDX(ctx context.Context) xid.ID { + id, err := mpq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single MentionProfile entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one MentionProfile entity is found. +// Returns a *NotFoundError when no MentionProfile entities are found. +func (mpq *MentionProfileQuery) Only(ctx context.Context) (*MentionProfile, error) { + nodes, err := mpq.Limit(2).All(setContextOp(ctx, mpq.ctx, "Only")) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{mentionprofile.Label} + default: + return nil, &NotSingularError{mentionprofile.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (mpq *MentionProfileQuery) OnlyX(ctx context.Context) *MentionProfile { + node, err := mpq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only MentionProfile ID in the query. +// Returns a *NotSingularError when more than one MentionProfile ID is found. +// Returns a *NotFoundError when no entities are found. +func (mpq *MentionProfileQuery) OnlyID(ctx context.Context) (id xid.ID, err error) { + var ids []xid.ID + if ids, err = mpq.Limit(2).IDs(setContextOp(ctx, mpq.ctx, "OnlyID")); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{mentionprofile.Label} + default: + err = &NotSingularError{mentionprofile.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (mpq *MentionProfileQuery) OnlyIDX(ctx context.Context) xid.ID { + id, err := mpq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of MentionProfiles. +func (mpq *MentionProfileQuery) All(ctx context.Context) ([]*MentionProfile, error) { + ctx = setContextOp(ctx, mpq.ctx, "All") + if err := mpq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*MentionProfile, *MentionProfileQuery]() + return withInterceptors[[]*MentionProfile](ctx, mpq, qr, mpq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (mpq *MentionProfileQuery) AllX(ctx context.Context) []*MentionProfile { + nodes, err := mpq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of MentionProfile IDs. +func (mpq *MentionProfileQuery) IDs(ctx context.Context) (ids []xid.ID, err error) { + if mpq.ctx.Unique == nil && mpq.path != nil { + mpq.Unique(true) + } + ctx = setContextOp(ctx, mpq.ctx, "IDs") + if err = mpq.Select(mentionprofile.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (mpq *MentionProfileQuery) IDsX(ctx context.Context) []xid.ID { + ids, err := mpq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (mpq *MentionProfileQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, mpq.ctx, "Count") + if err := mpq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, mpq, querierCount[*MentionProfileQuery](), mpq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (mpq *MentionProfileQuery) CountX(ctx context.Context) int { + count, err := mpq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (mpq *MentionProfileQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, mpq.ctx, "Exist") + switch _, err := mpq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (mpq *MentionProfileQuery) ExistX(ctx context.Context) bool { + exist, err := mpq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the MentionProfileQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (mpq *MentionProfileQuery) Clone() *MentionProfileQuery { + if mpq == nil { + return nil + } + return &MentionProfileQuery{ + config: mpq.config, + ctx: mpq.ctx.Clone(), + order: append([]mentionprofile.OrderOption{}, mpq.order...), + inters: append([]Interceptor{}, mpq.inters...), + predicates: append([]predicate.MentionProfile{}, mpq.predicates...), + withAccount: mpq.withAccount.Clone(), + withPost: mpq.withPost.Clone(), + // clone intermediate query. + sql: mpq.sql.Clone(), + path: mpq.path, + } +} + +// WithAccount tells the query-builder to eager-load the nodes that are connected to +// the "account" edge. The optional arguments are used to configure the query builder of the edge. +func (mpq *MentionProfileQuery) WithAccount(opts ...func(*AccountQuery)) *MentionProfileQuery { + query := (&AccountClient{config: mpq.config}).Query() + for _, opt := range opts { + opt(query) + } + mpq.withAccount = query + return mpq +} + +// WithPost tells the query-builder to eager-load the nodes that are connected to +// the "Post" edge. The optional arguments are used to configure the query builder of the edge. +func (mpq *MentionProfileQuery) WithPost(opts ...func(*PostQuery)) *MentionProfileQuery { + query := (&PostClient{config: mpq.config}).Query() + for _, opt := range opts { + opt(query) + } + mpq.withPost = query + return mpq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"created_at,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.MentionProfile.Query(). +// GroupBy(mentionprofile.FieldCreatedAt). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (mpq *MentionProfileQuery) GroupBy(field string, fields ...string) *MentionProfileGroupBy { + mpq.ctx.Fields = append([]string{field}, fields...) + grbuild := &MentionProfileGroupBy{build: mpq} + grbuild.flds = &mpq.ctx.Fields + grbuild.label = mentionprofile.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CreatedAt time.Time `json:"created_at,omitempty"` +// } +// +// client.MentionProfile.Query(). +// Select(mentionprofile.FieldCreatedAt). +// Scan(ctx, &v) +func (mpq *MentionProfileQuery) Select(fields ...string) *MentionProfileSelect { + mpq.ctx.Fields = append(mpq.ctx.Fields, fields...) + sbuild := &MentionProfileSelect{MentionProfileQuery: mpq} + sbuild.label = mentionprofile.Label + sbuild.flds, sbuild.scan = &mpq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a MentionProfileSelect configured with the given aggregations. +func (mpq *MentionProfileQuery) Aggregate(fns ...AggregateFunc) *MentionProfileSelect { + return mpq.Select().Aggregate(fns...) +} + +func (mpq *MentionProfileQuery) prepareQuery(ctx context.Context) error { + for _, inter := range mpq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, mpq); err != nil { + return err + } + } + } + for _, f := range mpq.ctx.Fields { + if !mentionprofile.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if mpq.path != nil { + prev, err := mpq.path(ctx) + if err != nil { + return err + } + mpq.sql = prev + } + return nil +} + +func (mpq *MentionProfileQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*MentionProfile, error) { + var ( + nodes = []*MentionProfile{} + _spec = mpq.querySpec() + loadedTypes = [2]bool{ + mpq.withAccount != nil, + mpq.withPost != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*MentionProfile).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &MentionProfile{config: mpq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + if len(mpq.modifiers) > 0 { + _spec.Modifiers = mpq.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, mpq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := mpq.withAccount; query != nil { + if err := mpq.loadAccount(ctx, query, nodes, nil, + func(n *MentionProfile, e *Account) { n.Edges.Account = e }); err != nil { + return nil, err + } + } + if query := mpq.withPost; query != nil { + if err := mpq.loadPost(ctx, query, nodes, nil, + func(n *MentionProfile, e *Post) { n.Edges.Post = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (mpq *MentionProfileQuery) loadAccount(ctx context.Context, query *AccountQuery, nodes []*MentionProfile, init func(*MentionProfile), assign func(*MentionProfile, *Account)) error { + ids := make([]xid.ID, 0, len(nodes)) + nodeids := make(map[xid.ID][]*MentionProfile) + for i := range nodes { + fk := nodes[i].AccountID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(account.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "account_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (mpq *MentionProfileQuery) loadPost(ctx context.Context, query *PostQuery, nodes []*MentionProfile, init func(*MentionProfile), assign func(*MentionProfile, *Post)) error { + ids := make([]xid.ID, 0, len(nodes)) + nodeids := make(map[xid.ID][]*MentionProfile) + for i := range nodes { + fk := nodes[i].PostID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(post.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "post_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (mpq *MentionProfileQuery) sqlCount(ctx context.Context) (int, error) { + _spec := mpq.querySpec() + if len(mpq.modifiers) > 0 { + _spec.Modifiers = mpq.modifiers + } + _spec.Node.Columns = mpq.ctx.Fields + if len(mpq.ctx.Fields) > 0 { + _spec.Unique = mpq.ctx.Unique != nil && *mpq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, mpq.driver, _spec) +} + +func (mpq *MentionProfileQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(mentionprofile.Table, mentionprofile.Columns, sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString)) + _spec.From = mpq.sql + if unique := mpq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if mpq.path != nil { + _spec.Unique = true + } + if fields := mpq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, mentionprofile.FieldID) + for i := range fields { + if fields[i] != mentionprofile.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if mpq.withAccount != nil { + _spec.Node.AddColumnOnce(mentionprofile.FieldAccountID) + } + if mpq.withPost != nil { + _spec.Node.AddColumnOnce(mentionprofile.FieldPostID) + } + } + if ps := mpq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := mpq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := mpq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := mpq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (mpq *MentionProfileQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(mpq.driver.Dialect()) + t1 := builder.Table(mentionprofile.Table) + columns := mpq.ctx.Fields + if len(columns) == 0 { + columns = mentionprofile.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if mpq.sql != nil { + selector = mpq.sql + selector.Select(selector.Columns(columns...)...) + } + if mpq.ctx.Unique != nil && *mpq.ctx.Unique { + selector.Distinct() + } + for _, m := range mpq.modifiers { + m(selector) + } + for _, p := range mpq.predicates { + p(selector) + } + for _, p := range mpq.order { + p(selector) + } + if offset := mpq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := mpq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (mpq *MentionProfileQuery) Modify(modifiers ...func(s *sql.Selector)) *MentionProfileSelect { + mpq.modifiers = append(mpq.modifiers, modifiers...) + return mpq.Select() +} + +// MentionProfileGroupBy is the group-by builder for MentionProfile entities. +type MentionProfileGroupBy struct { + selector + build *MentionProfileQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (mpgb *MentionProfileGroupBy) Aggregate(fns ...AggregateFunc) *MentionProfileGroupBy { + mpgb.fns = append(mpgb.fns, fns...) + return mpgb +} + +// Scan applies the selector query and scans the result into the given value. +func (mpgb *MentionProfileGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, mpgb.build.ctx, "GroupBy") + if err := mpgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MentionProfileQuery, *MentionProfileGroupBy](ctx, mpgb.build, mpgb, mpgb.build.inters, v) +} + +func (mpgb *MentionProfileGroupBy) sqlScan(ctx context.Context, root *MentionProfileQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(mpgb.fns)) + for _, fn := range mpgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*mpgb.flds)+len(mpgb.fns)) + for _, f := range *mpgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*mpgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := mpgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// MentionProfileSelect is the builder for selecting fields of MentionProfile entities. +type MentionProfileSelect struct { + *MentionProfileQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (mps *MentionProfileSelect) Aggregate(fns ...AggregateFunc) *MentionProfileSelect { + mps.fns = append(mps.fns, fns...) + return mps +} + +// Scan applies the selector query and scans the result into the given value. +func (mps *MentionProfileSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, mps.ctx, "Select") + if err := mps.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MentionProfileQuery, *MentionProfileSelect](ctx, mps.MentionProfileQuery, mps, mps.inters, v) +} + +func (mps *MentionProfileSelect) sqlScan(ctx context.Context, root *MentionProfileQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(mps.fns)) + for _, fn := range mps.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*mps.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := mps.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (mps *MentionProfileSelect) Modify(modifiers ...func(s *sql.Selector)) *MentionProfileSelect { + mps.modifiers = append(mps.modifiers, modifiers...) + return mps +} diff --git a/internal/ent/mentionprofile_update.go b/internal/ent/mentionprofile_update.go new file mode 100644 index 000000000..17d30a24f --- /dev/null +++ b/internal/ent/mentionprofile_update.go @@ -0,0 +1,438 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/Southclaws/storyden/internal/ent/account" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" + "github.com/Southclaws/storyden/internal/ent/post" + "github.com/Southclaws/storyden/internal/ent/predicate" + "github.com/rs/xid" +) + +// MentionProfileUpdate is the builder for updating MentionProfile entities. +type MentionProfileUpdate struct { + config + hooks []Hook + mutation *MentionProfileMutation + modifiers []func(*sql.UpdateBuilder) +} + +// Where appends a list predicates to the MentionProfileUpdate builder. +func (mpu *MentionProfileUpdate) Where(ps ...predicate.MentionProfile) *MentionProfileUpdate { + mpu.mutation.Where(ps...) + return mpu +} + +// SetAccountID sets the "account_id" field. +func (mpu *MentionProfileUpdate) SetAccountID(x xid.ID) *MentionProfileUpdate { + mpu.mutation.SetAccountID(x) + return mpu +} + +// SetNillableAccountID sets the "account_id" field if the given value is not nil. +func (mpu *MentionProfileUpdate) SetNillableAccountID(x *xid.ID) *MentionProfileUpdate { + if x != nil { + mpu.SetAccountID(*x) + } + return mpu +} + +// SetPostID sets the "post_id" field. +func (mpu *MentionProfileUpdate) SetPostID(x xid.ID) *MentionProfileUpdate { + mpu.mutation.SetPostID(x) + return mpu +} + +// SetNillablePostID sets the "post_id" field if the given value is not nil. +func (mpu *MentionProfileUpdate) SetNillablePostID(x *xid.ID) *MentionProfileUpdate { + if x != nil { + mpu.SetPostID(*x) + } + return mpu +} + +// SetAccount sets the "account" edge to the Account entity. +func (mpu *MentionProfileUpdate) SetAccount(a *Account) *MentionProfileUpdate { + return mpu.SetAccountID(a.ID) +} + +// SetPost sets the "Post" edge to the Post entity. +func (mpu *MentionProfileUpdate) SetPost(p *Post) *MentionProfileUpdate { + return mpu.SetPostID(p.ID) +} + +// Mutation returns the MentionProfileMutation object of the builder. +func (mpu *MentionProfileUpdate) Mutation() *MentionProfileMutation { + return mpu.mutation +} + +// ClearAccount clears the "account" edge to the Account entity. +func (mpu *MentionProfileUpdate) ClearAccount() *MentionProfileUpdate { + mpu.mutation.ClearAccount() + return mpu +} + +// ClearPost clears the "Post" edge to the Post entity. +func (mpu *MentionProfileUpdate) ClearPost() *MentionProfileUpdate { + mpu.mutation.ClearPost() + return mpu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (mpu *MentionProfileUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, mpu.sqlSave, mpu.mutation, mpu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (mpu *MentionProfileUpdate) SaveX(ctx context.Context) int { + affected, err := mpu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (mpu *MentionProfileUpdate) Exec(ctx context.Context) error { + _, err := mpu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (mpu *MentionProfileUpdate) ExecX(ctx context.Context) { + if err := mpu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (mpu *MentionProfileUpdate) check() error { + if _, ok := mpu.mutation.AccountID(); mpu.mutation.AccountCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "MentionProfile.account"`) + } + if _, ok := mpu.mutation.PostID(); mpu.mutation.PostCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "MentionProfile.Post"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (mpu *MentionProfileUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *MentionProfileUpdate { + mpu.modifiers = append(mpu.modifiers, modifiers...) + return mpu +} + +func (mpu *MentionProfileUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := mpu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(mentionprofile.Table, mentionprofile.Columns, sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString)) + if ps := mpu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if mpu.mutation.AccountCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.AccountTable, + Columns: []string{mentionprofile.AccountColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(account.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := mpu.mutation.AccountIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.AccountTable, + Columns: []string{mentionprofile.AccountColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(account.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if mpu.mutation.PostCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.PostTable, + Columns: []string{mentionprofile.PostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(post.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := mpu.mutation.PostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.PostTable, + Columns: []string{mentionprofile.PostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(post.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(mpu.modifiers...) + if n, err = sqlgraph.UpdateNodes(ctx, mpu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{mentionprofile.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + mpu.mutation.done = true + return n, nil +} + +// MentionProfileUpdateOne is the builder for updating a single MentionProfile entity. +type MentionProfileUpdateOne struct { + config + fields []string + hooks []Hook + mutation *MentionProfileMutation + modifiers []func(*sql.UpdateBuilder) +} + +// SetAccountID sets the "account_id" field. +func (mpuo *MentionProfileUpdateOne) SetAccountID(x xid.ID) *MentionProfileUpdateOne { + mpuo.mutation.SetAccountID(x) + return mpuo +} + +// SetNillableAccountID sets the "account_id" field if the given value is not nil. +func (mpuo *MentionProfileUpdateOne) SetNillableAccountID(x *xid.ID) *MentionProfileUpdateOne { + if x != nil { + mpuo.SetAccountID(*x) + } + return mpuo +} + +// SetPostID sets the "post_id" field. +func (mpuo *MentionProfileUpdateOne) SetPostID(x xid.ID) *MentionProfileUpdateOne { + mpuo.mutation.SetPostID(x) + return mpuo +} + +// SetNillablePostID sets the "post_id" field if the given value is not nil. +func (mpuo *MentionProfileUpdateOne) SetNillablePostID(x *xid.ID) *MentionProfileUpdateOne { + if x != nil { + mpuo.SetPostID(*x) + } + return mpuo +} + +// SetAccount sets the "account" edge to the Account entity. +func (mpuo *MentionProfileUpdateOne) SetAccount(a *Account) *MentionProfileUpdateOne { + return mpuo.SetAccountID(a.ID) +} + +// SetPost sets the "Post" edge to the Post entity. +func (mpuo *MentionProfileUpdateOne) SetPost(p *Post) *MentionProfileUpdateOne { + return mpuo.SetPostID(p.ID) +} + +// Mutation returns the MentionProfileMutation object of the builder. +func (mpuo *MentionProfileUpdateOne) Mutation() *MentionProfileMutation { + return mpuo.mutation +} + +// ClearAccount clears the "account" edge to the Account entity. +func (mpuo *MentionProfileUpdateOne) ClearAccount() *MentionProfileUpdateOne { + mpuo.mutation.ClearAccount() + return mpuo +} + +// ClearPost clears the "Post" edge to the Post entity. +func (mpuo *MentionProfileUpdateOne) ClearPost() *MentionProfileUpdateOne { + mpuo.mutation.ClearPost() + return mpuo +} + +// Where appends a list predicates to the MentionProfileUpdate builder. +func (mpuo *MentionProfileUpdateOne) Where(ps ...predicate.MentionProfile) *MentionProfileUpdateOne { + mpuo.mutation.Where(ps...) + return mpuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (mpuo *MentionProfileUpdateOne) Select(field string, fields ...string) *MentionProfileUpdateOne { + mpuo.fields = append([]string{field}, fields...) + return mpuo +} + +// Save executes the query and returns the updated MentionProfile entity. +func (mpuo *MentionProfileUpdateOne) Save(ctx context.Context) (*MentionProfile, error) { + return withHooks(ctx, mpuo.sqlSave, mpuo.mutation, mpuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (mpuo *MentionProfileUpdateOne) SaveX(ctx context.Context) *MentionProfile { + node, err := mpuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (mpuo *MentionProfileUpdateOne) Exec(ctx context.Context) error { + _, err := mpuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (mpuo *MentionProfileUpdateOne) ExecX(ctx context.Context) { + if err := mpuo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (mpuo *MentionProfileUpdateOne) check() error { + if _, ok := mpuo.mutation.AccountID(); mpuo.mutation.AccountCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "MentionProfile.account"`) + } + if _, ok := mpuo.mutation.PostID(); mpuo.mutation.PostCleared() && !ok { + return errors.New(`ent: clearing a required unique edge "MentionProfile.Post"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (mpuo *MentionProfileUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *MentionProfileUpdateOne { + mpuo.modifiers = append(mpuo.modifiers, modifiers...) + return mpuo +} + +func (mpuo *MentionProfileUpdateOne) sqlSave(ctx context.Context) (_node *MentionProfile, err error) { + if err := mpuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(mentionprofile.Table, mentionprofile.Columns, sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString)) + id, ok := mpuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "MentionProfile.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := mpuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, mentionprofile.FieldID) + for _, f := range fields { + if !mentionprofile.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != mentionprofile.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := mpuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if mpuo.mutation.AccountCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.AccountTable, + Columns: []string{mentionprofile.AccountColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(account.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := mpuo.mutation.AccountIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.AccountTable, + Columns: []string{mentionprofile.AccountColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(account.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if mpuo.mutation.PostCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.PostTable, + Columns: []string{mentionprofile.PostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(post.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := mpuo.mutation.PostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: mentionprofile.PostTable, + Columns: []string{mentionprofile.PostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(post.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(mpuo.modifiers...) + _node = &MentionProfile{config: mpuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, mpuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{mentionprofile.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + mpuo.mutation.done = true + return _node, nil +} diff --git a/internal/ent/migrate/schema.go b/internal/ent/migrate/schema.go index 84ceb4096..8a68c473b 100644 --- a/internal/ent/migrate/schema.go +++ b/internal/ent/migrate/schema.go @@ -332,6 +332,40 @@ var ( }, }, } + // MentionProfilesColumns holds the columns for the "mention_profiles" table. + MentionProfilesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, Size: 20}, + {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + {Name: "account_id", Type: field.TypeString, Size: 20}, + {Name: "post_id", Type: field.TypeString, Size: 20}, + } + // MentionProfilesTable holds the schema information for the "mention_profiles" table. + MentionProfilesTable = &schema.Table{ + Name: "mention_profiles", + Columns: MentionProfilesColumns, + PrimaryKey: []*schema.Column{MentionProfilesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "mention_profiles_accounts_mentions", + Columns: []*schema.Column{MentionProfilesColumns[2]}, + RefColumns: []*schema.Column{AccountsColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "mention_profiles_posts_mentions", + Columns: []*schema.Column{MentionProfilesColumns[3]}, + RefColumns: []*schema.Column{PostsColumns[0]}, + OnDelete: schema.Cascade, + }, + }, + Indexes: []*schema.Index{ + { + Name: "unique_mentions_post", + Unique: true, + Columns: []*schema.Column{MentionProfilesColumns[2], MentionProfilesColumns[3]}, + }, + }, + } // NodesColumns holds the columns for the "nodes" table. NodesColumns = []*schema.Column{ {Name: "id", Type: field.TypeString, Size: 20}, @@ -774,6 +808,7 @@ var ( EmailsTable, LikePostsTable, LinksTable, + MentionProfilesTable, NodesTable, NotificationsTable, PostsTable, @@ -809,6 +844,8 @@ func init() { LikePostsTable.ForeignKeys[1].RefTable = PostsTable LinksTable.ForeignKeys[0].RefTable = AssetsTable LinksTable.ForeignKeys[1].RefTable = AssetsTable + MentionProfilesTable.ForeignKeys[0].RefTable = AccountsTable + MentionProfilesTable.ForeignKeys[1].RefTable = PostsTable NodesTable.ForeignKeys[0].RefTable = AccountsTable NodesTable.ForeignKeys[1].RefTable = LinksTable NodesTable.ForeignKeys[2].RefTable = NodesTable diff --git a/internal/ent/mutation.go b/internal/ent/mutation.go index dc5f71067..d98ecd981 100644 --- a/internal/ent/mutation.go +++ b/internal/ent/mutation.go @@ -22,6 +22,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -54,6 +55,7 @@ const ( TypeEmail = "Email" TypeLikePost = "LikePost" TypeLink = "Link" + TypeMentionProfile = "MentionProfile" TypeNode = "Node" TypeNotification = "Notification" TypePost = "Post" @@ -104,6 +106,9 @@ type AccountMutation struct { likes map[xid.ID]struct{} removedlikes map[xid.ID]struct{} clearedlikes bool + mentions map[xid.ID]struct{} + removedmentions map[xid.ID]struct{} + clearedmentions bool roles map[xid.ID]struct{} removedroles map[xid.ID]struct{} clearedroles bool @@ -1055,6 +1060,60 @@ func (m *AccountMutation) ResetLikes() { m.removedlikes = nil } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by ids. +func (m *AccountMutation) AddMentionIDs(ids ...xid.ID) { + if m.mentions == nil { + m.mentions = make(map[xid.ID]struct{}) + } + for i := range ids { + m.mentions[ids[i]] = struct{}{} + } +} + +// ClearMentions clears the "mentions" edge to the MentionProfile entity. +func (m *AccountMutation) ClearMentions() { + m.clearedmentions = true +} + +// MentionsCleared reports if the "mentions" edge to the MentionProfile entity was cleared. +func (m *AccountMutation) MentionsCleared() bool { + return m.clearedmentions +} + +// RemoveMentionIDs removes the "mentions" edge to the MentionProfile entity by IDs. +func (m *AccountMutation) RemoveMentionIDs(ids ...xid.ID) { + if m.removedmentions == nil { + m.removedmentions = make(map[xid.ID]struct{}) + } + for i := range ids { + delete(m.mentions, ids[i]) + m.removedmentions[ids[i]] = struct{}{} + } +} + +// RemovedMentions returns the removed IDs of the "mentions" edge to the MentionProfile entity. +func (m *AccountMutation) RemovedMentionsIDs() (ids []xid.ID) { + for id := range m.removedmentions { + ids = append(ids, id) + } + return +} + +// MentionsIDs returns the "mentions" edge IDs in the mutation. +func (m *AccountMutation) MentionsIDs() (ids []xid.ID) { + for id := range m.mentions { + ids = append(ids, id) + } + return +} + +// ResetMentions resets all changes to the "mentions" edge. +func (m *AccountMutation) ResetMentions() { + m.mentions = nil + m.clearedmentions = false + m.removedmentions = nil +} + // AddRoleIDs adds the "roles" edge to the Role entity by ids. func (m *AccountMutation) AddRoleIDs(ids ...xid.ID) { if m.roles == nil { @@ -1675,7 +1734,7 @@ func (m *AccountMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *AccountMutation) AddedEdges() []string { - edges := make([]string, 0, 14) + edges := make([]string, 0, 15) if m.emails != nil { edges = append(edges, account.EdgeEmails) } @@ -1700,6 +1759,9 @@ func (m *AccountMutation) AddedEdges() []string { if m.likes != nil { edges = append(edges, account.EdgeLikes) } + if m.mentions != nil { + edges = append(edges, account.EdgeMentions) + } if m.roles != nil { edges = append(edges, account.EdgeRoles) } @@ -1773,6 +1835,12 @@ func (m *AccountMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case account.EdgeMentions: + ids := make([]ent.Value, 0, len(m.mentions)) + for id := range m.mentions { + ids = append(ids, id) + } + return ids case account.EdgeRoles: ids := make([]ent.Value, 0, len(m.roles)) for id := range m.roles { @@ -1815,7 +1883,7 @@ func (m *AccountMutation) AddedIDs(name string) []ent.Value { // RemovedEdges returns all edge names that were removed in this mutation. func (m *AccountMutation) RemovedEdges() []string { - edges := make([]string, 0, 14) + edges := make([]string, 0, 15) if m.removedemails != nil { edges = append(edges, account.EdgeEmails) } @@ -1840,6 +1908,9 @@ func (m *AccountMutation) RemovedEdges() []string { if m.removedlikes != nil { edges = append(edges, account.EdgeLikes) } + if m.removedmentions != nil { + edges = append(edges, account.EdgeMentions) + } if m.removedroles != nil { edges = append(edges, account.EdgeRoles) } @@ -1913,6 +1984,12 @@ func (m *AccountMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case account.EdgeMentions: + ids := make([]ent.Value, 0, len(m.removedmentions)) + for id := range m.removedmentions { + ids = append(ids, id) + } + return ids case account.EdgeRoles: ids := make([]ent.Value, 0, len(m.removedroles)) for id := range m.removedroles { @@ -1955,7 +2032,7 @@ func (m *AccountMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *AccountMutation) ClearedEdges() []string { - edges := make([]string, 0, 14) + edges := make([]string, 0, 15) if m.clearedemails { edges = append(edges, account.EdgeEmails) } @@ -1980,6 +2057,9 @@ func (m *AccountMutation) ClearedEdges() []string { if m.clearedlikes { edges = append(edges, account.EdgeLikes) } + if m.clearedmentions { + edges = append(edges, account.EdgeMentions) + } if m.clearedroles { edges = append(edges, account.EdgeRoles) } @@ -2021,6 +2101,8 @@ func (m *AccountMutation) EdgeCleared(name string) bool { return m.clearedreacts case account.EdgeLikes: return m.clearedlikes + case account.EdgeMentions: + return m.clearedmentions case account.EdgeRoles: return m.clearedroles case account.EdgeAuthentication: @@ -2073,6 +2155,9 @@ func (m *AccountMutation) ResetEdge(name string) error { case account.EdgeLikes: m.ResetLikes() return nil + case account.EdgeMentions: + m.ResetMentions() + return nil case account.EdgeRoles: m.ResetRoles() return nil @@ -9620,6 +9705,546 @@ func (m *LinkMutation) ResetEdge(name string) error { return fmt.Errorf("unknown Link edge %s", name) } +// MentionProfileMutation represents an operation that mutates the MentionProfile nodes in the graph. +type MentionProfileMutation struct { + config + op Op + typ string + id *xid.ID + created_at *time.Time + clearedFields map[string]struct{} + account *xid.ID + clearedaccount bool + _Post *xid.ID + cleared_Post bool + done bool + oldValue func(context.Context) (*MentionProfile, error) + predicates []predicate.MentionProfile +} + +var _ ent.Mutation = (*MentionProfileMutation)(nil) + +// mentionprofileOption allows management of the mutation configuration using functional options. +type mentionprofileOption func(*MentionProfileMutation) + +// newMentionProfileMutation creates new mutation for the MentionProfile entity. +func newMentionProfileMutation(c config, op Op, opts ...mentionprofileOption) *MentionProfileMutation { + m := &MentionProfileMutation{ + config: c, + op: op, + typ: TypeMentionProfile, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withMentionProfileID sets the ID field of the mutation. +func withMentionProfileID(id xid.ID) mentionprofileOption { + return func(m *MentionProfileMutation) { + var ( + err error + once sync.Once + value *MentionProfile + ) + m.oldValue = func(ctx context.Context) (*MentionProfile, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().MentionProfile.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withMentionProfile sets the old MentionProfile of the mutation. +func withMentionProfile(node *MentionProfile) mentionprofileOption { + return func(m *MentionProfileMutation) { + m.oldValue = func(context.Context) (*MentionProfile, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m MentionProfileMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m MentionProfileMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of MentionProfile entities. +func (m *MentionProfileMutation) SetID(id xid.ID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *MentionProfileMutation) ID() (id xid.ID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *MentionProfileMutation) IDs(ctx context.Context) ([]xid.ID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []xid.ID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().MentionProfile.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedAt sets the "created_at" field. +func (m *MentionProfileMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *MentionProfileMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the MentionProfile entity. +// If the MentionProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MentionProfileMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *MentionProfileMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetAccountID sets the "account_id" field. +func (m *MentionProfileMutation) SetAccountID(x xid.ID) { + m.account = &x +} + +// AccountID returns the value of the "account_id" field in the mutation. +func (m *MentionProfileMutation) AccountID() (r xid.ID, exists bool) { + v := m.account + if v == nil { + return + } + return *v, true +} + +// OldAccountID returns the old "account_id" field's value of the MentionProfile entity. +// If the MentionProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MentionProfileMutation) OldAccountID(ctx context.Context) (v xid.ID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAccountID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAccountID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAccountID: %w", err) + } + return oldValue.AccountID, nil +} + +// ResetAccountID resets all changes to the "account_id" field. +func (m *MentionProfileMutation) ResetAccountID() { + m.account = nil +} + +// SetPostID sets the "post_id" field. +func (m *MentionProfileMutation) SetPostID(x xid.ID) { + m._Post = &x +} + +// PostID returns the value of the "post_id" field in the mutation. +func (m *MentionProfileMutation) PostID() (r xid.ID, exists bool) { + v := m._Post + if v == nil { + return + } + return *v, true +} + +// OldPostID returns the old "post_id" field's value of the MentionProfile entity. +// If the MentionProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MentionProfileMutation) OldPostID(ctx context.Context) (v xid.ID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPostID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPostID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPostID: %w", err) + } + return oldValue.PostID, nil +} + +// ResetPostID resets all changes to the "post_id" field. +func (m *MentionProfileMutation) ResetPostID() { + m._Post = nil +} + +// ClearAccount clears the "account" edge to the Account entity. +func (m *MentionProfileMutation) ClearAccount() { + m.clearedaccount = true + m.clearedFields[mentionprofile.FieldAccountID] = struct{}{} +} + +// AccountCleared reports if the "account" edge to the Account entity was cleared. +func (m *MentionProfileMutation) AccountCleared() bool { + return m.clearedaccount +} + +// AccountIDs returns the "account" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// AccountID instead. It exists only for internal usage by the builders. +func (m *MentionProfileMutation) AccountIDs() (ids []xid.ID) { + if id := m.account; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetAccount resets all changes to the "account" edge. +func (m *MentionProfileMutation) ResetAccount() { + m.account = nil + m.clearedaccount = false +} + +// ClearPost clears the "Post" edge to the Post entity. +func (m *MentionProfileMutation) ClearPost() { + m.cleared_Post = true + m.clearedFields[mentionprofile.FieldPostID] = struct{}{} +} + +// PostCleared reports if the "Post" edge to the Post entity was cleared. +func (m *MentionProfileMutation) PostCleared() bool { + return m.cleared_Post +} + +// PostIDs returns the "Post" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// PostID instead. It exists only for internal usage by the builders. +func (m *MentionProfileMutation) PostIDs() (ids []xid.ID) { + if id := m._Post; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetPost resets all changes to the "Post" edge. +func (m *MentionProfileMutation) ResetPost() { + m._Post = nil + m.cleared_Post = false +} + +// Where appends a list predicates to the MentionProfileMutation builder. +func (m *MentionProfileMutation) Where(ps ...predicate.MentionProfile) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the MentionProfileMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *MentionProfileMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.MentionProfile, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *MentionProfileMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *MentionProfileMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (MentionProfile). +func (m *MentionProfileMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *MentionProfileMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.created_at != nil { + fields = append(fields, mentionprofile.FieldCreatedAt) + } + if m.account != nil { + fields = append(fields, mentionprofile.FieldAccountID) + } + if m._Post != nil { + fields = append(fields, mentionprofile.FieldPostID) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *MentionProfileMutation) Field(name string) (ent.Value, bool) { + switch name { + case mentionprofile.FieldCreatedAt: + return m.CreatedAt() + case mentionprofile.FieldAccountID: + return m.AccountID() + case mentionprofile.FieldPostID: + return m.PostID() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *MentionProfileMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case mentionprofile.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case mentionprofile.FieldAccountID: + return m.OldAccountID(ctx) + case mentionprofile.FieldPostID: + return m.OldPostID(ctx) + } + return nil, fmt.Errorf("unknown MentionProfile field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MentionProfileMutation) SetField(name string, value ent.Value) error { + switch name { + case mentionprofile.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case mentionprofile.FieldAccountID: + v, ok := value.(xid.ID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAccountID(v) + return nil + case mentionprofile.FieldPostID: + v, ok := value.(xid.ID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPostID(v) + return nil + } + return fmt.Errorf("unknown MentionProfile field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *MentionProfileMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *MentionProfileMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MentionProfileMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown MentionProfile numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *MentionProfileMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *MentionProfileMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *MentionProfileMutation) ClearField(name string) error { + return fmt.Errorf("unknown MentionProfile nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *MentionProfileMutation) ResetField(name string) error { + switch name { + case mentionprofile.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case mentionprofile.FieldAccountID: + m.ResetAccountID() + return nil + case mentionprofile.FieldPostID: + m.ResetPostID() + return nil + } + return fmt.Errorf("unknown MentionProfile field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *MentionProfileMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.account != nil { + edges = append(edges, mentionprofile.EdgeAccount) + } + if m._Post != nil { + edges = append(edges, mentionprofile.EdgePost) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *MentionProfileMutation) AddedIDs(name string) []ent.Value { + switch name { + case mentionprofile.EdgeAccount: + if id := m.account; id != nil { + return []ent.Value{*id} + } + case mentionprofile.EdgePost: + if id := m._Post; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *MentionProfileMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *MentionProfileMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *MentionProfileMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedaccount { + edges = append(edges, mentionprofile.EdgeAccount) + } + if m.cleared_Post { + edges = append(edges, mentionprofile.EdgePost) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *MentionProfileMutation) EdgeCleared(name string) bool { + switch name { + case mentionprofile.EdgeAccount: + return m.clearedaccount + case mentionprofile.EdgePost: + return m.cleared_Post + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *MentionProfileMutation) ClearEdge(name string) error { + switch name { + case mentionprofile.EdgeAccount: + m.ClearAccount() + return nil + case mentionprofile.EdgePost: + m.ClearPost() + return nil + } + return fmt.Errorf("unknown MentionProfile unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *MentionProfileMutation) ResetEdge(name string) error { + switch name { + case mentionprofile.EdgeAccount: + m.ResetAccount() + return nil + case mentionprofile.EdgePost: + m.ResetPost() + return nil + } + return fmt.Errorf("unknown MentionProfile edge %s", name) +} + // NodeMutation represents an operation that mutates the Node nodes in the graph. type NodeMutation struct { config @@ -12208,6 +12833,9 @@ type PostMutation struct { likes map[xid.ID]struct{} removedlikes map[xid.ID]struct{} clearedlikes bool + mentions map[xid.ID]struct{} + removedmentions map[xid.ID]struct{} + clearedmentions bool assets map[xid.ID]struct{} removedassets map[xid.ID]struct{} clearedassets bool @@ -13388,6 +14016,60 @@ func (m *PostMutation) ResetLikes() { m.removedlikes = nil } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by ids. +func (m *PostMutation) AddMentionIDs(ids ...xid.ID) { + if m.mentions == nil { + m.mentions = make(map[xid.ID]struct{}) + } + for i := range ids { + m.mentions[ids[i]] = struct{}{} + } +} + +// ClearMentions clears the "mentions" edge to the MentionProfile entity. +func (m *PostMutation) ClearMentions() { + m.clearedmentions = true +} + +// MentionsCleared reports if the "mentions" edge to the MentionProfile entity was cleared. +func (m *PostMutation) MentionsCleared() bool { + return m.clearedmentions +} + +// RemoveMentionIDs removes the "mentions" edge to the MentionProfile entity by IDs. +func (m *PostMutation) RemoveMentionIDs(ids ...xid.ID) { + if m.removedmentions == nil { + m.removedmentions = make(map[xid.ID]struct{}) + } + for i := range ids { + delete(m.mentions, ids[i]) + m.removedmentions[ids[i]] = struct{}{} + } +} + +// RemovedMentions returns the removed IDs of the "mentions" edge to the MentionProfile entity. +func (m *PostMutation) RemovedMentionsIDs() (ids []xid.ID) { + for id := range m.removedmentions { + ids = append(ids, id) + } + return +} + +// MentionsIDs returns the "mentions" edge IDs in the mutation. +func (m *PostMutation) MentionsIDs() (ids []xid.ID) { + for id := range m.mentions { + ids = append(ids, id) + } + return +} + +// ResetMentions resets all changes to the "mentions" edge. +func (m *PostMutation) ResetMentions() { + m.mentions = nil + m.clearedmentions = false + m.removedmentions = nil +} + // AddAssetIDs adds the "assets" edge to the Asset entity by ids. func (m *PostMutation) AddAssetIDs(ids ...xid.ID) { if m.assets == nil { @@ -13999,7 +14681,7 @@ func (m *PostMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *PostMutation) AddedEdges() []string { - edges := make([]string, 0, 13) + edges := make([]string, 0, 14) if m.author != nil { edges = append(edges, post.EdgeAuthor) } @@ -14027,6 +14709,9 @@ func (m *PostMutation) AddedEdges() []string { if m.likes != nil { edges = append(edges, post.EdgeLikes) } + if m.mentions != nil { + edges = append(edges, post.EdgeMentions) + } if m.assets != nil { edges = append(edges, post.EdgeAssets) } @@ -14092,6 +14777,12 @@ func (m *PostMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case post.EdgeMentions: + ids := make([]ent.Value, 0, len(m.mentions)) + for id := range m.mentions { + ids = append(ids, id) + } + return ids case post.EdgeAssets: ids := make([]ent.Value, 0, len(m.assets)) for id := range m.assets { @@ -14120,7 +14811,7 @@ func (m *PostMutation) AddedIDs(name string) []ent.Value { // RemovedEdges returns all edge names that were removed in this mutation. func (m *PostMutation) RemovedEdges() []string { - edges := make([]string, 0, 13) + edges := make([]string, 0, 14) if m.removedtags != nil { edges = append(edges, post.EdgeTags) } @@ -14136,6 +14827,9 @@ func (m *PostMutation) RemovedEdges() []string { if m.removedlikes != nil { edges = append(edges, post.EdgeLikes) } + if m.removedmentions != nil { + edges = append(edges, post.EdgeMentions) + } if m.removedassets != nil { edges = append(edges, post.EdgeAssets) } @@ -14182,6 +14876,12 @@ func (m *PostMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case post.EdgeMentions: + ids := make([]ent.Value, 0, len(m.removedmentions)) + for id := range m.removedmentions { + ids = append(ids, id) + } + return ids case post.EdgeAssets: ids := make([]ent.Value, 0, len(m.removedassets)) for id := range m.removedassets { @@ -14206,7 +14906,7 @@ func (m *PostMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *PostMutation) ClearedEdges() []string { - edges := make([]string, 0, 13) + edges := make([]string, 0, 14) if m.clearedauthor { edges = append(edges, post.EdgeAuthor) } @@ -14234,6 +14934,9 @@ func (m *PostMutation) ClearedEdges() []string { if m.clearedlikes { edges = append(edges, post.EdgeLikes) } + if m.clearedmentions { + edges = append(edges, post.EdgeMentions) + } if m.clearedassets { edges = append(edges, post.EdgeAssets) } @@ -14271,6 +14974,8 @@ func (m *PostMutation) EdgeCleared(name string) bool { return m.clearedreacts case post.EdgeLikes: return m.clearedlikes + case post.EdgeMentions: + return m.clearedmentions case post.EdgeAssets: return m.clearedassets case post.EdgeCollections: @@ -14337,6 +15042,9 @@ func (m *PostMutation) ResetEdge(name string) error { case post.EdgeLikes: m.ResetLikes() return nil + case post.EdgeMentions: + m.ResetMentions() + return nil case post.EdgeAssets: m.ResetAssets() return nil diff --git a/internal/ent/post.go b/internal/ent/post.go index 07f0f198e..ec0dde9da 100644 --- a/internal/ent/post.go +++ b/internal/ent/post.go @@ -79,6 +79,8 @@ type PostEdges struct { Reacts []*React `json:"reacts,omitempty"` // Likes holds the value of the likes edge. Likes []*LikePost `json:"likes,omitempty"` + // Mentions holds the value of the mentions edge. + Mentions []*MentionProfile `json:"mentions,omitempty"` // Assets holds the value of the assets edge. Assets []*Asset `json:"assets,omitempty"` // Collections holds the value of the collections edge. @@ -89,7 +91,7 @@ type PostEdges struct { ContentLinks []*Link `json:"content_links,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [13]bool + loadedTypes [14]bool } // AuthorOrErr returns the Author value or an error if the edge @@ -181,10 +183,19 @@ func (e PostEdges) LikesOrErr() ([]*LikePost, error) { return nil, &NotLoadedError{edge: "likes"} } +// MentionsOrErr returns the Mentions value or an error if the edge +// was not loaded in eager-loading. +func (e PostEdges) MentionsOrErr() ([]*MentionProfile, error) { + if e.loadedTypes[9] { + return e.Mentions, nil + } + return nil, &NotLoadedError{edge: "mentions"} +} + // AssetsOrErr returns the Assets value or an error if the edge // was not loaded in eager-loading. func (e PostEdges) AssetsOrErr() ([]*Asset, error) { - if e.loadedTypes[9] { + if e.loadedTypes[10] { return e.Assets, nil } return nil, &NotLoadedError{edge: "assets"} @@ -193,7 +204,7 @@ func (e PostEdges) AssetsOrErr() ([]*Asset, error) { // CollectionsOrErr returns the Collections value or an error if the edge // was not loaded in eager-loading. func (e PostEdges) CollectionsOrErr() ([]*Collection, error) { - if e.loadedTypes[10] { + if e.loadedTypes[11] { return e.Collections, nil } return nil, &NotLoadedError{edge: "collections"} @@ -204,7 +215,7 @@ func (e PostEdges) CollectionsOrErr() ([]*Collection, error) { func (e PostEdges) LinkOrErr() (*Link, error) { if e.Link != nil { return e.Link, nil - } else if e.loadedTypes[11] { + } else if e.loadedTypes[12] { return nil, &NotFoundError{label: link.Label} } return nil, &NotLoadedError{edge: "link"} @@ -213,7 +224,7 @@ func (e PostEdges) LinkOrErr() (*Link, error) { // ContentLinksOrErr returns the ContentLinks value or an error if the edge // was not loaded in eager-loading. func (e PostEdges) ContentLinksOrErr() ([]*Link, error) { - if e.loadedTypes[12] { + if e.loadedTypes[13] { return e.ContentLinks, nil } return nil, &NotLoadedError{edge: "content_links"} @@ -415,6 +426,11 @@ func (po *Post) QueryLikes() *LikePostQuery { return NewPostClient(po.config).QueryLikes(po) } +// QueryMentions queries the "mentions" edge of the Post entity. +func (po *Post) QueryMentions() *MentionProfileQuery { + return NewPostClient(po.config).QueryMentions(po) +} + // QueryAssets queries the "assets" edge of the Post entity. func (po *Post) QueryAssets() *AssetQuery { return NewPostClient(po.config).QueryAssets(po) diff --git a/internal/ent/post/post.go b/internal/ent/post/post.go index 8bd49c0f3..1a79d4b05 100644 --- a/internal/ent/post/post.go +++ b/internal/ent/post/post.go @@ -64,6 +64,8 @@ const ( EdgeReacts = "reacts" // EdgeLikes holds the string denoting the likes edge name in mutations. EdgeLikes = "likes" + // EdgeMentions holds the string denoting the mentions edge name in mutations. + EdgeMentions = "mentions" // EdgeAssets holds the string denoting the assets edge name in mutations. EdgeAssets = "assets" // EdgeCollections holds the string denoting the collections edge name in mutations. @@ -123,6 +125,13 @@ const ( LikesInverseTable = "like_posts" // LikesColumn is the table column denoting the likes relation/edge. LikesColumn = "post_id" + // MentionsTable is the table that holds the mentions relation/edge. + MentionsTable = "mention_profiles" + // MentionsInverseTable is the table name for the MentionProfile entity. + // It exists in this package in order to avoid circular dependency with the "mentionprofile" package. + MentionsInverseTable = "mention_profiles" + // MentionsColumn is the table column denoting the mentions relation/edge. + MentionsColumn = "post_id" // AssetsTable is the table that holds the assets relation/edge. The primary key declared below. AssetsTable = "post_assets" // AssetsInverseTable is the table name for the Asset entity. @@ -422,6 +431,20 @@ func ByLikes(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { } } +// ByMentionsCount orders the results by mentions count. +func ByMentionsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newMentionsStep(), opts...) + } +} + +// ByMentions orders the results by mentions terms. +func ByMentions(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newMentionsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + // ByAssetsCount orders the results by assets count. func ByAssetsCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { @@ -533,6 +556,13 @@ func newLikesStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, LikesTable, LikesColumn), ) } +func newMentionsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(MentionsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, MentionsTable, MentionsColumn), + ) +} func newAssetsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), diff --git a/internal/ent/post/where.go b/internal/ent/post/where.go index e2641ed92..35ed0a8fe 100644 --- a/internal/ent/post/where.go +++ b/internal/ent/post/where.go @@ -1108,6 +1108,29 @@ func HasLikesWith(preds ...predicate.LikePost) predicate.Post { }) } +// HasMentions applies the HasEdge predicate on the "mentions" edge. +func HasMentions() predicate.Post { + return predicate.Post(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, MentionsTable, MentionsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasMentionsWith applies the HasEdge predicate on the "mentions" edge with a given conditions (other predicates). +func HasMentionsWith(preds ...predicate.MentionProfile) predicate.Post { + return predicate.Post(func(s *sql.Selector) { + step := newMentionsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // HasAssets applies the HasEdge predicate on the "assets" edge. func HasAssets() predicate.Post { return predicate.Post(func(s *sql.Selector) { diff --git a/internal/ent/post_create.go b/internal/ent/post_create.go index 5d8b87d7a..8a94b2d55 100644 --- a/internal/ent/post_create.go +++ b/internal/ent/post_create.go @@ -18,6 +18,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/collection" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/post" "github.com/Southclaws/storyden/internal/ent/react" "github.com/Southclaws/storyden/internal/ent/tag" @@ -353,6 +354,21 @@ func (pc *PostCreate) AddLikes(l ...*LikePost) *PostCreate { return pc.AddLikeIDs(ids...) } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by IDs. +func (pc *PostCreate) AddMentionIDs(ids ...xid.ID) *PostCreate { + pc.mutation.AddMentionIDs(ids...) + return pc +} + +// AddMentions adds the "mentions" edges to the MentionProfile entity. +func (pc *PostCreate) AddMentions(m ...*MentionProfile) *PostCreate { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return pc.AddMentionIDs(ids...) +} + // AddAssetIDs adds the "assets" edge to the Asset entity by IDs. func (pc *PostCreate) AddAssetIDs(ids ...xid.ID) *PostCreate { pc.mutation.AddAssetIDs(ids...) @@ -724,6 +740,22 @@ func (pc *PostCreate) createSpec() (*Post, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := pc.mutation.MentionsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } if nodes := pc.mutation.AssetsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, diff --git a/internal/ent/post_query.go b/internal/ent/post_query.go index 8a9899cd0..40769b50b 100644 --- a/internal/ent/post_query.go +++ b/internal/ent/post_query.go @@ -17,6 +17,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/collection" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/post" "github.com/Southclaws/storyden/internal/ent/predicate" "github.com/Southclaws/storyden/internal/ent/react" @@ -40,6 +41,7 @@ type PostQuery struct { withReplies *PostQuery withReacts *ReactQuery withLikes *LikePostQuery + withMentions *MentionProfileQuery withAssets *AssetQuery withCollections *CollectionQuery withLink *LinkQuery @@ -280,6 +282,28 @@ func (pq *PostQuery) QueryLikes() *LikePostQuery { return query } +// QueryMentions chains the current query on the "mentions" edge. +func (pq *PostQuery) QueryMentions() *MentionProfileQuery { + query := (&MentionProfileClient{config: pq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := pq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := pq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(post.Table, post.FieldID, selector), + sqlgraph.To(mentionprofile.Table, mentionprofile.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, post.MentionsTable, post.MentionsColumn), + ) + fromU = sqlgraph.SetNeighbors(pq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // QueryAssets chains the current query on the "assets" edge. func (pq *PostQuery) QueryAssets() *AssetQuery { query := (&AssetClient{config: pq.config}).Query() @@ -569,6 +593,7 @@ func (pq *PostQuery) Clone() *PostQuery { withReplies: pq.withReplies.Clone(), withReacts: pq.withReacts.Clone(), withLikes: pq.withLikes.Clone(), + withMentions: pq.withMentions.Clone(), withAssets: pq.withAssets.Clone(), withCollections: pq.withCollections.Clone(), withLink: pq.withLink.Clone(), @@ -678,6 +703,17 @@ func (pq *PostQuery) WithLikes(opts ...func(*LikePostQuery)) *PostQuery { return pq } +// WithMentions tells the query-builder to eager-load the nodes that are connected to +// the "mentions" edge. The optional arguments are used to configure the query builder of the edge. +func (pq *PostQuery) WithMentions(opts ...func(*MentionProfileQuery)) *PostQuery { + query := (&MentionProfileClient{config: pq.config}).Query() + for _, opt := range opts { + opt(query) + } + pq.withMentions = query + return pq +} + // WithAssets tells the query-builder to eager-load the nodes that are connected to // the "assets" edge. The optional arguments are used to configure the query builder of the edge. func (pq *PostQuery) WithAssets(opts ...func(*AssetQuery)) *PostQuery { @@ -801,7 +837,7 @@ func (pq *PostQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Post, e nodes = []*Post{} withFKs = pq.withFKs _spec = pq.querySpec() - loadedTypes = [13]bool{ + loadedTypes = [14]bool{ pq.withAuthor != nil, pq.withCategory != nil, pq.withTags != nil, @@ -811,6 +847,7 @@ func (pq *PostQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Post, e pq.withReplies != nil, pq.withReacts != nil, pq.withLikes != nil, + pq.withMentions != nil, pq.withAssets != nil, pq.withCollections != nil, pq.withLink != nil, @@ -903,6 +940,13 @@ func (pq *PostQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Post, e return nil, err } } + if query := pq.withMentions; query != nil { + if err := pq.loadMentions(ctx, query, nodes, + func(n *Post) { n.Edges.Mentions = []*MentionProfile{} }, + func(n *Post, e *MentionProfile) { n.Edges.Mentions = append(n.Edges.Mentions, e) }); err != nil { + return nil, err + } + } if query := pq.withAssets; query != nil { if err := pq.loadAssets(ctx, query, nodes, func(n *Post) { n.Edges.Assets = []*Asset{} }, @@ -1235,6 +1279,36 @@ func (pq *PostQuery) loadLikes(ctx context.Context, query *LikePostQuery, nodes } return nil } +func (pq *PostQuery) loadMentions(ctx context.Context, query *MentionProfileQuery, nodes []*Post, init func(*Post), assign func(*Post, *MentionProfile)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[xid.ID]*Post) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(mentionprofile.FieldPostID) + } + query.Where(predicate.MentionProfile(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(post.MentionsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.PostID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "post_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (pq *PostQuery) loadAssets(ctx context.Context, query *AssetQuery, nodes []*Post, init func(*Post), assign func(*Post, *Asset)) error { edgeIDs := make([]driver.Value, len(nodes)) byID := make(map[xid.ID]*Post) diff --git a/internal/ent/post_update.go b/internal/ent/post_update.go index fa2bb3dbb..ad7e34b09 100644 --- a/internal/ent/post_update.go +++ b/internal/ent/post_update.go @@ -17,6 +17,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/collection" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/post" "github.com/Southclaws/storyden/internal/ent/predicate" "github.com/Southclaws/storyden/internal/ent/react" @@ -395,6 +396,21 @@ func (pu *PostUpdate) AddLikes(l ...*LikePost) *PostUpdate { return pu.AddLikeIDs(ids...) } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by IDs. +func (pu *PostUpdate) AddMentionIDs(ids ...xid.ID) *PostUpdate { + pu.mutation.AddMentionIDs(ids...) + return pu +} + +// AddMentions adds the "mentions" edges to the MentionProfile entity. +func (pu *PostUpdate) AddMentions(m ...*MentionProfile) *PostUpdate { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return pu.AddMentionIDs(ids...) +} + // AddAssetIDs adds the "assets" edge to the Asset entity by IDs. func (pu *PostUpdate) AddAssetIDs(ids ...xid.ID) *PostUpdate { pu.mutation.AddAssetIDs(ids...) @@ -579,6 +595,27 @@ func (pu *PostUpdate) RemoveLikes(l ...*LikePost) *PostUpdate { return pu.RemoveLikeIDs(ids...) } +// ClearMentions clears all "mentions" edges to the MentionProfile entity. +func (pu *PostUpdate) ClearMentions() *PostUpdate { + pu.mutation.ClearMentions() + return pu +} + +// RemoveMentionIDs removes the "mentions" edge to MentionProfile entities by IDs. +func (pu *PostUpdate) RemoveMentionIDs(ids ...xid.ID) *PostUpdate { + pu.mutation.RemoveMentionIDs(ids...) + return pu +} + +// RemoveMentions removes "mentions" edges to MentionProfile entities. +func (pu *PostUpdate) RemoveMentions(m ...*MentionProfile) *PostUpdate { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return pu.RemoveMentionIDs(ids...) +} + // ClearAssets clears all "assets" edges to the Asset entity. func (pu *PostUpdate) ClearAssets() *PostUpdate { pu.mutation.ClearAssets() @@ -1098,6 +1135,51 @@ func (pu *PostUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if pu.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := pu.mutation.RemovedMentionsIDs(); len(nodes) > 0 && !pu.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := pu.mutation.MentionsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if pu.mutation.AssetsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, @@ -1641,6 +1723,21 @@ func (puo *PostUpdateOne) AddLikes(l ...*LikePost) *PostUpdateOne { return puo.AddLikeIDs(ids...) } +// AddMentionIDs adds the "mentions" edge to the MentionProfile entity by IDs. +func (puo *PostUpdateOne) AddMentionIDs(ids ...xid.ID) *PostUpdateOne { + puo.mutation.AddMentionIDs(ids...) + return puo +} + +// AddMentions adds the "mentions" edges to the MentionProfile entity. +func (puo *PostUpdateOne) AddMentions(m ...*MentionProfile) *PostUpdateOne { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return puo.AddMentionIDs(ids...) +} + // AddAssetIDs adds the "assets" edge to the Asset entity by IDs. func (puo *PostUpdateOne) AddAssetIDs(ids ...xid.ID) *PostUpdateOne { puo.mutation.AddAssetIDs(ids...) @@ -1825,6 +1922,27 @@ func (puo *PostUpdateOne) RemoveLikes(l ...*LikePost) *PostUpdateOne { return puo.RemoveLikeIDs(ids...) } +// ClearMentions clears all "mentions" edges to the MentionProfile entity. +func (puo *PostUpdateOne) ClearMentions() *PostUpdateOne { + puo.mutation.ClearMentions() + return puo +} + +// RemoveMentionIDs removes the "mentions" edge to MentionProfile entities by IDs. +func (puo *PostUpdateOne) RemoveMentionIDs(ids ...xid.ID) *PostUpdateOne { + puo.mutation.RemoveMentionIDs(ids...) + return puo +} + +// RemoveMentions removes "mentions" edges to MentionProfile entities. +func (puo *PostUpdateOne) RemoveMentions(m ...*MentionProfile) *PostUpdateOne { + ids := make([]xid.ID, len(m)) + for i := range m { + ids[i] = m[i].ID + } + return puo.RemoveMentionIDs(ids...) +} + // ClearAssets clears all "assets" edges to the Asset entity. func (puo *PostUpdateOne) ClearAssets() *PostUpdateOne { puo.mutation.ClearAssets() @@ -2374,6 +2492,51 @@ func (puo *PostUpdateOne) sqlSave(ctx context.Context) (_node *Post, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if puo.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := puo.mutation.RemovedMentionsIDs(); len(nodes) > 0 && !puo.mutation.MentionsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := puo.mutation.MentionsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: post.MentionsTable, + Columns: []string{post.MentionsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(mentionprofile.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if puo.mutation.AssetsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2M, diff --git a/internal/ent/predicate/predicate.go b/internal/ent/predicate/predicate.go index e84805fca..f2ffab461 100644 --- a/internal/ent/predicate/predicate.go +++ b/internal/ent/predicate/predicate.go @@ -39,6 +39,9 @@ type LikePost func(*sql.Selector) // Link is the predicate function for link builders. type Link func(*sql.Selector) +// MentionProfile is the predicate function for mentionprofile builders. +type MentionProfile func(*sql.Selector) + // Node is the predicate function for node builders. type Node func(*sql.Selector) diff --git a/internal/ent/runtime.go b/internal/ent/runtime.go index 203463143..13b3490b4 100644 --- a/internal/ent/runtime.go +++ b/internal/ent/runtime.go @@ -16,6 +16,7 @@ import ( "github.com/Southclaws/storyden/internal/ent/email" "github.com/Southclaws/storyden/internal/ent/likepost" "github.com/Southclaws/storyden/internal/ent/link" + "github.com/Southclaws/storyden/internal/ent/mentionprofile" "github.com/Southclaws/storyden/internal/ent/node" "github.com/Southclaws/storyden/internal/ent/notification" "github.com/Southclaws/storyden/internal/ent/post" @@ -512,6 +513,37 @@ func init() { return nil } }() + mentionprofileMixin := schema.MentionProfile{}.Mixin() + mentionprofileMixinFields0 := mentionprofileMixin[0].Fields() + _ = mentionprofileMixinFields0 + mentionprofileMixinFields1 := mentionprofileMixin[1].Fields() + _ = mentionprofileMixinFields1 + mentionprofileFields := schema.MentionProfile{}.Fields() + _ = mentionprofileFields + // mentionprofileDescCreatedAt is the schema descriptor for created_at field. + mentionprofileDescCreatedAt := mentionprofileMixinFields1[0].Descriptor() + // mentionprofile.DefaultCreatedAt holds the default value on creation for the created_at field. + mentionprofile.DefaultCreatedAt = mentionprofileDescCreatedAt.Default.(func() time.Time) + // mentionprofileDescID is the schema descriptor for id field. + mentionprofileDescID := mentionprofileMixinFields0[0].Descriptor() + // mentionprofile.DefaultID holds the default value on creation for the id field. + mentionprofile.DefaultID = mentionprofileDescID.Default.(func() xid.ID) + // mentionprofile.IDValidator is a validator for the "id" field. It is called by the builders before save. + mentionprofile.IDValidator = func() func(string) error { + validators := mentionprofileDescID.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(id string) error { + for _, fn := range fns { + if err := fn(id); err != nil { + return err + } + } + return nil + } + }() nodeMixin := schema.Node{}.Mixin() nodeMixinFields0 := nodeMixin[0].Fields() _ = nodeMixinFields0 diff --git a/internal/ent/schema/account.go b/internal/ent/schema/account.go index b40965696..fe4f9fc8a 100644 --- a/internal/ent/schema/account.go +++ b/internal/ent/schema/account.go @@ -56,6 +56,9 @@ func (Account) Edges() []ent.Edge { edge.To("likes", LikePost.Type). Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("mentions", MentionProfile.Type). + Annotations(entsql.OnDelete(entsql.Cascade)), + edge.From("roles", Role.Type). Ref("accounts"), diff --git a/internal/ent/schema/mention_profile.go b/internal/ent/schema/mention_profile.go new file mode 100644 index 000000000..6058e46db --- /dev/null +++ b/internal/ent/schema/mention_profile.go @@ -0,0 +1,48 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/rs/xid" +) + +type MentionProfile struct { + ent.Schema +} + +func (MentionProfile) Mixin() []ent.Mixin { + return []ent.Mixin{Identifier{}, CreatedAt{}} +} + +func (MentionProfile) Fields() []ent.Field { + return []ent.Field{ + field.String("account_id").GoType(xid.ID{}), + field.String("post_id").GoType(xid.ID{}), + } +} + +func (MentionProfile) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("account", Account.Type). + Ref("mentions"). + Field("account_id"). + Unique(). + Required(), + + edge.From("Post", Post.Type). + Ref("mentions"). + Field("post_id"). + Unique(). + Required(), + } +} + +func (MentionProfile) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("account_id", "post_id"). + Unique(). + StorageKey("unique_mentions_post"), + } +} diff --git a/internal/ent/schema/post.go b/internal/ent/schema/post.go index 98d851e42..a78201861 100644 --- a/internal/ent/schema/post.go +++ b/internal/ent/schema/post.go @@ -81,6 +81,9 @@ func (Post) Edges() []ent.Edge { edge.To("likes", LikePost.Type). Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("mentions", MentionProfile.Type). + Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("assets", Asset.Type), edge.From("collections", Collection.Type). diff --git a/internal/ent/tx.go b/internal/ent/tx.go index 2a14fac3d..896d8b986 100644 --- a/internal/ent/tx.go +++ b/internal/ent/tx.go @@ -36,6 +36,8 @@ type Tx struct { LikePost *LikePostClient // Link is the client for interacting with the Link builders. Link *LinkClient + // MentionProfile is the client for interacting with the MentionProfile builders. + MentionProfile *MentionProfileClient // Node is the client for interacting with the Node builders. Node *NodeClient // Notification is the client for interacting with the Notification builders. @@ -192,6 +194,7 @@ func (tx *Tx) init() { tx.Email = NewEmailClient(tx.config) tx.LikePost = NewLikePostClient(tx.config) tx.Link = NewLinkClient(tx.config) + tx.MentionProfile = NewMentionProfileClient(tx.config) tx.Node = NewNodeClient(tx.config) tx.Notification = NewNotificationClient(tx.config) tx.Post = NewPostClient(tx.config) diff --git a/internal/integration/fx.go b/internal/integration/fx.go index 90adc4908..b933068aa 100644 --- a/internal/integration/fx.go +++ b/internal/integration/fx.go @@ -51,7 +51,6 @@ func Test(t *testing.T, cfg *config.Config, o ...fx.Option) { ctx, cf := context.WithCancel(context.Background()) t.Cleanup(func() { - fmt.Println("integration test cleanup") cf() }) @@ -82,7 +81,7 @@ func Test(t *testing.T, cfg *config.Config, o ...fx.Option) { // application gives you some basics needed by most components. func application() fx.Option { return fx.Options( - // fx.NopLogger, + fx.NopLogger, infrastructure.Build(), resources.Build(), diff --git a/web/src/api/openapi-schema/notificationEvent.ts b/web/src/api/openapi-schema/notificationEvent.ts index 135d8adb8..c5ef87f92 100644 --- a/web/src/api/openapi-schema/notificationEvent.ts +++ b/web/src/api/openapi-schema/notificationEvent.ts @@ -21,4 +21,5 @@ export const NotificationEvent = { thread_reply: "thread_reply", post_like: "post_like", follow: "follow", + profile_mention: "profile_mention", } as const;