Skip to content

Commit

Permalink
start on item crud
Browse files Browse the repository at this point in the history
  • Loading branch information
Southclaws committed Oct 14, 2023
1 parent a2cde85 commit 6d6f406
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 24 deletions.
129 changes: 129 additions & 0 deletions app/resources/item/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package item

import (
"context"
"time"

"github.com/Southclaws/dt"
"github.com/Southclaws/fault"
"github.com/Southclaws/fault/fctx"
"github.com/Southclaws/fault/fmsg"
"github.com/Southclaws/fault/ftag"
"github.com/rs/xid"

"github.com/Southclaws/storyden/app/resources/account"
"github.com/Southclaws/storyden/app/resources/datagraph"
"github.com/Southclaws/storyden/internal/ent"
"github.com/Southclaws/storyden/internal/ent/item"
)

type database struct {
db *ent.Client
}

func New(db *ent.Client) Repository {
return &database{db}
}

func (d *database) Create(
ctx context.Context,
owner account.AccountID,
name string,
slug string,
desc string,
opts ...Option,
) (*datagraph.Item, error) {
create := d.db.Item.Create()
mutate := create.Mutation()

mutate.SetOwnerID(xid.ID(owner))
mutate.SetName(name)
mutate.SetSlug(slug)
mutate.SetDescription(desc)

for _, fn := range opts {
fn(mutate)
}

item, err := create.Save(ctx)
if err != nil {
if ent.IsConstraintError(err) {
return nil, fault.Wrap(err,
fctx.With(ctx),
ftag.With(ftag.AlreadyExists),
fmsg.WithDesc("already exists", "The item URL slug must be unique and the specified slug is already in use."),
)
}
return nil, fault.Wrap(err, fctx.With(ctx))
}

return d.Get(ctx, datagraph.ItemSlug(item.Slug))
}

