saved mock listing

This commit is contained in:
2026-02-04 22:48:57 -07:00
parent a8dda86882
commit 813fbb11b1
13 changed files with 792 additions and 369 deletions
@@ -0,0 +1,34 @@
BEGIN;
ALTER TABLE mock.shop_etsy_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_ebay_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_walmart_marketplace_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_amazon_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_big_cartel_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_ecwid_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_zoho_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_square_online_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_squarespace_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_wix_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_woo_commerce_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_shopify_listings
DROP COLUMN "count";
ALTER TABLE mock.shop_tiktok_listings
DROP COLUMN "count";
COMMIT;
@@ -0,0 +1,34 @@
BEGIN;
ALTER TABLE mock.shop_tiktok_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_shopify_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_woo_commerce_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_wix_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_squarespace_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_square_online_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_zoho_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_ecwid_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_big_cartel_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_amazon_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_walmart_marketplace_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_ebay_listings
ADD COLUMN "count" INTEGER NOT NULL;
ALTER TABLE mock.shop_etsy_listings
ADD COLUMN "count" INTEGER NOT NULL;
COMMIT;
@@ -0,0 +1,47 @@
BEGIN;
ALTER TABLE mock.shop_etsy_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_ebay_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_walmart_marketplace_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_amazon_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_big_cartel_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_ecwid_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_zoho_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_square_online_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_squarespace_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_wix_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_woo_commerce_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_shopify_listings
DROP COLUMN sku,
DROP COLUMN description;
ALTER TABLE mock.shop_tiktok_listings
DROP COLUMN sku,
DROP COLUMN description;
COMMIT;
@@ -0,0 +1,47 @@
BEGIN;
ALTER TABLE mock.shop_tiktok_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_shopify_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_woo_commerce_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_wix_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_squarespace_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_square_online_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_zoho_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_ecwid_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_big_cartel_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_amazon_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_walmart_marketplace_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_ebay_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
ALTER TABLE mock.shop_etsy_listings
ADD COLUMN sku TEXT NOT NULL,
ADD COLUMN description TEXT NOT NULL;
COMMIT;
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 245 KiB

