diff --git a/database_migrations/000018_add_mock_listing_counts.down.sql b/database_migrations/000018_add_mock_listing_counts.down.sql new file mode 100644 index 0000000..026d237 --- /dev/null +++ b/database_migrations/000018_add_mock_listing_counts.down.sql @@ -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; diff --git a/database_migrations/000018_add_mock_listing_counts.up.sql b/database_migrations/000018_add_mock_listing_counts.up.sql new file mode 100644 index 0000000..2538314 --- /dev/null +++ b/database_migrations/000018_add_mock_listing_counts.up.sql @@ -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; diff --git a/database_migrations/000019_add_mock_listing_sku_and_description.down.sql b/database_migrations/000019_add_mock_listing_sku_and_description.down.sql new file mode 100644 index 0000000..dbbb52c --- /dev/null +++ b/database_migrations/000019_add_mock_listing_sku_and_description.down.sql @@ -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; diff --git a/database_migrations/000019_add_mock_listing_sku_and_description.up.sql b/database_migrations/000019_add_mock_listing_sku_and_description.up.sql new file mode 100644 index 0000000..15399b9 --- /dev/null +++ b/database_migrations/000019_add_mock_listing_sku_and_description.up.sql @@ -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; diff --git a/diagrams/database_schema_mock.svg b/diagrams/database_schema_mock.svg index 567e7a9..ead62c9 100644 --- a/diagrams/database_schema_mock.svg +++ b/diagrams/database_schema_mock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/diagrams/database_schema_mock.uml b/diagrams/database_schema_mock.uml index 922b90f..eec866a 100644 --- a/diagrams/database_schema_mock.uml +++ b/diagrams/database_schema_mock.uml @@ -23,6 +23,9 @@ entity "**shop_amazon_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_amazon_sync_group_listings**" { @@ -48,6 +51,9 @@ entity "**shop_big_cartel_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_big_cartel_sync_group_listings**" { @@ -73,6 +79,9 @@ entity "**shop_ebay_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_ebay_sync_group_listings**" { @@ -98,6 +107,9 @@ entity "**shop_ecwid_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_ecwid_sync_group_listings**" { @@ -123,6 +135,9 @@ entity "**shop_etsy_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_etsy_sync_group_listings**" { @@ -148,6 +163,9 @@ entity "**shop_shopify_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_shopify_sync_group_listings**" { @@ -173,6 +191,9 @@ entity "**shop_square_online_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_square_online_sync_group_listings**" { @@ -198,6 +219,9 @@ entity "**shop_squarespace_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_squarespace_sync_group_listings**" { @@ -223,6 +247,9 @@ entity "**shop_tiktok_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_tiktok_sync_group_listings**" { @@ -248,6 +275,9 @@ entity "**shop_walmart_marketplace_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_walmart_marketplace_sync_group_listings**" { @@ -273,6 +303,9 @@ entity "**shop_wix_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_wix_sync_group_listings**" { @@ -298,6 +331,9 @@ entity "**shop_woo_commerce_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_woo_commerce_sync_group_listings**" { @@ -323,6 +359,9 @@ entity "**shop_zoho_listings**" { -- *""account_id"": //integer [FK]// *""name"": //text // + *""count"": //integer // + *""sku"": //text // + *""description"": //text // } entity "**shop_zoho_sync_group_listings**" { diff --git a/internal/domains/accounts/accounts.go b/internal/domains/accounts/accounts.go index 5cf3cdf..fe18bcf 100644 --- a/internal/domains/accounts/accounts.go +++ b/internal/domains/accounts/accounts.go @@ -10,7 +10,6 @@ import ( "ruben/inventory2/internal/consts" "ruben/inventory2/internal/logging" - "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) @@ -43,28 +42,6 @@ type ( Description string 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 { @@ -315,258 +292,6 @@ func (db *Store) GetListingsForShop(ctx context.Context, acctID int64, platform 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 func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) { diff --git a/internal/domains/accounts/ids.go b/internal/domains/accounts/ids.go new file mode 100644 index 0000000..745ea4e --- /dev/null +++ b/internal/domains/accounts/ids.go @@ -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, + } +} diff --git a/internal/domains/accounts/mocks.go b/internal/domains/accounts/mocks.go new file mode 100644 index 0000000..6da060e --- /dev/null +++ b/internal/domains/accounts/mocks.go @@ -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 + +} diff --git a/internal/domains/accounts/store_with_context.go b/internal/domains/accounts/store_with_context.go index 50b5d55..17d93ba 100644 --- a/internal/domains/accounts/store_with_context.go +++ b/internal/domains/accounts/store_with_context.go @@ -48,6 +48,10 @@ func (v_ctx *StoreWithContext) GetListingsForShop(acctID int64, platform Platfor 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) { 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) } -func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) { - return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID) +func (v_ctx *StoreWithContext) CreateMockListing(listing MockListing) (MockListing, error) { + 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) { diff --git a/internal/server/api/accounts/router.go b/internal/server/api/accounts/router.go index 4225d11..102e799 100644 --- a/internal/server/api/accounts/router.go +++ b/internal/server/api/accounts/router.go @@ -269,6 +269,8 @@ func (s *accountSubrouter) createMockShop(c *gin.Context) (response.Response, er // POST /:acctID/platforms/:platform/shops/mocks/:shop-id/listings func (s *accountSubrouter) addMockListing(c *gin.Context) (response.Response, error) { + acctID := auth.GetIdentity(c).Account.AccountID + var ( platform accounts.Platform shopID string @@ -287,11 +289,20 @@ func (s *accountSubrouter) addMockListing(c *gin.Context) (response.Response, er return nil, err } - s.log.Debugf("platform: %v", platform) - s.log.Debugf("shopID: %v", shopID) - s.log.Debugf("name: %v", name) - s.log.Debugf("sku: %v", sku) - s.log.Debugf("description: %v", description) + _, err = s.accts.CreateMockListing( + c, + accounts.MockListing{ + AccountShopListingIDs: accounts.NewAccountIDs(acctID). + 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 } diff --git a/styles/index.css b/styles/index.css index 65c9548..3c6b0eb 100644 --- a/styles/index.css +++ b/styles/index.css @@ -15,8 +15,6 @@ --text-lg--line-height: calc(1.75 / 1.125); --text-xl: 1.25rem; --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-6xl: 3.75rem; --font-weight-semibold: 600; @@ -291,6 +289,18 @@ .h-\[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-height: 100%; } @@ -333,12 +343,6 @@ .basis-full { 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 { animation: var(--animate-pulse); } @@ -351,9 +355,6 @@ .resize { resize: both; } - .list-disc { - list-style-type: disc; - } .list-none { list-style-type: none; } @@ -372,9 +373,6 @@ .grid-cols-\[max-content_max-content\] { grid-template-columns: max-content max-content; } - .grid-rows-\[auto\] { - grid-template-rows: auto; - } .grid-rows-\[auto_auto_auto\] { grid-template-rows: auto auto auto; } @@ -417,9 +415,15 @@ .overflow-x-hidden { overflow-x: hidden; } + .overflow-y-auto { + overflow-y: auto; + } .overflow-y-hidden { overflow-y: hidden; } + .overflow-y-scroll { + overflow-y: scroll; + } .rounded { border-radius: 0.25rem; } @@ -435,10 +439,6 @@ .rounded-sm { border-radius: calc(var(--radius) - 4px); } - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } .border-\[1px\] { border-style: var(--tw-border-style); border-width: 1px; @@ -455,9 +455,6 @@ --tw-border-style: solid; border-style: solid; } - .border-background { - border-color: var(--background); - } .border-foreground { border-color: var(--foreground); } @@ -467,9 +464,6 @@ .border-sidebar-border { border-color: var(--sidebar-border); } - .border-white { - border-color: var(--color-white); - } .bg-accent { background-color: var(--accent); } @@ -494,9 +488,6 @@ .py-\[0\.5em\] { padding-block: 0.5em; } - .py-\[1em\] { - padding-block: 1em; - } .pt-\[1em\] { padding-top: 1em; } @@ -518,13 +509,6 @@ .font-display { 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 { font-size: var(--text-lg); line-height: var(--tw-leading, var(--text-lg--line-height)); @@ -548,9 +532,6 @@ .text-nowrap { text-wrap: nowrap; } - .text-wrap { - text-wrap: wrap; - } .capitalize { text-transform: capitalize; } @@ -560,10 +541,6 @@ .underline { text-decoration-line: underline; } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } .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,); } @@ -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 { syntax: "*"; inherits: false; @@ -1168,11 +1125,6 @@ syntax: "*"; inherits: false; } -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} @property --tw-blur { syntax: "*"; inherits: false; @@ -1241,6 +1193,26 @@ inherits: false; 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 { syntax: "*"; initial-value: ""; @@ -1258,14 +1230,8 @@ @layer properties { @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 { - --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-font-weight: initial; - --tw-outline-style: solid; --tw-blur: initial; --tw-brightness: initial; --tw-contrast: initial; @@ -1282,6 +1248,11 @@ --tw-scale-x: 1; --tw-scale-y: 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-leading: initial; } diff --git a/templates/pages/accounts/{acctID.int64}/platforms/{platform}/shops/mocks/{shopID}/index.html.tmpl b/templates/pages/accounts/{acctID.int64}/platforms/{platform}/shops/mocks/{shopID}/index.html.tmpl index ef2242a..8c4721a 100644 --- a/templates/pages/accounts/{acctID.int64}/platforms/{platform}/shops/mocks/{shopID}/index.html.tmpl +++ b/templates/pages/accounts/{acctID.int64}/platforms/{platform}/shops/mocks/{shopID}/index.html.tmpl @@ -27,7 +27,7 @@ -