func (d *database) List(ctx context.Context, filters ...Filter) ([]*datagraph.Item, error) {
q := d.db.Item.
Query().
WithOwner()

for _, fn := range filters {
fn(q)
}

cols, err := q.All(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

all, err := dt.MapErr(cols, datagraph.ItemFromModel)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return all, nil
}

func (d *database) Get(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error) {
item, err := d.db.Item.
Query().
Where(item.Slug(string(slug))).
WithOwner().
Only(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

r, err := datagraph.ItemFromModel(item)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return r, nil
}

func (d *database) Update(ctx context.Context, id datagraph.ItemID, opts ...Option) (*datagraph.Item, error) {
create := d.db.Item.UpdateOneID(xid.ID(id))
mutate := create.Mutation()

for _, fn := range opts {
fn(mutate)
}

c, err := create.Save(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return d.Get(ctx, datagraph.ItemSlug(c.Slug))
}

func (d *database) Archive(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error) {
update := d.db.Item.Update().Where(item.Slug(string(slug)))
update.SetDeletedAt(time.Now())

_, err := update.Save(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return d.Get(ctx, slug)
}
12 changes: 9 additions & 3 deletions app/resources/item/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type (
Filter func(*ent.ItemQuery)
)

type ItemRepository interface {
type Repository interface {
Create(ctx context.Context,
owner account.AccountID,
name string,
Expand All @@ -27,9 +27,9 @@ type ItemRepository interface {
List(ctx context.Context, filters ...Filter) ([]*datagraph.Item, error)
Get(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error)

Update(ctx context.Context, slug datagraph.ItemSlug, opts ...Option) (*datagraph.Item, error)
Update(ctx context.Context, slug datagraph.ItemID, opts ...Option) (*datagraph.Item, error)

Delete(ctx context.Context, slug datagraph.ItemSlug) error
Archive(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error)
}

func WithID(id datagraph.ItemID) Option {
Expand Down Expand Up @@ -62,6 +62,12 @@ func WithDescription(v string) Option {
}
}

func WithProperties(v any) Option {
return func(c *ent.ItemMutation) {
c.SetProperties(v)
}
}

func WithParentClusterAdd(id xid.ID) Option {
return func(c *ent.ItemMutation) {
c.AddClusterIDs(id)
Expand Down
2 changes: 2 additions & 0 deletions app/resources/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/Southclaws/storyden/app/resources/cluster"
"github.com/Southclaws/storyden/app/resources/cluster_traversal"
"github.com/Southclaws/storyden/app/resources/collection"
"github.com/Southclaws/storyden/app/resources/item"
"github.com/Southclaws/storyden/app/resources/notification"
"github.com/Southclaws/storyden/app/resources/post_search"
"github.com/Southclaws/storyden/app/resources/rbac"
Expand Down Expand Up @@ -38,6 +39,7 @@ func Build() fx.Option {
collection.New,
cluster.New,
cluster_traversal.New,
item.New,
),
)
}
92 changes: 90 additions & 2 deletions app/services/item/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ package item
import (
"context"

"github.com/Southclaws/fault"
"github.com/Southclaws/fault/fctx"
"github.com/Southclaws/fault/ftag"
"github.com/Southclaws/opt"

"github.com/Southclaws/storyden/app/resources/account"
"github.com/Southclaws/storyden/app/resources/datagraph"
"github.com/Southclaws/storyden/app/resources/item"
"github.com/Southclaws/storyden/app/services/authentication"
)

type ItemManager interface {
Create(ctx context.Context) (*datagraph.Item, error)
var errNotAuthorised = fault.Wrap(fault.New("not authorised"), ftag.With(ftag.PermissionDenied))

type Manager interface {
Create(ctx context.Context,
owner account.AccountID,
name string,
slug string,
desc string,
opts ...item.Option) (*datagraph.Item, error)
Get(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error)
Update(ctx context.Context, slug datagraph.ItemSlug, p Partial) (*datagraph.Item, error)
Archive(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error)
Expand All @@ -20,4 +33,79 @@ type Partial struct {
Slug opt.Optional[string]
ImageURL opt.Optional[string]
Description opt.Optional[string]
Properties opt.Optional[any]
}

type service struct {
cr item.Repository
}

func New(cr item.Repository) Manager {
return &service{cr: cr}
}

func (s *service) Create(ctx context.Context,
owner account.AccountID,
name string,
slug string,
desc string,
opts ...item.Option,
) (*datagraph.Item, error) {
itm, err := s.cr.Create(ctx, owner, name, slug, desc, opts...)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return itm, nil
}

func (s *service) Get(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error) {
itm, err := s.cr.Get(ctx, slug)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return itm, nil
}

func (s *service) Update(ctx context.Context, slug datagraph.ItemSlug, p Partial) (*datagraph.Item, error) {
accountID, err := authentication.GetAccountID(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

itm, err := s.cr.Get(ctx, slug)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

if !itm.Owner.Admin {
if itm.Owner.ID != accountID {
return nil, fault.Wrap(errNotAuthorised, fctx.With(ctx))
}
}

opts := []item.Option{}

p.Name.Call(func(value string) { opts = append(opts, item.WithName(value)) })
p.Slug.Call(func(value string) { opts = append(opts, item.WithSlug(value)) })
p.ImageURL.Call(func(value string) { opts = append(opts, item.WithImageURL(value)) })
p.Description.Call(func(value string) { opts = append(opts, item.WithDescription(value)) })
p.Properties.Call(func(value any) { opts = append(opts, item.WithProperties(value)) })

itm, err = s.cr.Update(ctx, itm.ID, opts...)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return itm, nil
}

func (s *service) Archive(ctx context.Context, slug datagraph.ItemSlug) (*datagraph.Item, error) {
itm, err := s.cr.Archive(ctx, slug)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return itm, nil
}
2 changes: 2 additions & 0 deletions app/services/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Southclaws/storyden/app/services/clustertree"
"github.com/Southclaws/storyden/app/services/collection"
"github.com/Southclaws/storyden/app/services/icon"
"github.com/Southclaws/storyden/app/services/item"
"github.com/Southclaws/storyden/app/services/onboarding"
"github.com/Southclaws/storyden/app/services/react"
"github.com/Southclaws/storyden/app/services/reply"
Expand Down Expand Up @@ -42,5 +43,6 @@ func Build() fx.Option {
thread_url.Build(),
fx.Provide(avatar_gen.New),
fx.Provide(cluster.New, clustertree.New),
fx.Provide(item.New),
)
}
47 changes: 36 additions & 11 deletions app/transports/openapi/bindings/items.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,57 @@ package bindings
import (
"context"

"github.com/Southclaws/storyden/app/resources/collection"
"github.com/Southclaws/fault"
"github.com/Southclaws/fault/fctx"

"github.com/Southclaws/storyden/app/resources/datagraph"
collection_svc "github.com/Southclaws/storyden/app/services/collection"
item_repo "github.com/Southclaws/storyden/app/resources/item"
"github.com/Southclaws/storyden/app/services/authentication"
item_svc "github.com/Southclaws/storyden/app/services/item"
"github.com/Southclaws/storyden/internal/openapi"
)

type Items struct {
collection_repo collection.Repository
collection_svc collection_svc.Service
im item_svc.Manager
}

func NewItems(
collection_repo collection.Repository,
collection_svc collection_svc.Service,
im item_svc.Manager,
) Items {
return Items{
collection_repo: collection_repo,
collection_svc: collection_svc,
im: im,
}
}

func (i *Items) ItemList(ctx context.Context, request openapi.ItemListRequestObject) (openapi.ItemListResponseObject, error) {
return nil, nil
func (i *Items) ItemCreate(ctx context.Context, request openapi.ItemCreateRequestObject) (openapi.ItemCreateResponseObject, error) {
session, err := authentication.GetAccountID(ctx)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

opts := []item_repo.Option{}

if v := request.Body.Properties; v != nil {
opts = append(opts, item_repo.WithProperties(*v))
}

itm, err := i.im.Create(ctx,
session,
request.Body.Name,
request.Body.Slug,
request.Body.Description,
opts...,
)
if err != nil {
return nil, fault.Wrap(err, fctx.With(ctx))
}

return openapi.ItemCreate200JSONResponse{
ItemCreateOKJSONResponse: openapi.ItemCreateOKJSONResponse(serialiseItem(itm)),
}, nil
}

func (i *Items) ItemCreate(ctx context.Context, request openapi.ItemCreateRequestObject) (openapi.ItemCreateResponseObject, error) {
func (i *Items) ItemList(ctx context.Context, request openapi.ItemListRequestObject) (openapi.ItemListResponseObject, error) {
return nil, nil
}

Expand Down
Loading

0 comments on commit 6d6f406

Please sign in to comment.