fx build fix
This commit is contained in:
69
api/server/internal/server/papitemplate/archive.go
Normal file
69
api/server/internal/server/papitemplate/archive.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) archive(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
objectRef, err := a.Cph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to restore object reference", zap.Error(err), mutil.PLog(a.Cph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Cph.Name(), a.Cph.GetID(r), err)
|
||||
}
|
||||
organizationRef, err := a.Oph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to restore organization reference", zap.Error(err), mutil.PLog(a.Oph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Oph.Name(), a.Oph.GetID(r), err)
|
||||
}
|
||||
|
||||
archived, err := mutil.GetArchiveParam(a.Logger, r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to read optional 'archived' param", zap.Error(err))
|
||||
return response.Auto(a.Logger, a.resource, err)
|
||||
}
|
||||
if archived == nil {
|
||||
a.Logger.Warn("No archivation setting provided")
|
||||
return response.BadRequest(a.Logger, a.resource, "invalid_query_parameter", "'archived' pram must be present")
|
||||
}
|
||||
cascade, err := mutil.GetCascadeParam(a.Logger, r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to read optional 'cascade' param", zap.Error(err))
|
||||
return response.Auto(a.Logger, a.resource, err)
|
||||
}
|
||||
if cascade == nil {
|
||||
a.Logger.Warn("Cascade property not specified, defaulting to false")
|
||||
csc := false
|
||||
cascade = &csc
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
_, err = a.a.DBFactory().TransactionFactory().CreateTransaction().Execute(ctx, func(ctx context.Context) (any, error) {
|
||||
return nil, a.DB.SetArchived(r.Context(), *account.GetID(), organizationRef, objectRef, *archived, *cascade)
|
||||
})
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to change archive property", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r),
|
||||
zap.Bool("archived", *archived), zap.Bool("cascade", *cascade))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
if a.nconfig.NeedArchiveNotification {
|
||||
var object T
|
||||
if err := a.DB.Get(ctx, *account.GetID(), objectRef, &object); err != nil {
|
||||
a.Logger.Warn("Failed to fetch object for notification", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r))
|
||||
} else {
|
||||
if err := a.nconfig.ArchiveNotification(&object, *account.GetID()); err != nil {
|
||||
a.Logger.Warn("Failed to send archivation notification", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a.Objects([]T{}, accessToken)
|
||||
}
|
||||
133
api/server/internal/server/papitemplate/config.go
Normal file
133
api/server/internal/server/papitemplate/config.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
)
|
||||
|
||||
type HandlerResolver func(sresponse.AccountHandlerFunc) sresponse.AccountHandlerFunc
|
||||
|
||||
type Config interface {
|
||||
WithNoCreate() Config
|
||||
WithCreateHandler(handler sresponse.AccountHandlerFunc) Config
|
||||
WithNoList() Config
|
||||
WithListHandler(handler sresponse.AccountHandlerFunc) Config
|
||||
WithNoGet() Config
|
||||
WithGetHandler(handler sresponse.AccountHandlerFunc) Config
|
||||
WithNoUpdate() Config
|
||||
WithUpdateHandler(handler sresponse.AccountHandlerFunc) Config
|
||||
WithNoDelete() Config
|
||||
WithDeleteHandler(handler sresponse.AccountHandlerFunc) Config
|
||||
WithReorderHandler(reorder ReorderConfig) Config
|
||||
WithTaggableHandler(taggable TaggableConfig) Config
|
||||
}
|
||||
|
||||
type PAPIConfig struct {
|
||||
CreateResolver HandlerResolver
|
||||
ListResolver HandlerResolver
|
||||
GetResolver HandlerResolver
|
||||
UpdateResolver HandlerResolver
|
||||
DeleteResolver HandlerResolver
|
||||
ArchiveResolver HandlerResolver
|
||||
Reorder *ReorderConfig
|
||||
Taggable *TaggableConfig
|
||||
}
|
||||
|
||||
// WithNoCreate disables the create endpoint by replacing its resolver.
|
||||
func (cfg *PAPIConfig) WithNoCreate() *PAPIConfig {
|
||||
cfg.CreateResolver = disableResolver
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithCreateHandler overrides the create endpoint by replacing its resolver.
|
||||
func (cfg *PAPIConfig) WithCreateHandler(handler sresponse.AccountHandlerFunc) *PAPIConfig {
|
||||
cfg.CreateResolver = overrideResolver(handler)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithNoList disables the list endpoint.
|
||||
func (cfg *PAPIConfig) WithNoList() *PAPIConfig {
|
||||
cfg.ListResolver = disableResolver
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithListHandler overrides the list endpoint.
|
||||
func (cfg *PAPIConfig) WithListHandler(handler sresponse.AccountHandlerFunc) *PAPIConfig {
|
||||
cfg.ListResolver = overrideResolver(handler)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithNoGet disables the get endpoint.
|
||||
func (cfg *PAPIConfig) WithNoGet() *PAPIConfig {
|
||||
cfg.GetResolver = disableResolver
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithGetHandler overrides the get endpoint.
|
||||
func (cfg *PAPIConfig) WithGetHandler(handler sresponse.AccountHandlerFunc) *PAPIConfig {
|
||||
cfg.GetResolver = overrideResolver(handler)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithNoUpdate disables the update endpoint.
|
||||
func (cfg *PAPIConfig) WithNoUpdate() *PAPIConfig {
|
||||
cfg.UpdateResolver = disableResolver
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithUpdateHandler overrides the update endpoint.
|
||||
func (cfg *PAPIConfig) WithUpdateHandler(handler sresponse.AccountHandlerFunc) *PAPIConfig {
|
||||
cfg.UpdateResolver = overrideResolver(handler)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithNoDelete disables the delete endpoint.
|
||||
func (cfg *PAPIConfig) WithNoDelete() *PAPIConfig {
|
||||
cfg.DeleteResolver = disableResolver
|
||||
return cfg
|
||||
}
|
||||
|
||||
// WithDeleteHandler overrides the delete endpoint.
|
||||
func (cfg *PAPIConfig) WithDeleteHandler(handler sresponse.AccountHandlerFunc) *PAPIConfig {
|
||||
cfg.DeleteResolver = overrideResolver(handler)
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (cfg *PAPIConfig) WithNoArchive() *PAPIConfig {
|
||||
cfg.ArchiveResolver = disableResolver
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (cfg *PAPIConfig) WithArchiveHandler(handler sresponse.AccountHandlerFunc) *PAPIConfig {
|
||||
cfg.ArchiveResolver = overrideResolver(handler)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// defaultResolver returns the default handler unchanged.
|
||||
func defaultResolver(defaultHandler sresponse.AccountHandlerFunc) sresponse.AccountHandlerFunc {
|
||||
return defaultHandler
|
||||
}
|
||||
|
||||
// disableResolver always returns nil, disabling the endpoint.
|
||||
func disableResolver(_ sresponse.AccountHandlerFunc) sresponse.AccountHandlerFunc {
|
||||
return nil
|
||||
}
|
||||
|
||||
// overrideResolver returns a resolver that always returns the given custom handler.
|
||||
func overrideResolver(custom sresponse.AccountHandlerFunc) HandlerResolver {
|
||||
return func(_ sresponse.AccountHandlerFunc) sresponse.AccountHandlerFunc {
|
||||
return custom
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfig() *PAPIConfig {
|
||||
return &PAPIConfig{
|
||||
CreateResolver: defaultResolver,
|
||||
ListResolver: defaultResolver,
|
||||
GetResolver: defaultResolver,
|
||||
UpdateResolver: defaultResolver,
|
||||
DeleteResolver: defaultResolver,
|
||||
ArchiveResolver: defaultResolver,
|
||||
Reorder: nil,
|
||||
Taggable: nil,
|
||||
}
|
||||
}
|
||||
38
api/server/internal/server/papitemplate/create.go
Normal file
38
api/server/internal/server/papitemplate/create.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) create(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
organizationRef, err := a.Oph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to parse parent object reference", zap.Error(err), mutil.PLog(a.Oph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Oph.Name(), a.Oph.GetID(r), err)
|
||||
}
|
||||
|
||||
var object T
|
||||
if err := json.NewDecoder(r.Body).Decode(&object); err != nil {
|
||||
a.Logger.Warn("Failed to decode object when creating", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Oph, r))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
if err := a.DB.Create(r.Context(), *account.GetID(), organizationRef, &object); err != nil {
|
||||
a.Logger.Warn("Error creating object", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Oph, r), mutil.PLog(a.Cph, r))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
if err := a.nconfig.CreateNotification(&object, *account.GetID()); err != nil {
|
||||
a.Logger.Warn("Failed to send creation notification", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Oph, r), mutil.PLog(a.Cph, r))
|
||||
}
|
||||
|
||||
return a.ObjectCreated(&object, accessToken)
|
||||
}
|
||||
23
api/server/internal/server/papitemplate/db.go
Normal file
23
api/server/internal/server/papitemplate/db.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ProtectedDB[T any] interface {
|
||||
Create(ctx context.Context, accountRef, organizationRef primitive.ObjectID, object *T) error
|
||||
Get(ctx context.Context, accountRef, objectRef primitive.ObjectID, result *T) error
|
||||
Update(ctx context.Context, accountRef primitive.ObjectID, object *T) error
|
||||
Delete(ctx context.Context, accountRef, objectRef primitive.ObjectID) error
|
||||
DeleteCascadeAuth(ctx context.Context, accountRef, objectRef primitive.ObjectID) error
|
||||
SetArchived(ctx context.Context, accountRef, organizationRef, objectRef primitive.ObjectID, isArchived, cascade bool) error
|
||||
List(ctx context.Context, accountRef, organizationRef, parentRef primitive.ObjectID, cursor *model.ViewCursor) ([]T, error)
|
||||
}
|
||||
|
||||
type ReorderDB interface {
|
||||
Reorder(ctx context.Context, accountRef, objectRef primitive.ObjectID, newIndex int, filter builder.Query) error
|
||||
}
|
||||
67
api/server/internal/server/papitemplate/delete.go
Normal file
67
api/server/internal/server/papitemplate/delete.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) deleteImp(ctx context.Context, account *model.Account, objectRef primitive.ObjectID, cascade *bool) error {
|
||||
var err error
|
||||
if (cascade != nil) && (*cascade) {
|
||||
_, err = a.a.DBFactory().TransactionFactory().CreateTransaction().Execute(ctx, func(ctx context.Context) (any, error) {
|
||||
return nil, a.DB.DeleteCascadeAuth(ctx, *account.GetID(), objectRef)
|
||||
})
|
||||
} else {
|
||||
err = a.DB.Delete(ctx, *account.GetID(), objectRef)
|
||||
}
|
||||
if err != nil {
|
||||
a.Logger.Warn("Error deleting object", zap.Error(err), mzap.StorableRef(account), mzap.ObjRef("object_ref", objectRef))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) delete(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
objectRef, err := a.Cph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to restore object reference", zap.Error(err), mutil.PLog(a.Cph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Cph.Name(), a.Cph.GetID(r), err)
|
||||
}
|
||||
|
||||
cascade, err := mutil.GetCascadeParam(a.Logger, r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to read optional 'cascade' param", zap.Error(err))
|
||||
return response.Auto(a.Logger, a.resource, err)
|
||||
}
|
||||
|
||||
var objPtr *T
|
||||
if a.nconfig.NeedDeleteNotification {
|
||||
var object T
|
||||
if err := a.DB.Get(r.Context(), *account.GetID(), objectRef, &object); err != nil {
|
||||
a.Logger.Warn("Failed to fetch object for notification", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r))
|
||||
} else {
|
||||
objPtr = &object
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.deleteImp(r.Context(), account, objectRef, cascade); err != nil {
|
||||
a.Logger.Warn("Error deleting object", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
if objPtr != nil {
|
||||
if err := a.nconfig.DeleteNotification(objPtr, *account.GetID()); err != nil {
|
||||
a.Logger.Warn("Failed to send deletion notification", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r))
|
||||
}
|
||||
}
|
||||
|
||||
return a.Objects([]T{}, accessToken)
|
||||
}
|
||||
29
api/server/internal/server/papitemplate/get.go
Normal file
29
api/server/internal/server/papitemplate/get.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) get(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
ctx := r.Context()
|
||||
objectRef, err := a.Cph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to restore object reference", zap.Error(err), mutil.PLog(a.Cph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Cph.Name(), a.Cph.GetID(r), err)
|
||||
}
|
||||
|
||||
var object T
|
||||
if err := a.DB.Get(ctx, *account.GetID(), objectRef, &object); err != nil {
|
||||
a.Logger.Warn("Failed to fetch object", zap.Error(err), mzap.StorableRef(account), mutil.PLog(a.Cph, r))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
return a.Object(&object, accessToken)
|
||||
}
|
||||
42
api/server/internal/server/papitemplate/list.go
Normal file
42
api/server/internal/server/papitemplate/list.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) list(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
organizationRef, err := a.Oph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to restore organization reference", zap.Error(err), mutil.PLog(a.Oph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Oph.Name(), a.Oph.GetID(r), err)
|
||||
}
|
||||
parentRef, err := a.Pph.GetRef(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to restore parent reference", zap.Error(err), mutil.PLog(a.Pph, r))
|
||||
return response.BadReference(a.Logger, a.Name(), a.Pph.Name(), a.Pph.GetID(r), err)
|
||||
}
|
||||
|
||||
cursor, err := mutil.GetViewCursor(a.Logger, r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode view cursor", zap.Error(err))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
objects, err := a.DB.List(r.Context(), *account.GetID(), organizationRef, parentRef, cursor)
|
||||
if err != nil {
|
||||
if !errors.Is(err, merrors.ErrNoData) {
|
||||
a.Logger.Warn("Failed to list objects", zap.Error(err), mutil.PLog(a.Pph, r))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
} else {
|
||||
a.Logger.Debug("No objects available", zap.Error(err), mutil.PLog(a.Pph, r))
|
||||
}
|
||||
}
|
||||
return a.Objects(objects, accessToken)
|
||||
}
|
||||
88
api/server/internal/server/papitemplate/nconfig.go
Normal file
88
api/server/internal/server/papitemplate/nconfig.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/messaging"
|
||||
notifications "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
model "github.com/tech/sendico/pkg/model/notification"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// NotificationHandler is a function that processes an object of type T and returns an error.
|
||||
type NotificationHandler[T any] func(template T, actorAccountRef primitive.ObjectID) error
|
||||
|
||||
// sinkNotification is the default no-op strategy.
|
||||
func sinkNotification[T any](_ T, _ primitive.ObjectID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotificationConfig manages notifications for Create, Update, and Delete operations.
|
||||
type NotificationConfig[T any] struct {
|
||||
producer messaging.Producer
|
||||
// The factory now receives a NotificationAction so it knows which event is being processed.
|
||||
factory func(template T, actorAccountRef primitive.ObjectID, t model.NotificationAction) notifications.Envelope
|
||||
CreateNotification NotificationHandler[T]
|
||||
UpdateNotification NotificationHandler[T]
|
||||
NeedArchiveNotification bool
|
||||
ArchiveNotification NotificationHandler[T]
|
||||
NeedDeleteNotification bool
|
||||
DeleteNotification NotificationHandler[T]
|
||||
}
|
||||
|
||||
// NewNotificationConfig creates a new NotificationConfig with default (no-op) strategies.
|
||||
func NewNotificationConfig[T any](producer messaging.Producer) *NotificationConfig[T] {
|
||||
return &NotificationConfig[T]{
|
||||
producer: producer,
|
||||
factory: nil, // no factory by default
|
||||
CreateNotification: sinkNotification[T],
|
||||
UpdateNotification: sinkNotification[T],
|
||||
ArchiveNotification: sinkNotification[T],
|
||||
NeedArchiveNotification: false,
|
||||
DeleteNotification: sinkNotification[T],
|
||||
NeedDeleteNotification: false,
|
||||
}
|
||||
}
|
||||
|
||||
// WithNotifications sets the notification factory and switches all endpoints to the sending strategy.
|
||||
func (nc *NotificationConfig[T]) WithNotifications(factory func(template T, actorAccountRef primitive.ObjectID, typ model.NotificationAction) notifications.Envelope) *NotificationConfig[T] {
|
||||
nc.factory = factory
|
||||
// Build sending functions for each notification type.
|
||||
nc.CreateNotification = func(template T, actorAccountRef primitive.ObjectID) error {
|
||||
return nc.producer.SendMessage(factory(template, actorAccountRef, model.NACreated))
|
||||
}
|
||||
nc.UpdateNotification = func(template T, actorAccountRef primitive.ObjectID) error {
|
||||
return nc.producer.SendMessage(factory(template, actorAccountRef, model.NAUpdated))
|
||||
}
|
||||
nc.ArchiveNotification = func(template T, actorAccountRef primitive.ObjectID) error {
|
||||
return nc.producer.SendMessage(factory(template, actorAccountRef, model.NAArchived))
|
||||
}
|
||||
nc.NeedArchiveNotification = true
|
||||
nc.DeleteNotification = func(template T, actorAccountRef primitive.ObjectID) error {
|
||||
return nc.producer.SendMessage(factory(template, actorAccountRef, model.NADeleted))
|
||||
}
|
||||
nc.NeedDeleteNotification = true
|
||||
return nc
|
||||
}
|
||||
|
||||
// WithNoCreateNotification disables the create notification.
|
||||
func (nc *NotificationConfig[T]) WithNoCreateNotification() *NotificationConfig[T] {
|
||||
nc.CreateNotification = sinkNotification[T]
|
||||
return nc
|
||||
}
|
||||
|
||||
// WithNoUpdateNotification disables the update notification.
|
||||
func (nc *NotificationConfig[T]) WithNoUpdateNotification() *NotificationConfig[T] {
|
||||
nc.UpdateNotification = sinkNotification[T]
|
||||
return nc
|
||||
}
|
||||
|
||||
func (nc *NotificationConfig[T]) WithNoArchiveNotification() *NotificationConfig[T] {
|
||||
nc.ArchiveNotification = sinkNotification[T]
|
||||
return nc
|
||||
}
|
||||
|
||||
// WithNoDeleteNotification disables the delete notification.
|
||||
func (nc *NotificationConfig[T]) WithNoDeleteNotification() *NotificationConfig[T] {
|
||||
nc.DeleteNotification = sinkNotification[T]
|
||||
nc.NeedDeleteNotification = false
|
||||
return nc
|
||||
}
|
||||
33
api/server/internal/server/papitemplate/rconfig.go
Normal file
33
api/server/internal/server/papitemplate/rconfig.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/db/repository/builder"
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
)
|
||||
|
||||
type ReorderRequestProcessor func(r *http.Request) (*srequest.ReorderX, builder.Query, error)
|
||||
|
||||
type ReorderConfig struct {
|
||||
DB ReorderDB
|
||||
ReqProcessor ReorderRequestProcessor
|
||||
}
|
||||
|
||||
func (cfg *PAPIConfig) WithReorderHandler(reorder ReorderConfig) *PAPIConfig {
|
||||
cfg.Reorder = &reorder
|
||||
if cfg.Reorder.ReqProcessor == nil {
|
||||
cfg.Reorder.ReqProcessor = defaultRequestProcessor
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func defaultRequestProcessor(r *http.Request) (*srequest.ReorderX, builder.Query, error) {
|
||||
var req srequest.ReorderXDefault
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &req.ReorderX, repository.OrgFilter(req.ParentRef), nil
|
||||
}
|
||||
33
api/server/internal/server/papitemplate/reorder.go
Normal file
33
api/server/internal/server/papitemplate/reorder.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) reorder(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing reorder request...")
|
||||
req, filter, err := a.config.Reorder.ReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode tasks reorder request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Moving objects", mzap.ObjRef("object_ref", req.ObjectRef), zap.Int("new_index", req.To))
|
||||
|
||||
if _, err := a.a.DBFactory().TransactionFactory().CreateTransaction().Execute(r.Context(), func(ctx context.Context) (any, error) {
|
||||
// reorder is not atomic, so wrappping into transaction
|
||||
return nil, a.config.Reorder.DB.Reorder(ctx, account.ID, req.ObjectRef, req.To, filter)
|
||||
}); err != nil {
|
||||
a.Logger.Warn("Failed to reorder tasks", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef), zap.Int("to", req.To))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Reorder request processing complete")
|
||||
return response.Success(a.Logger)
|
||||
}
|
||||
19
api/server/internal/server/papitemplate/responses.go
Normal file
19
api/server/internal/server/papitemplate/responses.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) Objects(items []T, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
return sresponse.ObjectsAuth(a.Logger, items, accessToken, a.Name())
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) Object(item *T, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
return sresponse.ObjectAuth(a.Logger, item, accessToken, a.Name())
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) ObjectCreated(item *T, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
return sresponse.ObjectAuthCreated(a.Logger, item, accessToken, a.Name())
|
||||
}
|
||||
203
api/server/internal/server/papitemplate/service.go
Normal file
203
api/server/internal/server/papitemplate/service.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/tech/sendico/pkg/api/http"
|
||||
notifications "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
model "github.com/tech/sendico/pkg/model/notification"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
eapi "github.com/tech/sendico/server/interface/api"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
mutil "github.com/tech/sendico/server/internal/mutil/param"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ProtectedAPI[T any] struct {
|
||||
Logger mlogger.Logger
|
||||
DB ProtectedDB[T]
|
||||
Oph mutil.ParamHelper // org param handler
|
||||
Pph mutil.ParamHelper // parent object param handler
|
||||
Cph mutil.ParamHelper // child object param handler
|
||||
resource mservice.Type
|
||||
a eapi.API
|
||||
config *PAPIConfig
|
||||
nconfig *NotificationConfig[*T]
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[_]) Name() mservice.Type {
|
||||
return a.resource
|
||||
}
|
||||
|
||||
func (_ *ProtectedAPI[_]) Finish(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) Build() *ProtectedAPI[T] {
|
||||
createHandler := a.config.CreateResolver(a.create)
|
||||
if createHandler != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), a.Oph.AddRef("/"), api.Post, createHandler)
|
||||
}
|
||||
|
||||
listHandler := a.config.ListResolver(a.list)
|
||||
if listHandler != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), a.Pph.AddRef(a.Oph.AddRef("/list")), api.Get, listHandler)
|
||||
}
|
||||
|
||||
getHandler := a.config.GetResolver(a.get)
|
||||
if getHandler != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), a.Cph.AddRef("/"), api.Get, getHandler)
|
||||
}
|
||||
|
||||
updateHandler := a.config.UpdateResolver(a.update)
|
||||
if updateHandler != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), "/", api.Put, updateHandler)
|
||||
}
|
||||
|
||||
deleteHandler := a.config.DeleteResolver(a.delete)
|
||||
if deleteHandler != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), a.Cph.AddRef("/"), api.Delete, deleteHandler)
|
||||
}
|
||||
|
||||
archiveHandler := a.config.ArchiveResolver(a.archive)
|
||||
if archiveHandler != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), a.Cph.AddRef(a.Oph.AddRef("/archive")), api.Get, archiveHandler)
|
||||
}
|
||||
|
||||
if a.config.Reorder != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), "/reorder", api.Post, a.reorder)
|
||||
}
|
||||
|
||||
if a.config.Taggable != nil {
|
||||
a.a.Register().AccountHandler(a.Name(), "/tags/add", api.Put, a.addTag)
|
||||
a.a.Register().AccountHandler(a.Name(), "/tags/add", api.Post, a.addTags)
|
||||
a.a.Register().AccountHandler(a.Name(), "/tags", api.Delete, a.removeTag)
|
||||
a.a.Register().AccountHandler(a.Name(), "/tags/all", api.Delete, a.removeAllTags)
|
||||
a.a.Register().AccountHandler(a.Name(), "/tags/set", api.Post, a.setTags)
|
||||
a.a.Register().AccountHandler(a.Name(), "/tags", api.Get, a.getTags)
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithNotifications(factory func(template *T, actorAccountRef primitive.ObjectID, t model.NotificationAction) notifications.Envelope) *ProtectedAPI[T] {
|
||||
a.nconfig.WithNotifications(factory)
|
||||
a.Logger.Info("Notificatons handler installed")
|
||||
return a
|
||||
}
|
||||
|
||||
// WithNoCreateNotification disables the create notification.
|
||||
func (a *ProtectedAPI[T]) WithNoCreateNotification() *ProtectedAPI[T] {
|
||||
a.nconfig.WithNoCreateNotification()
|
||||
a.Logger.Info("Object creation notificaton disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
// WithNoUpdateNotification disables the update notification.
|
||||
func (a *ProtectedAPI[T]) WithNoUpdateNotification() *ProtectedAPI[T] {
|
||||
a.nconfig.WithNoUpdateNotification()
|
||||
a.Logger.Info("Object update notificaton disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
// WithNoDeleteNotification disables the delete notification.
|
||||
func (a *ProtectedAPI[T]) WithNoDeleteNotification() *ProtectedAPI[T] {
|
||||
a.nconfig.WithNoDeleteNotification()
|
||||
a.Logger.Info("Object deletion notificaton disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithNoCreate() *ProtectedAPI[T] {
|
||||
a.config.WithNoCreate()
|
||||
a.Logger.Info("Create handler disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithCreateHandler(handler sresponse.AccountHandlerFunc) *ProtectedAPI[T] {
|
||||
a.config.WithCreateHandler(handler)
|
||||
a.Logger.Info("Create handler overridden")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithNoList() *ProtectedAPI[T] {
|
||||
a.config.WithNoList()
|
||||
a.Logger.Info("List handler disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithListHandler(handler sresponse.AccountHandlerFunc) *ProtectedAPI[T] {
|
||||
a.config.WithListHandler(handler)
|
||||
a.Logger.Info("List handler overridden")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithNoGet() *ProtectedAPI[T] {
|
||||
a.config.WithNoGet()
|
||||
a.Logger.Info("Get handler disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithGetHandler(handler sresponse.AccountHandlerFunc) *ProtectedAPI[T] {
|
||||
a.config.WithGetHandler(handler)
|
||||
a.Logger.Info("Get handler overridden")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithReorderHandler(reorder ReorderConfig) *ProtectedAPI[T] {
|
||||
a.config.WithReorderHandler(reorder)
|
||||
a.Logger.Info("Reorder handler installed")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithTaggableHandler(taggable TaggableConfig) *ProtectedAPI[T] {
|
||||
a.config.WithTaggableHandler(taggable)
|
||||
a.Logger.Info("Taggable handlers installed")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithNoUpdate() *ProtectedAPI[T] {
|
||||
a.config.WithNoUpdate()
|
||||
a.Logger.Info("Update handler disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithUpdateHandler(handler sresponse.AccountHandlerFunc) *ProtectedAPI[T] {
|
||||
a.config.WithUpdateHandler(handler)
|
||||
a.Logger.Info("Update handler overridden")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithNoDelete() *ProtectedAPI[T] {
|
||||
a.config.WithNoDelete()
|
||||
a.Logger.Info("Delete handler disabled")
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) WithDeleteHandler(handler sresponse.AccountHandlerFunc) *ProtectedAPI[T] {
|
||||
a.config.WithDeleteHandler(handler)
|
||||
a.Logger.Info("Delete handler overriden")
|
||||
return a
|
||||
}
|
||||
|
||||
func CreateAPI[T any](a eapi.API, dbFactory func() (ProtectedDB[T], error), parent, resource mservice.Type) (*ProtectedAPI[T], error) {
|
||||
p := &ProtectedAPI[T]{
|
||||
Logger: a.Logger().Named(resource),
|
||||
Oph: mutil.CreatePH("org"), // to avoid collision with organizaitons_ref when
|
||||
Pph: mutil.CreatePH(parent),
|
||||
resource: resource,
|
||||
Cph: mutil.CreatePH(resource),
|
||||
a: a,
|
||||
config: NewConfig(),
|
||||
nconfig: NewNotificationConfig[*T](a.Register().Messaging().Producer()),
|
||||
}
|
||||
|
||||
var err error
|
||||
if p.DB, err = dbFactory(); err != nil {
|
||||
p.Logger.Error("Failed to create protected database", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
122
api/server/internal/server/papitemplate/taggable.go
Normal file
122
api/server/internal/server/papitemplate/taggable.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) addTag(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing add tag request...")
|
||||
req, err := a.config.Taggable.AddTagReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode add tag request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Adding tag to object", mzap.ObjRef("object_ref", req.ObjectRef), mzap.ObjRef("tag_ref", req.TagRef))
|
||||
|
||||
if err := a.config.Taggable.DB.AddTag(r.Context(), account.ID, req.ObjectRef, req.TagRef); err != nil {
|
||||
a.Logger.Warn("Failed to add tag to object", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef), mzap.ObjRef("tag_ref", req.TagRef))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Add tag request processing complete")
|
||||
return response.Success(a.Logger)
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) addTags(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing add tags request...")
|
||||
req, err := a.config.Taggable.AddTagsReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode add tags request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Adding tags to object", mzap.ObjRef("object_ref", req.ObjectRef), zap.Int("tag_count", len(req.TagRefs)))
|
||||
|
||||
if err := a.config.Taggable.DB.AddTags(r.Context(), account.ID, req.ObjectRef, req.TagRefs); err != nil {
|
||||
a.Logger.Warn("Failed to add tags to object", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Add tags request processing complete")
|
||||
return response.Success(a.Logger)
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) removeTag(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing remove tag request...")
|
||||
req, err := a.config.Taggable.RemoveTagReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode remove tag request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Removing tag from object", mzap.ObjRef("object_ref", req.ObjectRef), mzap.ObjRef("tag_ref", req.TagRef))
|
||||
|
||||
if err := a.config.Taggable.DB.RemoveTag(r.Context(), account.ID, req.ObjectRef, req.TagRef); err != nil {
|
||||
a.Logger.Warn("Failed to remove tag from object", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef), mzap.ObjRef("tag_ref", req.TagRef))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Remove tag request processing complete")
|
||||
return response.Success(a.Logger)
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) removeAllTags(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing remove all tags request...")
|
||||
req, err := a.config.Taggable.RemoveAllTagsReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode remove all tags request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Removing all tags from object", mzap.ObjRef("object_ref", req.ObjectRef))
|
||||
|
||||
if err := a.config.Taggable.DB.RemoveAllTags(r.Context(), account.ID, req.ObjectRef); err != nil {
|
||||
a.Logger.Warn("Failed to remove all tags from object", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Remove all tags request processing complete")
|
||||
return response.Success(a.Logger)
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) setTags(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing set tags request...")
|
||||
req, err := a.config.Taggable.SetTagsReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode set tags request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Setting tags for object", mzap.ObjRef("object_ref", req.ObjectRef), zap.Int("tag_count", len(req.TagRefs)))
|
||||
|
||||
if err := a.config.Taggable.DB.SetTags(r.Context(), account.ID, req.ObjectRef, req.TagRefs); err != nil {
|
||||
a.Logger.Warn("Failed to set tags for object", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Set tags request processing complete")
|
||||
return response.Success(a.Logger)
|
||||
}
|
||||
|
||||
func (a *ProtectedAPI[T]) getTags(r *http.Request, account *model.Account, _ *sresponse.TokenData) http.HandlerFunc {
|
||||
a.Logger.Debug("Processing get tags request...")
|
||||
req, err := a.config.Taggable.GetTagsReqProcessor(r)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to decode get tags request", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
a.Logger.Debug("Getting tags for object", mzap.ObjRef("object_ref", req.ObjectRef))
|
||||
|
||||
tagRefs, err := a.config.Taggable.DB.GetTags(r.Context(), account.ID, req.ObjectRef)
|
||||
if err != nil {
|
||||
a.Logger.Warn("Failed to get tags for object", zap.Error(err), mzap.ObjRef("object_ref", req.ObjectRef))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
a.Logger.Debug("Get tags request processing complete", zap.Int("tag_count", len(tagRefs)))
|
||||
return response.Ok(a.Logger, map[string]interface{}{
|
||||
"tagRefs": tagRefs,
|
||||
})
|
||||
}
|
||||
80
api/server/internal/server/papitemplate/tconfig.go
Normal file
80
api/server/internal/server/papitemplate/tconfig.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/server/interface/api/srequest"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type (
|
||||
TaggableSingleRequestProcessor func(r *http.Request) (*srequest.TaggableSingle, error)
|
||||
TaggableMultipleRequestProcessor func(r *http.Request) (*srequest.TaggableMultiple, error)
|
||||
TaggableObjectRequestProcessor func(r *http.Request) (*srequest.TaggableObject, error)
|
||||
)
|
||||
|
||||
// TaggableDB interface defines the required methods for tag operations
|
||||
type TaggableDB interface {
|
||||
AddTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) error
|
||||
AddTags(ctx context.Context, accountRef, objectRef primitive.ObjectID, tagRefs []primitive.ObjectID) error
|
||||
RemoveTag(ctx context.Context, accountRef, objectRef, tagRef primitive.ObjectID) error
|
||||
RemoveAllTags(ctx context.Context, accountRef, objectRef primitive.ObjectID) error
|
||||
SetTags(ctx context.Context, accountRef, objectRef primitive.ObjectID, tagRefs []primitive.ObjectID) error
|
||||
GetTags(ctx context.Context, accountRef, objectRef primitive.ObjectID) ([]primitive.ObjectID, error)
|
||||
}
|
||||
|
||||
type TaggableConfig struct {
|
||||
DB TaggableDB
|
||||
AddTagReqProcessor TaggableSingleRequestProcessor
|
||||
AddTagsReqProcessor TaggableMultipleRequestProcessor
|
||||
RemoveTagReqProcessor TaggableSingleRequestProcessor
|
||||
RemoveAllTagsReqProcessor TaggableObjectRequestProcessor
|
||||
SetTagsReqProcessor TaggableMultipleRequestProcessor
|
||||
GetTagsReqProcessor TaggableObjectRequestProcessor
|
||||
}
|
||||
|
||||
func (cfg *PAPIConfig) WithTaggableHandler(taggable TaggableConfig) *PAPIConfig {
|
||||
cfg.Taggable = &taggable
|
||||
if cfg.Taggable.AddTagReqProcessor == nil {
|
||||
cfg.Taggable.AddTagReqProcessor = defaultTaggableSingleRequestProcessor
|
||||
}
|
||||
if cfg.Taggable.AddTagsReqProcessor == nil {
|
||||
cfg.Taggable.AddTagsReqProcessor = defaultTaggableMultipleRequestProcessor
|
||||
}
|
||||
if cfg.Taggable.RemoveTagReqProcessor == nil {
|
||||
cfg.Taggable.RemoveTagReqProcessor = defaultTaggableSingleRequestProcessor
|
||||
}
|
||||
if cfg.Taggable.RemoveAllTagsReqProcessor == nil {
|
||||
cfg.Taggable.RemoveAllTagsReqProcessor = defaultTaggableObjectRequestProcessor
|
||||
}
|
||||
if cfg.Taggable.SetTagsReqProcessor == nil {
|
||||
cfg.Taggable.SetTagsReqProcessor = defaultTaggableMultipleRequestProcessor
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func defaultTaggableSingleRequestProcessor(r *http.Request) (*srequest.TaggableSingle, error) {
|
||||
var req srequest.TaggableSingle
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func defaultTaggableMultipleRequestProcessor(r *http.Request) (*srequest.TaggableMultiple, error) {
|
||||
var req srequest.TaggableMultiple
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &req, nil
|
||||
}
|
||||
|
||||
func defaultTaggableObjectRequestProcessor(r *http.Request) (*srequest.TaggableObject, error) {
|
||||
var req srequest.TaggableObject
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &req, nil
|
||||
}
|
||||
31
api/server/internal/server/papitemplate/update.go
Normal file
31
api/server/internal/server/papitemplate/update.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package papitemplate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/tech/sendico/pkg/api/http/response"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"github.com/tech/sendico/server/interface/api/sresponse"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (a *ProtectedAPI[T]) update(r *http.Request, account *model.Account, accessToken *sresponse.TokenData) http.HandlerFunc {
|
||||
var object T
|
||||
if err := json.NewDecoder(r.Body).Decode(&object); err != nil {
|
||||
a.Logger.Warn("Failed to decode object when updating settings", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.BadPayload(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
if err := a.DB.Update(r.Context(), *account.GetID(), &object); err != nil {
|
||||
a.Logger.Warn("Error updating object", zap.Error(err), mzap.StorableRef(account))
|
||||
return response.Auto(a.Logger, a.Name(), err)
|
||||
}
|
||||
|
||||
if err := a.nconfig.UpdateNotification(&object, *account.GetID()); err != nil {
|
||||
a.Logger.Warn("Failed to send creation notification", zap.Error(err))
|
||||
}
|
||||
|
||||
return a.Object(&object, accessToken)
|
||||
}
|
||||
Reference in New Issue
Block a user