mirror of
https://github.com/emo2007/block-accounting.git
synced 2025-04-04 13:46:27 +00:00
org creatin and listing with pagianation implemented with one small bug
This commit is contained in:
parent
d4430445a7
commit
9b1d371cb1
@ -18,10 +18,11 @@ import (
|
||||
|
||||
var interfaceSet wire.ProviderSet = wire.NewSet(
|
||||
provideAuthController,
|
||||
provideOrganizationsCOntroller,
|
||||
provideOrganizationsController,
|
||||
provideControllers,
|
||||
|
||||
provideAuthPresenter,
|
||||
provideOrganizationsPresenter,
|
||||
)
|
||||
|
||||
func provideLogger(c config.Config) *slog.Logger {
|
||||
@ -53,6 +54,10 @@ func provideAuthPresenter(
|
||||
return presenters.NewAuthPresenter(jwtInteractor)
|
||||
}
|
||||
|
||||
func provideOrganizationsPresenter() presenters.OrganizationsPresenter {
|
||||
return presenters.NewOrganizationsPresenter()
|
||||
}
|
||||
|
||||
func provideAuthController(
|
||||
log *slog.Logger,
|
||||
usersInteractor users.UsersInteractor,
|
||||
@ -67,13 +72,15 @@ func provideAuthController(
|
||||
)
|
||||
}
|
||||
|
||||
func provideOrganizationsCOntroller(
|
||||
func provideOrganizationsController(
|
||||
log *slog.Logger,
|
||||
organizationsInteractor organizations.OrganizationsInteractor,
|
||||
presenter presenters.OrganizationsPresenter,
|
||||
) controllers.OrganizationsController {
|
||||
return controllers.NewOrganizationsController(
|
||||
log.WithGroup("organizations-controller"),
|
||||
organizationsInteractor,
|
||||
presenter,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,8 @@ func ProvideService(c config.Config) (service.Service, func(), error) {
|
||||
authController := provideAuthController(logger, usersInteractor, authPresenter, jwtInteractor)
|
||||
organizationsRepository := provideOrganizationsRepository(db)
|
||||
organizationsInteractor := provideOrganizationsInteractor(logger, organizationsRepository)
|
||||
organizationsController := provideOrganizationsCOntroller(logger, organizationsInteractor)
|
||||
organizationsPresenter := provideOrganizationsPresenter()
|
||||
organizationsController := provideOrganizationsController(logger, organizationsInteractor, organizationsPresenter)
|
||||
rootController := provideControllers(logger, authController, organizationsController)
|
||||
server := provideRestServer(logger, rootController, c, jwtInteractor)
|
||||
serviceService := service.NewService(logger, server)
|
||||
|
@ -61,7 +61,7 @@ func (c *authController) Join(w http.ResponseWriter, req *http.Request) ([]byte,
|
||||
return nil, fmt.Errorf("error invalid mnemonic. %w", ErrorAuthInvalidMnemonic)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second)
|
||||
ctx, cancel := context.WithTimeout(req.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
user, err := c.usersInteractor.Create(ctx, users.CreateParams{
|
||||
@ -78,7 +78,7 @@ func (c *authController) Join(w http.ResponseWriter, req *http.Request) ([]byte,
|
||||
|
||||
c.log.Debug("join request", slog.String("user id", user.ID.String()))
|
||||
|
||||
return c.presenter.ResponseJoin(w, user)
|
||||
return c.presenter.ResponseJoin(user)
|
||||
}
|
||||
|
||||
// NIT: wrap with idempotent action handler
|
||||
@ -90,7 +90,7 @@ func (c *authController) Login(w http.ResponseWriter, req *http.Request) ([]byte
|
||||
|
||||
c.log.Debug("login request", slog.String("mnemonic", request.Mnemonic))
|
||||
|
||||
ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second)
|
||||
ctx, cancel := context.WithTimeout(req.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
seed, err := hdwallet.NewSeedFromMnemonic(request.Mnemonic)
|
||||
@ -111,7 +111,7 @@ func (c *authController) Login(w http.ResponseWriter, req *http.Request) ([]byte
|
||||
|
||||
c.log.Debug("login request", slog.String("user id", users[0].ID.String()))
|
||||
|
||||
return c.presenter.ResponseLogin(w, users[0])
|
||||
return c.presenter.ResponseLogin(users[0])
|
||||
}
|
||||
|
||||
// const mnemonicEntropyBitSize int = 256
|
||||
|
@ -1,43 +1,85 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/emochka2007/block-accounting/internal/interface/rest/domain"
|
||||
"github.com/emochka2007/block-accounting/internal/interface/rest/presenters"
|
||||
"github.com/emochka2007/block-accounting/internal/pkg/ctxmeta"
|
||||
"github.com/emochka2007/block-accounting/internal/usecase/interactors/organizations"
|
||||
)
|
||||
|
||||
type OrganizationsController interface {
|
||||
NewOrganization(w http.ResponseWriter, r *http.Request) ([]byte, error)
|
||||
ListOrganizations(w http.ResponseWriter, r *http.Request) ([]byte, error)
|
||||
}
|
||||
|
||||
type organizationsController struct {
|
||||
log *slog.Logger
|
||||
orgInteractor organizations.OrganizationsInteractor
|
||||
presenter presenters.OrganizationsPresenter
|
||||
}
|
||||
|
||||
func NewOrganizationsController(
|
||||
log *slog.Logger,
|
||||
orgInteractor organizations.OrganizationsInteractor,
|
||||
presenter presenters.OrganizationsPresenter,
|
||||
) OrganizationsController {
|
||||
return &organizationsController{
|
||||
log: log,
|
||||
orgInteractor: orgInteractor,
|
||||
presenter: presenter,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *organizationsController) NewOrganization(w http.ResponseWriter, r *http.Request) ([]byte, error) {
|
||||
_, err := presenters.CreateRequest[domain.NewOrganizationRequest](r)
|
||||
req, err := presenters.CreateRequest[domain.NewOrganizationRequest](r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error build request. %w", err)
|
||||
}
|
||||
|
||||
// todo call int.Create
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// todo build response
|
||||
org, err := c.orgInteractor.Create(ctx, organizations.CreateParams{
|
||||
Name: req.Name,
|
||||
Address: req.Address,
|
||||
WalletMnemonic: req.WalletMnemonic,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error create new organization. %w", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return c.presenter.ResponseCreate(org)
|
||||
}
|
||||
|
||||
func (c *organizationsController) ListOrganizations(w http.ResponseWriter, r *http.Request) ([]byte, error) {
|
||||
req, err := presenters.CreateRequest[domain.ListOrganizationsRequest](r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error build request. %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
user, err := ctxmeta.User(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch user from context. %w", err)
|
||||
}
|
||||
|
||||
resp, err := c.orgInteractor.List(ctx, organizations.ListParams{
|
||||
UserId: user.Id(),
|
||||
Cursor: req.Cursor,
|
||||
Limit: req.Limit,
|
||||
OffsetDate: time.UnixMilli(req.OffsetDate),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch organizations list. %w", err)
|
||||
}
|
||||
|
||||
return c.presenter.ResponseList(resp.Organizations, resp.NextCursor)
|
||||
}
|
||||
|
@ -5,6 +5,20 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Generic
|
||||
|
||||
type Collection[T any] struct {
|
||||
Items []T `json:"items,omitempty"`
|
||||
Pagination Pagination `json:"pagination,omitempty"`
|
||||
}
|
||||
|
||||
type Pagination struct {
|
||||
NextCursor string `json:"next_cursor,omitempty"`
|
||||
TotalItems uint32 `json:"total_items,omitempty"`
|
||||
}
|
||||
|
||||
// Auth related DTO's
|
||||
|
||||
type JoinRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Credentals struct {
|
||||
@ -28,12 +42,28 @@ type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// Organizations
|
||||
|
||||
type NewOrganizationRequest struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
WalletMnemonic string `json:"wallet_mnemonic,omitempty"`
|
||||
}
|
||||
|
||||
type NewOrganizationResponse struct {
|
||||
Organization Organization `json:"organization"`
|
||||
}
|
||||
|
||||
type ListOrganizationsRequest struct {
|
||||
Cursor string `json:"cursor,omitempty"`
|
||||
Limit uint8 `json:"limit,omitempty"` // Default: 50, Max: 50
|
||||
OffsetDate int64 `json:"offset_date,omitempty"` // List organizations, updated since the date
|
||||
}
|
||||
|
||||
type ListOrganizationsResponse struct {
|
||||
Collection[Organization]
|
||||
}
|
||||
|
||||
func BuildRequest[T any](data []byte) (*T, error) {
|
||||
var req T
|
||||
|
||||
|
9
backend/internal/interface/rest/domain/organization.go
Normal file
9
backend/internal/interface/rest/domain/organization.go
Normal file
@ -0,0 +1,9 @@
|
||||
package domain
|
||||
|
||||
type Organization struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
CreatedAt uint64 `json:"created_at"`
|
||||
UpdatedAt uint64 `json:"updated_at"`
|
||||
}
|
@ -3,7 +3,6 @@ package presenters
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/emochka2007/block-accounting/internal/interface/rest/domain"
|
||||
@ -12,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
type AuthPresenter interface {
|
||||
ResponseJoin(w http.ResponseWriter, user *models.User) ([]byte, error)
|
||||
ResponseLogin(w http.ResponseWriter, user *models.User) ([]byte, error)
|
||||
ResponseJoin(user *models.User) ([]byte, error)
|
||||
ResponseLogin(user *models.User) ([]byte, error)
|
||||
}
|
||||
|
||||
type authPresenter struct {
|
||||
@ -28,7 +27,7 @@ func NewAuthPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
func (p *authPresenter) ResponseJoin(w http.ResponseWriter, user *models.User) ([]byte, error) {
|
||||
func (p *authPresenter) ResponseJoin(user *models.User) ([]byte, error) {
|
||||
resp := new(domain.JoinResponse)
|
||||
|
||||
token, err := p.jwtInteractor.NewToken(user, 24*time.Hour)
|
||||
@ -46,7 +45,7 @@ func (p *authPresenter) ResponseJoin(w http.ResponseWriter, user *models.User) (
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p *authPresenter) ResponseLogin(w http.ResponseWriter, user *models.User) ([]byte, error) {
|
||||
func (p *authPresenter) ResponseLogin(user *models.User) ([]byte, error) {
|
||||
resp := new(domain.LoginResponse)
|
||||
|
||||
token, err := p.jwtInteractor.NewToken(user, 24*time.Hour)
|
||||
|
74
backend/internal/interface/rest/presenters/organizations.go
Normal file
74
backend/internal/interface/rest/presenters/organizations.go
Normal file
@ -0,0 +1,74 @@
|
||||
package presenters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/emochka2007/block-accounting/internal/interface/rest/domain"
|
||||
"github.com/emochka2007/block-accounting/internal/pkg/models"
|
||||
)
|
||||
|
||||
type OrganizationsPresenter interface {
|
||||
ResponseCreate(organization *models.Organization) ([]byte, error)
|
||||
ResponseList(orgs []*models.Organization, nextCursor string) ([]byte, error)
|
||||
Organizations(orgs []*models.Organization) []domain.Organization
|
||||
}
|
||||
|
||||
type organizationsPresenter struct {
|
||||
}
|
||||
|
||||
func NewOrganizationsPresenter() OrganizationsPresenter {
|
||||
return &organizationsPresenter{}
|
||||
}
|
||||
|
||||
func (p *organizationsPresenter) ResponseCreate(o *models.Organization) ([]byte, error) {
|
||||
resp := &domain.NewOrganizationResponse{
|
||||
Organization: domain.Organization{
|
||||
Id: o.ID.String(),
|
||||
Name: o.Name,
|
||||
Address: o.Address,
|
||||
CreatedAt: uint64(o.CreatedAt.UnixMilli()),
|
||||
UpdatedAt: uint64(o.UpdatedAt.UnixMilli()),
|
||||
},
|
||||
}
|
||||
|
||||
out, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshal organization create response. %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p *organizationsPresenter) ResponseList(orgs []*models.Organization, nextCursor string) ([]byte, error) {
|
||||
resp := &domain.ListOrganizationsResponse{
|
||||
Collection: domain.Collection[domain.Organization]{
|
||||
Items: p.Organizations(orgs),
|
||||
Pagination: domain.Pagination{
|
||||
NextCursor: nextCursor,
|
||||
TotalItems: uint32(len(orgs)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshal organizations list response. %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p *organizationsPresenter) Organizations(orgs []*models.Organization) []domain.Organization {
|
||||
out := make([]domain.Organization, len(orgs))
|
||||
|
||||
for i, o := range orgs {
|
||||
out[i] = domain.Organization{
|
||||
Id: o.ID.String(),
|
||||
Name: o.Name,
|
||||
Address: o.Address,
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
@ -100,7 +100,7 @@ func (s *Server) buildRouter() {
|
||||
router.Route("/organizations", func(r chi.Router) {
|
||||
r = r.With(s.withAuthorization)
|
||||
|
||||
// r.Get("/", s.handle(s.controllers.Auth.Invite, "list_organizations"))
|
||||
r.Get("/", s.handle(s.controllers.Organizations.ListOrganizations, "list_organizations"))
|
||||
r.Post("/", s.handle(s.controllers.Organizations.NewOrganization, "new_organization"))
|
||||
|
||||
r.Route("/{organization_id}", func(r chi.Router) {
|
||||
@ -108,7 +108,7 @@ func (s *Server) buildRouter() {
|
||||
// r.Delete("/", s.handle(s.controllers.Organizations.NewOrganization, "delete_organization"))
|
||||
|
||||
r.Route("/transactions", func(r chi.Router) {
|
||||
r.Get("/", nil) // list
|
||||
r.Get("/", nil) // list todo add cache
|
||||
r.Post("/", nil) // add
|
||||
r.Put("/{tx_id}", nil) // update / approve (or maybe body?)
|
||||
r.Delete("/{tx_id}", nil) // remove
|
||||
@ -117,7 +117,7 @@ func (s *Server) buildRouter() {
|
||||
r.Post("/invite/{hash}", s.handle(s.controllers.Auth.Invite, "invite")) // create a new invite link
|
||||
|
||||
r.Route("/employees", func(r chi.Router) {
|
||||
r.Get("/", nil) // list
|
||||
r.Get("/", nil) // list. todo add cache
|
||||
r.Post("/", nil) // add
|
||||
r.Put("/{employee_id}", nil) // update (or maybe body?)
|
||||
r.Delete("/{employee_id}", nil) // remove
|
||||
@ -154,6 +154,8 @@ func (s *Server) handle(
|
||||
)
|
||||
|
||||
s.responseError(w, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
@ -186,6 +188,8 @@ func (s *Server) responseError(w http.ResponseWriter, e error) {
|
||||
}
|
||||
|
||||
func (s *Server) handleMw(next http.Handler) http.Handler {
|
||||
// todo add rate limiter && cirquit braker
|
||||
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
s.closeMu.RLock()
|
||||
defer s.closeMu.RUnlock()
|
||||
|
@ -2,6 +2,9 @@ package organizations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
@ -13,17 +16,34 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorUnauthorizedAccess = errors.New("unauthorized access")
|
||||
)
|
||||
|
||||
type CreateParams struct {
|
||||
Name string
|
||||
Address string
|
||||
WalletMnemonic string
|
||||
}
|
||||
|
||||
type ListParams struct {
|
||||
Ids uuid.UUIDs
|
||||
UserId uuid.UUID
|
||||
|
||||
Cursor string
|
||||
OffsetDate time.Time
|
||||
Limit uint8 // Max limit is 50 (may change)
|
||||
}
|
||||
|
||||
type OrganizationsInteractor interface {
|
||||
Create(
|
||||
ctx context.Context,
|
||||
params CreateParams,
|
||||
) (*models.Organization, error)
|
||||
List(
|
||||
ctx context.Context,
|
||||
params ListParams,
|
||||
) (*ListResponse, error)
|
||||
}
|
||||
|
||||
type organizationsInteractor struct {
|
||||
@ -41,6 +61,101 @@ func NewOrganizationsInteractor(
|
||||
}
|
||||
}
|
||||
|
||||
type organizationsListCursor struct {
|
||||
Id uuid.UUID `json:"id"`
|
||||
}
|
||||
|
||||
func newOrganizationsListCursor(id ...uuid.UUID) *organizationsListCursor {
|
||||
if len(id) > 0 {
|
||||
return &organizationsListCursor{id[0]}
|
||||
}
|
||||
|
||||
return new(organizationsListCursor)
|
||||
}
|
||||
|
||||
func (c *organizationsListCursor) encode() (string, error) {
|
||||
data, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't marshal reaction id. %w", err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(data), nil
|
||||
|
||||
}
|
||||
|
||||
func (c *organizationsListCursor) decode(s string) error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
token, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decode token. %w", err)
|
||||
}
|
||||
|
||||
return json.Unmarshal(token, c)
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
Organizations []*models.Organization
|
||||
NextCursor string
|
||||
}
|
||||
|
||||
func (i *organizationsInteractor) List(
|
||||
ctx context.Context,
|
||||
params ListParams,
|
||||
) (*ListResponse, error) {
|
||||
user, err := ctxmeta.User(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch user from context. %w", err)
|
||||
}
|
||||
|
||||
if params.UserId != uuid.Nil {
|
||||
if params.UserId != user.Id() {
|
||||
return nil, fmt.Errorf("error unauthorized organizations list access. %w", ErrorUnauthorizedAccess)
|
||||
}
|
||||
} else {
|
||||
params.UserId = user.Id()
|
||||
}
|
||||
|
||||
if params.Limit <= 0 || params.Limit > 50 {
|
||||
params.Limit = 50
|
||||
}
|
||||
|
||||
cursor := newOrganizationsListCursor()
|
||||
|
||||
if params.Cursor != "" {
|
||||
if err := cursor.decode(params.Cursor); err != nil {
|
||||
return nil, fmt.Errorf("error decode cursor value. %w", err) // maybe just log error?
|
||||
}
|
||||
}
|
||||
|
||||
orgs, err := i.orgRepository.Get(ctx, organizations.GetParams{
|
||||
UserId: params.UserId,
|
||||
Ids: params.Ids,
|
||||
OffsetDate: params.OffsetDate,
|
||||
Limit: int64(params.Limit),
|
||||
CursorId: cursor.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetch organizations. %w", err)
|
||||
}
|
||||
|
||||
var nextCursor string
|
||||
|
||||
// BUG: pagination by cursor works with errors. empty set as return value
|
||||
if len(orgs) >= 50 || len(orgs) >= int(params.Limit) {
|
||||
cursor.Id = orgs[len(orgs)-1].ID
|
||||
if nextCursor, err = cursor.encode(); err != nil {
|
||||
return nil, fmt.Errorf("error encode next page token. %w", err) // maybe just log error?
|
||||
}
|
||||
}
|
||||
|
||||
return &ListResponse{
|
||||
Organizations: orgs,
|
||||
NextCursor: nextCursor,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *organizationsInteractor) Create(
|
||||
ctx context.Context,
|
||||
params CreateParams,
|
||||
|
@ -3,6 +3,7 @@ package organizations
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -13,7 +14,12 @@ import (
|
||||
)
|
||||
|
||||
type GetParams struct {
|
||||
Ids uuid.UUIDs
|
||||
Ids uuid.UUIDs
|
||||
UserId uuid.UUID
|
||||
|
||||
OffsetDate time.Time
|
||||
CursorId uuid.UUID
|
||||
Limit int64
|
||||
}
|
||||
|
||||
type AddParticipantParams struct {
|
||||
@ -53,7 +59,12 @@ func (s *repositorySQL) Conn(ctx context.Context) sqltools.DBTX {
|
||||
func (r *repositorySQL) Create(ctx context.Context, org models.Organization) error {
|
||||
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
|
||||
query := sq.Insert("organizations").Columns(
|
||||
"id, name, address, wallet_seed, created_at, updated_at",
|
||||
"id",
|
||||
"name",
|
||||
"address",
|
||||
"wallet_seed",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).Values(
|
||||
org.ID,
|
||||
org.Name,
|
||||
@ -61,7 +72,7 @@ func (r *repositorySQL) Create(ctx context.Context, org models.Organization) err
|
||||
org.WalletSeed,
|
||||
org.CreatedAt,
|
||||
org.UpdatedAt,
|
||||
)
|
||||
).PlaceholderFormat(sq.Dollar)
|
||||
|
||||
if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil {
|
||||
return fmt.Errorf("error insert new organization. %w", err)
|
||||
@ -76,9 +87,95 @@ func (r *repositorySQL) Create(ctx context.Context, org models.Organization) err
|
||||
}
|
||||
|
||||
func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.Organization, error) {
|
||||
panic("implement me!")
|
||||
organizations := make([]*models.Organization, 0, params.Limit)
|
||||
|
||||
return nil, nil
|
||||
if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
|
||||
query := sq.Select(
|
||||
"o.id",
|
||||
"o.name",
|
||||
"o.address",
|
||||
"o.wallet_seed",
|
||||
"o.created_at",
|
||||
"o.updated_at",
|
||||
).From("organizations as o").
|
||||
Limit(uint64(params.Limit)).
|
||||
PlaceholderFormat(sq.Dollar)
|
||||
|
||||
if params.UserId != uuid.Nil {
|
||||
query = query.InnerJoin("organizations_users as ou on o.id = ou.organization_id").
|
||||
Where(sq.Eq{
|
||||
"ou.user_id": params.UserId,
|
||||
})
|
||||
}
|
||||
|
||||
if params.CursorId != uuid.Nil {
|
||||
query = query.Where(sq.Lt{
|
||||
"o.id": params.CursorId,
|
||||
})
|
||||
}
|
||||
|
||||
if params.Ids != nil {
|
||||
query = query.Where(sq.Eq{
|
||||
"o.id": params.Ids,
|
||||
})
|
||||
}
|
||||
|
||||
if !params.OffsetDate.IsZero() {
|
||||
query = query.Where(sq.GtOrEq{
|
||||
"o.updated_at": params.OffsetDate,
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Println(query.ToSql())
|
||||
|
||||
rows, err := query.RunWith(r.Conn(ctx)).QueryContext(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error fetch organizations from database. %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if closeErr := rows.Close(); closeErr != nil {
|
||||
err = errors.Join(fmt.Errorf("error close rows. %w", closeErr), err)
|
||||
}
|
||||
}()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
id uuid.UUID
|
||||
name string
|
||||
address string
|
||||
walletSeed []byte
|
||||
createdAt time.Time
|
||||
updatedAt time.Time
|
||||
)
|
||||
|
||||
if err = rows.Scan(
|
||||
&id,
|
||||
&name,
|
||||
&address,
|
||||
&walletSeed,
|
||||
&createdAt,
|
||||
&updatedAt,
|
||||
); err != nil {
|
||||
return fmt.Errorf("error scan row. %w", err)
|
||||
}
|
||||
|
||||
organizations = append(organizations, &models.Organization{
|
||||
ID: id,
|
||||
Name: name,
|
||||
Address: address,
|
||||
WalletSeed: walletSeed,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("error execute transactional operation. %w", err)
|
||||
}
|
||||
|
||||
return organizations, nil
|
||||
}
|
||||
|
||||
func (r *repositorySQL) Update(ctx context.Context, org models.Organization) error {
|
||||
@ -109,7 +206,7 @@ func (r *repositorySQL) AddParticipant(ctx context.Context, params AddParticipan
|
||||
time.Now(),
|
||||
time.Now(),
|
||||
params.IsAdmin,
|
||||
)
|
||||
).PlaceholderFormat(sq.Dollar)
|
||||
|
||||
if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil {
|
||||
return fmt.Errorf("error add new participant to organization. %w", err)
|
||||
|
@ -54,7 +54,7 @@ create index if not exists index_user_id_organization_id
|
||||
create table organizations_users (
|
||||
organization_id uuid not null references organizations(id),
|
||||
user_id uuid not null references users(id),
|
||||
employee_id uuid references employees(id) default null,
|
||||
employee_id uuid default null,
|
||||
added_at timestamp default current_timestamp,
|
||||
updated_at timestamp default current_timestamp,
|
||||
deleted_at timestamp default null,
|
||||
|
Loading…
Reference in New Issue
Block a user