+39
View File
@@ -23,6 +23,9 @@ entity "**shop_amazon_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_amazon_sync_group_listings**" { entity "**shop_amazon_sync_group_listings**" {
@@ -48,6 +51,9 @@ entity "**shop_big_cartel_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_big_cartel_sync_group_listings**" { entity "**shop_big_cartel_sync_group_listings**" {
@@ -73,6 +79,9 @@ entity "**shop_ebay_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_ebay_sync_group_listings**" { entity "**shop_ebay_sync_group_listings**" {
@@ -98,6 +107,9 @@ entity "**shop_ecwid_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_ecwid_sync_group_listings**" { entity "**shop_ecwid_sync_group_listings**" {
@@ -123,6 +135,9 @@ entity "**shop_etsy_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_etsy_sync_group_listings**" { entity "**shop_etsy_sync_group_listings**" {
@@ -148,6 +163,9 @@ entity "**shop_shopify_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_shopify_sync_group_listings**" { entity "**shop_shopify_sync_group_listings**" {
@@ -173,6 +191,9 @@ entity "**shop_square_online_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_square_online_sync_group_listings**" { entity "**shop_square_online_sync_group_listings**" {
@@ -198,6 +219,9 @@ entity "**shop_squarespace_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_squarespace_sync_group_listings**" { entity "**shop_squarespace_sync_group_listings**" {
@@ -223,6 +247,9 @@ entity "**shop_tiktok_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_tiktok_sync_group_listings**" { entity "**shop_tiktok_sync_group_listings**" {
@@ -248,6 +275,9 @@ entity "**shop_walmart_marketplace_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_walmart_marketplace_sync_group_listings**" { entity "**shop_walmart_marketplace_sync_group_listings**" {
@@ -273,6 +303,9 @@ entity "**shop_wix_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_wix_sync_group_listings**" { entity "**shop_wix_sync_group_listings**" {
@@ -298,6 +331,9 @@ entity "**shop_woo_commerce_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_woo_commerce_sync_group_listings**" { entity "**shop_woo_commerce_sync_group_listings**" {
@@ -323,6 +359,9 @@ entity "**shop_zoho_listings**" {
-- --
*""account_id"": //integer [FK]// *""account_id"": //integer [FK]//
*""name"": //text // *""name"": //text //
*""count"": //integer //
*""sku"": //text //
*""description"": //text //
} }
entity "**shop_zoho_sync_group_listings**" { entity "**shop_zoho_sync_group_listings**" {
-275
View File
@@ -10,7 +10,6 @@ import (
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/logging" "ruben/inventory2/internal/logging"
"github.com/google/uuid"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
) )
@@ -43,28 +42,6 @@ type (
Description string Description string
Count int64 Count int64
} }
// ids
AccountIDs struct {
AccountID int64
}
AccountShopIDs struct {
AccountIDs
Platform Platform
ShopID string
}
AccountShopListingIDs struct {
AccountShopIDs
ListingID string
}
MockShop struct {
AccountShopIDs
Name string
}
) )
func NewStore(logger *logging.Logger, db *pgxpool.Pool) *Store { func NewStore(logger *logging.Logger, db *pgxpool.Pool) *Store {
@@ -315,258 +292,6 @@ func (db *Store) GetListingsForShop(ctx context.Context, acctID int64, platform
return vs, nil return vs, nil
} }
func (db *Store) CreateMockShop(ctx context.Context, acctID int64, platform Platform, name string) (shopID uuid.UUID, err error) {
shopTableName, err := getMockShopTableName(platform)
if err != nil {
return uuid.Nil, err
}
tx, err := db.db.Begin(ctx)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to being transaction: %w", err)
}
defer tx.Rollback(ctx)
// TODO: are we really going to just copy the id between accounts and mock.accounts?
// upsert mock account
rows, err := tx.Query(
ctx,
`
WITH existing_account(account_id, user_id) AS (
SELECT
account_id,
user_id
FROM
accounts
WHERE
account_id = @account_id
), existing_mock_account AS (
SELECT
user_id
FROM
accounts
WHERE
account_id = @account_id
), inserted_mock_account AS (
INSERT INTO
mock.accounts (
account_id,
user_id
)
SELECT
account_id,
user_id
FROM
existing_account
ON CONFLICT
DO NOTHING
RETURNING
user_id
)
SELECT
COALESCE(ia.user_id, ea.user_id)
FROM
inserted_mock_account ia
FULL OUTER JOIN
existing_mock_account ea
USING
(user_id)
`,
pgx.NamedArgs{
"account_id": acctID,
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("test query failed: %w", err)
}
userID, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return uuid.Nil, fmt.Errorf("%w: account not found", consts.ErrNotFound)
}
return uuid.Nil, fmt.Errorf("failed to scan rows: %w", err)
}
// create the mock shop
shopID = uuid.New()
_, err = tx.Exec(
ctx,
fmt.Sprintf(
`
INSERT INTO
%s (
user_id,
account_id,
shop_id,
name
)
VALUES (
@user_id,
@account_id,
@shop_id,
@name
)
`,
shopTableName,
),
pgx.NamedArgs{
"account_id": acctID,
"user_id": userID,
"shop_id": shopID,
"name": name,
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to perform query to create mock shop: %w", err)
}
if err := tx.Commit(ctx); err != nil {
return uuid.Nil, fmt.Errorf("failed to commit transaction: %w", err)
}
return shopID, nil
}
func (db *Store) ListMockShopsForPlatform(ctx context.Context, acctID int64, platform Platform) ([]MockShop, error) {
shopTableName, err := getMockShopTableName(platform)
if err != nil {
return nil, err
}
rows, err := db.db.Query(
ctx,
fmt.Sprintf(
`
SELECT
shop_id,
name
FROM
%s
WHERE
account_id = @account_id
`,
shopTableName,
),
pgx.NamedArgs{
"account_id": acctID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
vs, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct {
Shop_id uuid.UUID
Name string
}])
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
shops := make([]MockShop, len(vs))
for i, v := range vs {
shops[i] = MockShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: acctID,
},
Platform: platform,
ShopID: v.Shop_id.String(),
},
Name: v.Name,
}
}
return shops, nil
}
func (db *Store) GetMockShop(ctx context.Context, acctID int64, platform Platform, shopID string) (*MockShop, error) {
shopTableName, err := getMockShopTableName(platform)
if err != nil {
return nil, err
}
rows, err := db.db.Query(
ctx,
fmt.Sprintf(
`
SELECT
name
FROM
%s
WHERE
account_id = @account_id
AND shop_id = @shop_id
`,
shopTableName,
),
pgx.NamedArgs{
"account_id": acctID,
"shop_id": shopID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
name, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, consts.ErrNotFound
}
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
return &MockShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: acctID,
},
Platform: platform,
ShopID: shopID,
},
Name: name,
}, nil
}
func getMockShopTableName(platform Platform) (string, error) {
switch platform {
case Amazon:
return "mock.shop_amazon", nil
case BigCartel:
return "mock.shop_big_cartel", nil
case Ebay:
return "mock.shop_ebay", nil
case Ecwid:
return "mock.shop_ecwid", nil
case Etsy:
return "mock.shop_etsy", nil
case Shopify:
return "mock.shop_shopify", nil
case SquareOnline:
return "mock.shop_square_online", nil
case Squarespace:
return "mock.shop_squarespace", nil
case Tiktok:
return "mock.shop_tiktok", nil
case WalmartMarketplace:
return "mock.shop_walmart_marketplace", nil
case Wix:
return "mock.shop_wix", nil
case WooCommerce:
return "mock.shop_woo_commerce", nil
case Zoho:
return "mock.shop_zoho", nil
default:
return "", fmt.Errorf("%w: unrecognized platform", consts.ErrBadRequest)
}
}
// additional context // additional context
func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) { func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) {
+39
View File
@@ -0,0 +1,39 @@
package accounts
type (
AccountIDs struct {
AccountID int64
}
AccountShopIDs struct {
AccountIDs
Platform Platform
ShopID string
}
AccountShopListingIDs struct {
AccountShopIDs
ListingID string
}
)
func NewAccountIDs(acctID int64) AccountIDs {
return AccountIDs{
AccountID: acctID,
}
}
func (ids AccountIDs) ShopID(p Platform, id string) AccountShopIDs {
return AccountShopIDs{
AccountIDs: ids,
Platform: p,
ShopID: id,
}
}
func (ids AccountShopIDs) ListingID(id string) AccountShopListingIDs {
return AccountShopListingIDs{
AccountShopIDs: ids,
ListingID: id,
}
}
+457
View File
@@ -0,0 +1,457 @@
package accounts
import (
"context"
"errors"
"fmt"
"ruben/inventory2/internal/consts"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
type (
MockShop struct {
AccountShopIDs
Name string
}
MockListing struct {
AccountShopListingIDs
SKU string
Name string
Description string
Count int64
}
)
func (db *Store) CreateMockShop(ctx context.Context, acctID int64, platform Platform, name string) (shopID uuid.UUID, err error) {
shopTableName, err := getMockShopTableName(platform)
if err != nil {
return uuid.Nil, err
}
tx, err := db.db.Begin(ctx)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to being transaction: %w", err)
}
defer tx.Rollback(ctx)
// TODO: are we really going to just copy the id between accounts and mock.accounts?
// upsert mock account
rows, err := tx.Query(
ctx,
`
WITH existing_account(account_id, user_id) AS (
SELECT
account_id,
user_id
FROM
accounts
WHERE
account_id = @account_id
), existing_mock_account AS (
SELECT
user_id
FROM
accounts
WHERE
account_id = @account_id
), inserted_mock_account AS (
INSERT INTO
mock.accounts (
account_id,
user_id
)
SELECT
account_id,
user_id
FROM
existing_account
ON CONFLICT
DO NOTHING
RETURNING
user_id
)
SELECT
COALESCE(ia.user_id, ea.user_id)
FROM
inserted_mock_account ia
FULL OUTER JOIN
existing_mock_account ea
USING
(user_id)
`,
pgx.NamedArgs{
"account_id": acctID,
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to perform query: %w", err)
}
userID, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return uuid.Nil, fmt.Errorf("%w: account not found", consts.ErrNotFound)
}
return uuid.Nil, fmt.Errorf("failed to scan rows: %w", err)
}
// create the mock shop
shopID = uuid.New()
_, err = tx.Exec(
ctx,
fmt.Sprintf(
`
INSERT INTO
%s (
user_id,
account_id,
shop_id,
name
)
VALUES (
@user_id,
@account_id,
@shop_id,
@name
)
`,
shopTableName,
),
pgx.NamedArgs{
"account_id": acctID,
"user_id": userID,
"shop_id": shopID,
"name": name,
},
)
if err != nil {
return uuid.Nil, fmt.Errorf("failed to perform query to create mock shop: %w", err)
}
if err := tx.Commit(ctx); err != nil {
return uuid.Nil, fmt.Errorf("failed to commit transaction: %w", err)
}
return shopID, nil
}
func (db *Store) ListMockShopsForPlatform(ctx context.Context, acctID int64, platform Platform) ([]MockShop, error) {
shopTableName, err := getMockShopTableName(platform)
if err != nil {
return nil, err
}
rows, err := db.db.Query(
ctx,
fmt.Sprintf(
`
SELECT
shop_id,
name
FROM
%s
WHERE
account_id = @account_id
`,
shopTableName,
),
pgx.NamedArgs{
"account_id": acctID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
vs, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct {
Shop_id uuid.UUID
Name string
}])
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
shops := make([]MockShop, len(vs))
for i, v := range vs {
shops[i] = MockShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: acctID,
},
Platform: platform,
ShopID: v.Shop_id.String(),
},
Name: v.Name,
}
}
return shops, nil
}
func (db *Store) GetMockShop(ctx context.Context, acctID int64, platform Platform, shopID string) (*MockShop, error) {
shopTableName, err := getMockShopTableName(platform)
if err != nil {
return nil, err
}
rows, err := db.db.Query(
ctx,
fmt.Sprintf(
`
SELECT
name
FROM
%s
WHERE
account_id = @account_id
AND shop_id = @shop_id
`,
shopTableName,
),
pgx.NamedArgs{
"account_id": acctID,
"shop_id": shopID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
name, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, consts.ErrNotFound
}
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
return &MockShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: acctID,
},
Platform: platform,
ShopID: shopID,
},
Name: name,
}, nil
}
func (db *Store) CreateMockListing(ctx context.Context, listing MockListing) (MockListing, error) {
shopTableName, listingTableName, err := getMockShopAndListingsTableNames(listing.Platform)
if err != nil {
return MockListing{}, err
}
listing.ListingID = uuid.New().String()
tag, err := db.db.Exec(
ctx,
fmt.Sprintf(
`
WITH existing_shop(account_id, shop_id) AS (
SELECT
account_id,
shop_id
FROM
%s
WHERE
account_id = @account_id
AND shop_id = @shop_id
), new_values(listing_id, sku, name, description, "count") AS (
SELECT
@listing_id::text,
@sku::text,
@name::text,
@description::text,
@count::integer
)
INSERT INTO
%s (
account_id,
shop_id,
listing_id,
sku,
name,
description,
count
)
SELECT
account_id,
shop_id,
listing_id,
sku,
name,
description,
"count"
FROM
existing_shop
JOIN
new_values
ON
true
`,
shopTableName,
listingTableName,
),
pgx.NamedArgs{
"account_id": listing.AccountID,
"shop_id": listing.ShopID,
"listing_id": listing.ListingID,
"sku": listing.SKU,
"name": listing.Name,
"description": listing.Description,
"count": listing.Count + 5,
},
)
if err != nil {
return MockListing{}, fmt.Errorf("failed to perform query: %w", err)
}
if tag.RowsAffected() == 0 {
return MockListing{}, fmt.Errorf("%w: shop not found", consts.ErrNotFound)
}
return listing, nil
}
func (db *Store) ListMockListingsForShop(ctx context.Context, acctID int64, platform Platform, shopID string) ([]MockListing, error) {
shopTableName, listingsTableName, err := getMockShopAndListingsTableNames(platform)
if err != nil {
return nil, err
}
rows, err := db.db.Query(
ctx,
fmt.Sprintf(
`
WITH shop_exists(shop_exists) AS (
SELECT
true
FROM
%s
WHERE
account_id = @account_id
AND shop_id = @shop_id
)
SELECT
COALESCE(shop_exists, false) AS shop_exists,
listing_id,
sku,
name,
description,
"count"
FROM
shop_exists
LEFT JOIN
%s
ON
TRUE
WHERE
account_id = @account_id
AND shop_id = @shop_id
`,
shopTableName,
listingsTableName,
),
pgx.NamedArgs{
"account_id": acctID,
"shop_id": shopID,
},
)
if err != nil {
return nil, fmt.Errorf("failed to perform query: %w", err)
}
vs, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[struct {
Shop_exists bool
Listing_id string
SKU string
Name string
Description string
Count int64
}])
if err != nil {
return nil, fmt.Errorf("failed to scan rows: %w", err)
}
// check if the shop exists, and confirm there were actually listings (LEFT JOIN)
if len(vs) == 0 || !vs[0].Shop_exists {
return nil, fmt.Errorf("%w: shop not found", consts.ErrNotFound)
}
if vs[0].SKU == "" {
return nil, nil
}
listings := make([]MockListing, len(vs))
for i, v := range vs {
listings[i] = MockListing{
AccountShopListingIDs: AccountShopListingIDs{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: acctID,
},
Platform: platform,
ShopID: shopID,
},
ListingID: v.Listing_id,
},
SKU: v.SKU,
Name: v.Name,
Description: v.Description,
Count: v.Count,
}
}
return listings, nil
}
func getMockShopTableName(platform Platform) (string, error) {
switch platform {
case Amazon:
return "mock.shop_amazon", nil
case BigCartel:
return "mock.shop_big_cartel", nil
case Ebay:
return "mock.shop_ebay", nil
case Ecwid:
return "mock.shop_ecwid", nil
case Etsy:
return "mock.shop_etsy", nil
case Shopify:
return "mock.shop_shopify", nil
case SquareOnline:
return "mock.shop_square_online", nil
case Squarespace:
return "mock.shop_squarespace", nil
case Tiktok:
return "mock.shop_tiktok", nil
case WalmartMarketplace:
return "mock.shop_walmart_marketplace", nil
case Wix:
return "mock.shop_wix", nil
case WooCommerce:
return "mock.shop_woo_commerce", nil
case Zoho:
return "mock.shop_zoho", nil
default:
return "", fmt.Errorf("%w: unrecognized platform", consts.ErrBadRequest)
}
}
func getMockShopAndListingsTableNames(platform Platform) (shop string, listing string, err error) {
shop, err = getMockShopTableName(platform)
if err != nil {
return "", "", err
}
return shop, shop + "_listings", nil
}
@@ -48,6 +48,10 @@ func (v_ctx *StoreWithContext) GetListingsForShop(acctID int64, platform Platfor
return v_ctx.Store.GetListingsForShop(v_ctx.ctx, acctID, platform, shopID) return v_ctx.Store.GetListingsForShop(v_ctx.ctx, acctID, platform, shopID)
} }
func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) {
return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID)
}
func (v_ctx *StoreWithContext) CreateMockShop(acctID int64, platform Platform, name string) (uuid.UUID, error) { func (v_ctx *StoreWithContext) CreateMockShop(acctID int64, platform Platform, name string) (uuid.UUID, error) {
return v_ctx.Store.CreateMockShop(v_ctx.ctx, acctID, platform, name) return v_ctx.Store.CreateMockShop(v_ctx.ctx, acctID, platform, name)
} }
@@ -60,8 +64,12 @@ func (v_ctx *StoreWithContext) GetMockShop(acctID int64, platform Platform, shop
return v_ctx.Store.GetMockShop(v_ctx.ctx, acctID, platform, shopID) return v_ctx.Store.GetMockShop(v_ctx.ctx, acctID, platform, shopID)
} }
func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) { func (v_ctx *StoreWithContext) CreateMockListing(listing MockListing) (MockListing, error) {
return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID) return v_ctx.Store.CreateMockListing(v_ctx.ctx, listing)
}
func (v_ctx *StoreWithContext) ListMockListingsForShop(acctID int64, platform Platform, shopID string) ([]MockListing, error) {
return v_ctx.Store.ListMockListingsForShop(v_ctx.ctx, acctID, platform, shopID)
} }
func (v_ctx *StoreWithContext) SetOrderOfPlatformOnAccountPage(acctID int64, platform Platform, orderIndex int) ([]Platform, int, error) { func (v_ctx *StoreWithContext) SetOrderOfPlatformOnAccountPage(acctID int64, platform Platform, orderIndex int) ([]Platform, int, error) {
+16 -5
View File
@@ -269,6 +269,8 @@ func (s *accountSubrouter) createMockShop(c *gin.Context) (response.Response, er
// POST /:acctID/platforms/:platform/shops/mocks/:shop-id/listings // POST /:acctID/platforms/:platform/shops/mocks/:shop-id/listings
func (s *accountSubrouter) addMockListing(c *gin.Context) (response.Response, error) { func (s *accountSubrouter) addMockListing(c *gin.Context) (response.Response, error) {
acctID := auth.GetIdentity(c).Account.AccountID
var ( var (
platform accounts.Platform platform accounts.Platform
shopID string shopID string
@@ -287,11 +289,20 @@ func (s *accountSubrouter) addMockListing(c *gin.Context) (response.Response, er
return nil, err return nil, err
} }
s.log.Debugf("platform: %v", platform) _, err = s.accts.CreateMockListing(
s.log.Debugf("shopID: %v", shopID) c,
s.log.Debugf("name: %v", name) accounts.MockListing{
s.log.Debugf("sku: %v", sku) AccountShopListingIDs: accounts.NewAccountIDs(acctID).
s.log.Debugf("description: %v", description) ShopID(platform, shopID).
ListingID(""),
Name: name,
SKU: sku,
Description: description,
},
)
if err != nil {
return nil, fmt.Errorf("failed to create new listing: %w", err)
}
return nil, nil return nil, nil
} }
+43 -72
View File
@@ -15,8 +15,6 @@
--text-lg--line-height: calc(1.75 / 1.125); --text-lg--line-height: calc(1.75 / 1.125);
--text-xl: 1.25rem; --text-xl: 1.25rem;
--text-xl--line-height: calc(1.75 / 1.25); --text-xl--line-height: calc(1.75 / 1.25);
--text-2xl: 1.5rem;
--text-2xl--line-height: calc(2 / 1.5);
--text-3xl: 1.875rem; --text-3xl: 1.875rem;
--text-6xl: 3.75rem; --text-6xl: 3.75rem;
--font-weight-semibold: 600; --font-weight-semibold: 600;
@@ -291,6 +289,18 @@
.h-\[2rem\] { .h-\[2rem\] {
height: 2rem; height: 2rem;
} }
.h-\[10em\] {
height: 10em;
}
.h-fit {
height: fit-content;
}
.max-h-\[10em\] {
max-height: 10em;
}
.max-h-\[50em\] {
max-height: 50em;
}
.max-h-full { .max-h-full {
max-height: 100%; max-height: 100%;
} }
@@ -333,12 +343,6 @@
.basis-full { .basis-full {
flex-basis: 100%; flex-basis: 100%;
} }
.border-collapse {
border-collapse: collapse;
}
.transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
}
.animate-pulse { .animate-pulse {
animation: var(--animate-pulse); animation: var(--animate-pulse);
} }
@@ -351,9 +355,6 @@
.resize { .resize {
resize: both; resize: both;
} }
.list-disc {
list-style-type: disc;
}
.list-none { .list-none {
list-style-type: none; list-style-type: none;
} }
@@ -372,9 +373,6 @@
.grid-cols-\[max-content_max-content\] { .grid-cols-\[max-content_max-content\] {
grid-template-columns: max-content max-content; grid-template-columns: max-content max-content;
} }
.grid-rows-\[auto\] {
grid-template-rows: auto;
}
.grid-rows-\[auto_auto_auto\] { .grid-rows-\[auto_auto_auto\] {
grid-template-rows: auto auto auto; grid-template-rows: auto auto auto;
} }
@@ -417,9 +415,15 @@
.overflow-x-hidden { .overflow-x-hidden {
overflow-x: hidden; overflow-x: hidden;
} }
.overflow-y-auto {
overflow-y: auto;
}
.overflow-y-hidden { .overflow-y-hidden {
overflow-y: hidden; overflow-y: hidden;
} }
.overflow-y-scroll {
overflow-y: scroll;
}
.rounded { .rounded {
border-radius: 0.25rem; border-radius: 0.25rem;
} }
@@ -435,10 +439,6 @@
.rounded-sm { .rounded-sm {
border-radius: calc(var(--radius) - 4px); border-radius: calc(var(--radius) - 4px);
} }
.border {
border-style: var(--tw-border-style);
border-width: 1px;
}
.border-\[1px\] { .border-\[1px\] {
border-style: var(--tw-border-style); border-style: var(--tw-border-style);
border-width: 1px; border-width: 1px;
@@ -455,9 +455,6 @@
--tw-border-style: solid; --tw-border-style: solid;
border-style: solid; border-style: solid;
} }
.border-background {
border-color: var(--background);
}
.border-foreground { .border-foreground {
border-color: var(--foreground); border-color: var(--foreground);
} }
@@ -467,9 +464,6 @@
.border-sidebar-border { .border-sidebar-border {
border-color: var(--sidebar-border); border-color: var(--sidebar-border);
} }
.border-white {
border-color: var(--color-white);
}
.bg-accent { .bg-accent {
background-color: var(--accent); background-color: var(--accent);
} }
@@ -494,9 +488,6 @@
.py-\[0\.5em\] { .py-\[0\.5em\] {
padding-block: 0.5em; padding-block: 0.5em;
} }
.py-\[1em\] {
padding-block: 1em;
}
.pt-\[1em\] { .pt-\[1em\] {
padding-top: 1em; padding-top: 1em;
} }
@@ -518,13 +509,6 @@
.font-display { .font-display {
font-family: var(--display-family); font-family: var(--display-family);
} }
.font-text {
font-family: var(--text-family);
}
.text-2xl {
font-size: var(--text-2xl);
line-height: var(--tw-leading, var(--text-2xl--line-height));
}
.text-lg { .text-lg {
font-size: var(--text-lg); font-size: var(--text-lg);
line-height: var(--tw-leading, var(--text-lg--line-height)); line-height: var(--tw-leading, var(--text-lg--line-height));
@@ -548,9 +532,6 @@
.text-nowrap { .text-nowrap {
text-wrap: nowrap; text-wrap: nowrap;
} }
.text-wrap {
text-wrap: wrap;
}
.capitalize { .capitalize {
text-transform: capitalize; text-transform: capitalize;
} }
@@ -560,10 +541,6 @@
.underline { .underline {
text-decoration-line: underline; text-decoration-line: underline;
} }
.outline {
outline-style: var(--tw-outline-style);
outline-width: 1px;
}
.filter { .filter {
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
} }
@@ -1139,26 +1116,6 @@
} }
} }
} }
@property --tw-rotate-x {
syntax: "*";
inherits: false;
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
}
@property --tw-border-style { @property --tw-border-style {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
@@ -1168,11 +1125,6 @@
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
} }
@property --tw-outline-style {
syntax: "*";
inherits: false;
initial-value: solid;
}
@property --tw-blur { @property --tw-blur {
syntax: "*"; syntax: "*";
inherits: false; inherits: false;
@@ -1241,6 +1193,26 @@
inherits: false; inherits: false;
initial-value: 1; initial-value: 1;
} }
@property --tw-rotate-x {
syntax: "*";
inherits: false;
}
@property --tw-rotate-y {
syntax: "*";
inherits: false;
}
@property --tw-rotate-z {
syntax: "*";
inherits: false;
}
@property --tw-skew-x {
syntax: "*";
inherits: false;
}
@property --tw-skew-y {
syntax: "*";
inherits: false;
}
@property --tw-content { @property --tw-content {
syntax: "*"; syntax: "*";
initial-value: ""; initial-value: "";
@@ -1258,14 +1230,8 @@
@layer properties { @layer properties {
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
*, ::before, ::after, ::backdrop { *, ::before, ::after, ::backdrop {
--tw-rotate-x: initial;
--tw-rotate-y: initial;
--tw-rotate-z: initial;
--tw-skew-x: initial;
--tw-skew-y: initial;
--tw-border-style: solid; --tw-border-style: solid;
--tw-font-weight: initial; --tw-font-weight: initial;
--tw-outline-style: solid;
--tw-blur: initial; --tw-blur: initial;
--tw-brightness: initial; --tw-brightness: initial;
--tw-contrast: initial; --tw-contrast: initial;
@@ -1282,6 +1248,11 @@
--tw-scale-x: 1; --tw-scale-x: 1;
--tw-scale-y: 1; --tw-scale-y: 1;
--tw-scale-z: 1; --tw-scale-z: 1;
--tw-rotate-x: initial;
--tw-rotate-y: initial;
--tw-rotate-z: initial;
--tw-skew-x: initial;
--tw-skew-y: initial;
--tw-content: ""; --tw-content: "";
--tw-leading: initial; --tw-leading: initial;
} }
@@ -27,7 +27,7 @@
</h3> </h3>
</header> </header>
<div class="grid grid-cols-[1fr_1fr]"> <div class="grid grid-cols-[1fr_1fr] gap-[1em] p-[1em]">
<form <form
id="new-listing-form" id="new-listing-form"
hx-post={{ printf "/api/accounts/%d/platforms/amazon/shops/mocks/%s/listings" $acctID $shopID }} hx-post={{ printf "/api/accounts/%d/platforms/amazon/shops/mocks/%s/listings" $acctID $shopID }}
@@ -37,7 +37,10 @@
grid-cols-[max-content_1fr] grid-cols-[max-content_1fr]
grid-rows-[auto_auto_auto] grid-rows-[auto_auto_auto]
gap-[0.5em] gap-[0.5em]
m-[0.5em] p-[1em]
bg-card
h-fit
rounded-lg
" "
> >
<label for="name"> <label for="name">
@@ -49,7 +52,7 @@
required required
minlength="5" minlength="5"
placeholder="What's in a name..." placeholder="What's in a name..."
class="bg-card p-[0.25em]" class="bg-accent p-[0.25em] rounded-sm"
/> />
<label for="sku"> <label for="sku">
SKU: SKU:
@@ -58,7 +61,7 @@
type="text" type="text"
name="sku" name="sku"
required required
class="bg-card p-[0.25em]" class="bg-accent p-[0.25em] rounded-sm"
/> />
<label for="description"> <label for="description">
Description: Description:
@@ -69,7 +72,7 @@
required required
minlength="10" minlength="10"
placeholder="Anything will do..." placeholder="Anything will do..."
class="bg-card p-[0.25em]" class="bg-accent p-[0.25em] rounded-sm"
/> />
{{ component "button" {{ component "button"
"Text" "Save Listing" "Text" "Save Listing"
@@ -77,16 +80,24 @@
}} }}
</form> </form>
<ul>
<li> <ul class="max-h-[50em] overflow-y-auto flex flex-col gap-[1em]">
Listing 1 {{- range $listing := .Accounts.ListMockListingsForShop $acctID $platform $shopID }}
</li> <li class="p-[1em] bg-card rounded-lg">
<li> <div>
Listing 2 Name: {{ $listing.Name }}
</li> </div>
<li> <div>
Listing 3 SKU: {{ $listing.SKU }}
</div>
<div>
Description: {{ $listing.Description }}
</div>
<div>
Count: {{ $listing.Count }}
</div>
</li> </li>
{{- end }}
</ul> </ul>
</div> </div>
</section> </section>