diff --git a/domains/accounts/accounts.go b/domains/accounts/accounts.go index bd666af..d6e89e9 100644 --- a/domains/accounts/accounts.go +++ b/domains/accounts/accounts.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" "ruben/inventory2/consts" @@ -500,6 +501,97 @@ func (db *Store) DeleteMockSyncGroup(ctx context.Context, acctID, syncGroupID in return nil } +func (db *Store) StartEditingMockSyncGroup(ctx context.Context, acctID, syncGroupID int64) (int64, bool, error) { + rows, err := db.db.Query( + ctx, + ` + WITH deleted_row AS ( + DELETE FROM + mock.sync_group_editing + WHERE + account_id = @account_id + AND sync_group_id <> @sync_group_id + RETURNING + account_id, + sync_group_id + ), new_row AS ( + INSERT INTO + mock.sync_group_editing ( + account_id, + sync_group_id + ) + SELECT + x.account_id, + x.sync_group_id + FROM ( + VALUES ( + @account_id, + @sync_group_id + ) + ) AS x(account_id, sync_group_id) + LEFT JOIN + deleted_row + ON + TRUE + ON CONFLICT + DO NOTHING + ) + SELECT + sync_group_id + FROM + deleted_row + `, + pgx.NamedArgs{ + "account_id": acctID, + "sync_group_id": syncGroupID, + }, + ) + if err != nil { + return 0, false, fmt.Errorf("failed to perform query: %w", err) + } + + v, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[pgtype.Int8]) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return 0, false, nil + } + return 0, false, fmt.Errorf("failed to scan rows: %w", err) + } + + return v.Int64, v.Valid, nil +} + +func (db *Store) MockSyncGroupIsBeingEdited(ctx context.Context, acctID, syncGroupID int64) (bool, error) { + rows, err := db.db.Query( + ctx, + ` + SELECT + true + FROM + mock.sync_group_editing + WHERE + account_id = @account_id + AND sync_group_id = @sync_group_id + `, + pgx.NamedArgs{ + "account_id": acctID, + "sync_group_id": syncGroupID, + }, + ) + if err != nil { + return false, fmt.Errorf("failed to perform query: %w", err) + } + + if _, err = pgx.CollectExactlyOneRow(rows, pgx.RowTo[pgtype.Bool]); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return false, nil + } + return false, fmt.Errorf("failed to scan rows: %w", err) + } + + return true, nil +} + // additional context func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) { diff --git a/domains/accounts/store_with_context.go b/domains/accounts/store_with_context.go index a267260..984ad8f 100644 --- a/domains/accounts/store_with_context.go +++ b/domains/accounts/store_with_context.go @@ -65,6 +65,14 @@ func (v_ctx *StoreWithContext) DeleteMockSyncGroup(acctID int64, syncGroupID int return v_ctx.Store.DeleteMockSyncGroup(v_ctx.ctx, acctID, syncGroupID) } +func (v_ctx *StoreWithContext) StartEditingMockSyncGroup(acctID int64, syncGroupID int64) (int64, bool, error) { + return v_ctx.Store.StartEditingMockSyncGroup(v_ctx.ctx, acctID, syncGroupID) +} + +func (v_ctx *StoreWithContext) MockSyncGroupIsBeingEdited(acctID int64, syncGroupID int64) (bool, error) { + return v_ctx.Store.MockSyncGroupIsBeingEdited(v_ctx.ctx, acctID, syncGroupID) +} + func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) { return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID) } diff --git a/server/api/accounts/router.go b/server/api/accounts/router.go index 4475905..ea2c6e0 100644 --- a/server/api/accounts/router.go +++ b/server/api/accounts/router.go @@ -58,6 +58,7 @@ func Routes( mockSyncGroups := syncGroups.Group("/mock") mockSyncGroups.POST("", pub.Publish("/:acctID/inventory/sync-groups/mock"), response.Handler(as.saveNewMockSyncGroup)) mockSyncGroups.DELETE("/:syncGroupID", pub.Publish("/:acctID/inventory/sync-groups/mock"), response.Handler(as.deleteMockSyncGroup)) + mockSyncGroups.PUT("/editing", pub.Publish("/:acctID/inventory/sync-groups/mock/editing"), response.Handler(as.selectMockSyncGroupForEditing)) mockDraftListings := mockSyncGroups.Group("/draft/listings", pub.Publish("/:acctID/inventory/sync-groups/mock/draft/listings")) mockDraftListings.POST("", response.Handler(as.createMockSyncGroupListingDraft)) @@ -534,6 +535,41 @@ func (s *accountSubrouter) deleteMockSyncGroup(c *gin.Context) (response.Respons return response.StatusCreated(), nil } +// PUT /:acctID/inventory/sync-groups/mock/editing +func (s *accountSubrouter) selectMockSyncGroupForEditing(c *gin.Context) (response.Response, error) { + r := c.Request + ctx := r.Context() + acctID := auth.GetIdentity(ctx).Account.AccountID + + var ( + syncGroupID int64 + ) + + if err := param.Form("syncGroupID", param.Int64(&syncGroupID)). + Unmarshal(c); err != nil { + return nil, err + } + + prevSyncGroupID, ok, err := s.accts.StartEditingMockSyncGroup(ctx, acctID, syncGroupID) + if err != nil { + return nil, response.Errorf("failed to save new mock sync group: %w", response.ErrorFromConstant(err)) + } + + // emit events indicating sync groups being edited and no longer being edited + + evts := make([]string, 1, 2) + evts[0] = fmt.Sprintf("accounts_%d_inventory_sync-groups_mock_%d", acctID, syncGroupID) + if ok { + evts = append(evts, fmt.Sprintf("accounts_%d_inventory_sync-groups_mock_%d", acctID, prevSyncGroupID)) + } + + if err := s.pub.Push(c, acctID, evts...); err != nil { + s.log.Errorf("failed to publish mock sync group editing events: %v", err) + } + + return response.StatusOK(), nil +} + func lowerSnakeCase(s accounts.Platform) string { return strings.ToLower(strings.Join(strings.Split(string(s), " "), "_")) } diff --git a/templates/components/accounts/{acctID.int64}/inventory/sync-groups/mock/{syncGroupID.int64}/list-item.html.tmpl b/templates/components/accounts/{acctID.int64}/inventory/sync-groups/mock/{syncGroupID.int64}/list-item.html.tmpl index 6100a72..858513d 100644 --- a/templates/components/accounts/{acctID.int64}/inventory/sync-groups/mock/{syncGroupID.int64}/list-item.html.tmpl +++ b/templates/components/accounts/{acctID.int64}/inventory/sync-groups/mock/{syncGroupID.int64}/list-item.html.tmpl @@ -2,6 +2,10 @@ {{- $syncGroupID := .PathParams.syncGroupID }} {{- $acctID := .PathParams.acctID }} +{{- $open := false }} +{{- if .Request }} + {{- $open = eq (.Request.URL.Query.Get "open") "true" }} +{{- end }} {{- $grp := .SyncGroup }} {{- if not $grp }} @@ -9,11 +13,15 @@ {{- end }} +{{- $id := (printf "sync-group-%d" $syncGroupID) }}