diff --git a/README.md b/README.md
index ab232f3..b2a2be7 100644
--- a/README.md
+++ b/README.md
@@ -94,9 +94,13 @@ All events (or commands) will be stored in a respective event series, and all da
# Architectural and Software Diagrams
-## Database schema
+## Database schemas
-
+**public**
+
+
+**mock**
+
## Events
diff --git a/database_migrations/000015_mock_accounts.down.sql b/database_migrations/000015_mock_accounts.down.sql
index c7debda..dabc12b 100644
--- a/database_migrations/000015_mock_accounts.down.sql
+++ b/database_migrations/000015_mock_accounts.down.sql
@@ -1,31 +1,99 @@
BEGIN;
+
+-- mock ebay sync group listings
+
+DROP TABLE mock.shop_ebay_sync_group_listings;
+DROP TABLE mock.shop_ebay_listings;
+
+-- mock walmart_marketplace sync group listings
+
+DROP TABLE mock.shop_walmart_marketplace_sync_group_listings;
+DROP TABLE mock.shop_walmart_marketplace_listings;
+
+-- mock amazon sync group listings
+
+DROP TABLE mock.shop_amazon_sync_group_listings;
+DROP TABLE mock.shop_amazon_listings;
+
+-- mock big_cartel sync group listings
+
+DROP TABLE mock.shop_big_cartel_sync_group_listings;
+DROP TABLE mock.shop_big_cartel_listings;
+
+-- mock ecwid sync group listings
+
+DROP TABLE mock.shop_ecwid_sync_group_listings;
+DROP TABLE mock.shop_ecwid_listings;
+
+-- mock zoho sync group listings
+
+DROP TABLE mock.shop_zoho_sync_group_listings;
+DROP TABLE mock.shop_zoho_listings;
+
+-- mock square_online sync group listings
+
+DROP TABLE mock.shop_square_online_sync_group_listings;
+DROP TABLE mock.shop_square_online_listings;
+
+-- mock squarespace sync group listings
+
+DROP TABLE mock.shop_squarespace_sync_group_listings;
+DROP TABLE mock.shop_squarespace_listings;
+
+-- mock wix sync group listings
+
+DROP TABLE mock.shop_wix_sync_group_listings;
+DROP TABLE mock.shop_wix_listings;
+
+-- mock woo_commerce sync group listings
+
+DROP TABLE mock.shop_woo_commerce_sync_group_listings;
+DROP TABLE mock.shop_woo_commerce_listings;
+
+-- mock shopify sync group listings
+
+DROP TABLE mock.shop_shopify_sync_group_listings;
+DROP TABLE mock.shop_shopify_listings;
+
+-- mock tiktok sync group listings
+
+DROP TABLE mock.shop_tiktok_sync_group_listings;
+DROP TABLE mock.shop_tiktok_listings;
+
+
-- mock sync group listings
-DROP TABLE mock_sync_group_listings;
-DROP TABLE mock_sync_groups;
+DROP TABLE mock.sync_group_listing_order_indexes;
+DROP TABLE mock.sync_groups;
--- mock stores
+-- mock shops
-DROP TABLE mock_store_ebay;
-DROP TABLE mock_store_walmart_marketplace;
-DROP TABLE mock_store_amazon;
-DROP TABLE mock_store_big_cartel;
-DROP TABLE mock_store_ecwid;
-DROP TABLE mock_store_zoho;
-DROP TABLE mock_store_square_online;
-DROP TABLE mock_store_squarespace;
-DROP TABLE mock_store_wix;
-DROP TABLE mock_store_woo_commerce;
-DROP TABLE mock_store_shopify;
-DROP TABLE mock_store_tiktok;
+DROP TABLE mock.shop_ebay;
+DROP TABLE mock.shop_walmart_marketplace;
+DROP TABLE mock.shop_amazon;
+DROP TABLE mock.shop_big_cartel;
+DROP TABLE mock.shop_ecwid;
+DROP TABLE mock.shop_zoho;
+DROP TABLE mock.shop_square_online;
+DROP TABLE mock.shop_squarespace;
+DROP TABLE mock.shop_wix;
+DROP TABLE mock.shop_woo_commerce;
+DROP TABLE mock.shop_shopify;
+DROP TABLE mock.shop_tiktok;
-- mock accounts
-DROP TABLE mock_accounts;
+DROP TABLE mock.accounts;
+
+
+-- new mock schema
+
+DROP SCHEMA mock;
+
COMMIT;
diff --git a/database_migrations/000015_mock_accounts.up.sql b/database_migrations/000015_mock_accounts.up.sql
index 3294473..6e4e126 100644
--- a/database_migrations/000015_mock_accounts.up.sql
+++ b/database_migrations/000015_mock_accounts.up.sql
@@ -1,143 +1,476 @@
BEGIN;
+-- new mock schema
+
+CREATE SCHEMA mock;
+
+
-- mock accounts
-CREATE TABLE mock_accounts (
+CREATE TABLE mock.accounts (
account_id SERIAL PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES oauth_users (user_id)
);
-CREATE UNIQUE INDEX ON mock_accounts (user_id);
-CREATE UNIQUE INDEX ON mock_accounts (account_id, user_id);
+CREATE UNIQUE INDEX ON mock.accounts (user_id);
+CREATE UNIQUE INDEX ON mock.accounts (account_id, user_id);
--- mock stores
+-- mock shops
-CREATE TABLE mock_store_tiktok (
+CREATE TABLE mock.shop_tiktok (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_shopify (
+CREATE TABLE mock.shop_shopify (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_woo_commerce (
+CREATE TABLE mock.shop_woo_commerce (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_wix (
+CREATE TABLE mock.shop_wix (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_squarespace (
+CREATE TABLE mock.shop_squarespace (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_square_online (
+CREATE TABLE mock.shop_square_online (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_zoho (
+CREATE TABLE mock.shop_zoho (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_ecwid (
+CREATE TABLE mock.shop_ecwid (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_big_cartel (
+CREATE TABLE mock.shop_big_cartel (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_amazon (
+CREATE TABLE mock.shop_amazon (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_walmart_marketplace (
+CREATE TABLE mock.shop_walmart_marketplace (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-CREATE TABLE mock_store_ebay (
+CREATE TABLE mock.shop_ebay (
user_id TEXT NOT NULL REFERENCES oauth_users (user_id),
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id),
- store_id TEXT PRIMARY KEY,
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
name TEXT NOT NULL CHECK (char_length(name) > 0),
- FOREIGN KEY (account_id, user_id) REFERENCES mock_accounts (account_id, user_id)
+ PRIMARY KEY (account_id, shop_id),
+ FOREIGN KEY (account_id, user_id) REFERENCES mock.accounts (account_id, user_id)
);
-- mock sync group listings
-CREATE TABLE mock_sync_groups (
+CREATE TABLE mock.sync_groups (
sync_group_id SERIAL PRIMARY KEY,
- account_id INTEGER NOT NULL REFERENCES mock_accounts (account_id)
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ name TEXT NOT NULL CHECK (char_length(name) > 0)
);
-CREATE TABLE mock_sync_group_listings (
- sync_group_id INTEGER NOT NULL REFERENCES mock_sync_groups (sync_group_id),
+CREATE TABLE mock.sync_group_listing_order_indexes (
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
order_index INTEGER NOT NULL,
- platform platform NOT NULL,
+
+ UNIQUE (sync_group_id, order_index)
+);
+
+
+-- mock tiktok sync group listings
+
+CREATE TABLE mock.shop_tiktok_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
shop_id TEXT NOT NULL,
listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
- PRIMARY KEY (sync_group_id, order_index)
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_tiktok (account_id, shop_id)
);
+CREATE TABLE mock.shop_tiktok_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_tiktok (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_tiktok_listings (shop_id, listing_id)
+);
+
+-- mock shopify sync group listings
+
+CREATE TABLE mock.shop_shopify_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_shopify (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_shopify_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_shopify (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_shopify_listings (shop_id, listing_id)
+);
+
+-- mock woo_commerce sync group listings
+
+CREATE TABLE mock.shop_woo_commerce_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_woo_commerce (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_woo_commerce_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_woo_commerce (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_woo_commerce_listings (shop_id, listing_id)
+);
+
+-- mock wix sync group listings
+
+CREATE TABLE mock.shop_wix_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_wix (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_wix_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_wix (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_wix_listings (shop_id, listing_id)
+);
+
+-- mock squarespace sync group listings
+
+CREATE TABLE mock.shop_squarespace_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_squarespace (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_squarespace_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_squarespace (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_squarespace_listings (shop_id, listing_id)
+);
+
+-- mock square_online sync group listings
+
+CREATE TABLE mock.shop_square_online_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_square_online (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_square_online_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_square_online (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_square_online_listings (shop_id, listing_id)
+);
+
+-- mock zoho sync group listings
+
+CREATE TABLE mock.shop_zoho_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_zoho (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_zoho_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_zoho (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_zoho_listings (shop_id, listing_id)
+);
+
+-- mock ecwid sync group listings
+
+CREATE TABLE mock.shop_ecwid_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_ecwid (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_ecwid_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_ecwid (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_ecwid_listings (shop_id, listing_id)
+);
+
+-- mock big_cartel sync group listings
+
+CREATE TABLE mock.shop_big_cartel_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_big_cartel (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_big_cartel_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_big_cartel (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_big_cartel_listings (shop_id, listing_id)
+);
+
+-- mock amazon sync group listings
+
+CREATE TABLE mock.shop_amazon_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_amazon (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_amazon_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_amazon (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_amazon_listings (shop_id, listing_id)
+);
+
+-- mock walmart_marketplace sync group listings
+
+CREATE TABLE mock.shop_walmart_marketplace_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_walmart_marketplace (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_walmart_marketplace_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_walmart_marketplace (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_walmart_marketplace_listings (shop_id, listing_id)
+);
+
+-- mock ebay sync group listings
+
+CREATE TABLE mock.shop_ebay_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ name TEXT NOT NULL,
+
+ PRIMARY KEY (shop_id, listing_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_ebay (account_id, shop_id)
+);
+
+CREATE TABLE mock.shop_ebay_sync_group_listings (
+ account_id INTEGER NOT NULL REFERENCES mock.accounts (account_id),
+ sync_group_id INTEGER NOT NULL REFERENCES mock.sync_groups (sync_group_id),
+ shop_id TEXT NOT NULL,
+ listing_id TEXT NOT NULL,
+ order_index INTEGER NOT NULL,
+
+ -- allow each shop to have at most ONE listing per sync group
+ UNIQUE (sync_group_id, shop_id),
+ FOREIGN KEY (account_id, shop_id) REFERENCES mock.shop_ebay (account_id, shop_id),
+ FOREIGN KEY (sync_group_id, order_index) REFERENCES mock.sync_group_listing_order_indexes (sync_group_id, order_index),
+ FOREIGN KEY (shop_id, listing_id) REFERENCES mock.shop_ebay_listings (shop_id, listing_id)
+);
+
+
+
+GRANT ALL ON SCHEMA mock TO PUBLIC;
+
+
COMMIT;
diff --git a/diagrams/database_schema.svg b/diagrams/database_schema.svg
deleted file mode 100644
index c9230ff..0000000
--- a/diagrams/database_schema.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/diagrams/database_schema.uml b/diagrams/database_schema.uml
deleted file mode 100644
index e85907b..0000000
--- a/diagrams/database_schema.uml
+++ /dev/null
@@ -1,466 +0,0 @@
-@startuml
-hide circle
-skinparam linetype ortho
-
-entity "**accounts**" {
- + ""account_id"": //serial [PK]//
- --
- *""email"": //text //
- *""verified"": //boolean //
- *""user_id"": //text [FK]//
-}
-
-entity "**etsy_access_tokens**" {
- + ""user_id"": //integer [PK][FK]//
- --
- *""access_token"": //text //
- *""refresh_token"": //text //
- *""access_token_expiration"": //timestamp with time zone //
- *""refresh_token_expiration"": //timestamp with time zone //
-}
-
-entity "**etsy_oauth_requests**" {
- + ""state"": //bytea [PK]//
- --
- *""code_verifier"": //bytea //
- *""expiration"": //timestamp with time zone //
- *""account_id"": //integer [FK]//
-}
-
-entity "**etsy_store_events**" {
- + ""store_id"": //text [PK][FK]//
- + ""event_timestamp"": //timestamp with time zone [PK][FK]//
- + ""event_id"": //text [PK][FK]//
- --
- *""platform"": //text [FK]//
-}
-
-entity "**etsy_users**" {
- + ""user_id"": //integer [PK]//
- --
- *""account_id"": //integer [FK]//
- *""shop_id"": //integer //
-}
-
-entity "**mock_accounts**" {
- + ""account_id"": //serial [PK]//
- --
- *""user_id"": //text [FK]//
-}
-
-entity "**mock_store_amazon**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_big_cartel**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_ebay**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_ecwid**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_shopify**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_square_online**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_squarespace**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_tiktok**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_walmart_marketplace**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_wix**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_woo_commerce**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_store_zoho**" {
- + ""store_id"": //text [PK]//
- --
- *""user_id"": //text [FK]//
- *""account_id"": //integer [FK]//
- *""name"": //text //
-}
-
-entity "**mock_sync_group_listings**" {
- + ""sync_group_id"": //integer [PK][FK]//
- + ""order_index"": //integer [PK]//
- --
- *""platform"": //platform //
- *""shop_id"": //text //
- *""listing_id"": //text //
-}
-
-entity "**mock_sync_groups**" {
- + ""sync_group_id"": //serial [PK]//
- --
- *""account_id"": //integer [FK]//
-}
-
-entity "**oauth_login_states**" {
- + ""state"": //bytea [PK]//
- --
- *""expiration"": //timestamp with time zone //
- *""target_uri"": //text //
-}
-
-entity "**oauth_tokens**" {
- + ""access_token"": //text [PK]//
- --
- *""token_type"": //text //
- *""refresh_token"": //text //
- *""expiry"": //timestamp with time zone //
- *""id_token_issuer"": //text //
- *""id_token_audience"": //text[] //
- *""id_token_subject"": //text [FK]//
- *""id_token_expiry"": //timestamp with time zone //
- *""id_token_issued_at"": //timestamp with time zone //
- *""id_token_nonce"": //text //
- *""id_token_access_token_hash"": //text //
- *""claims"": //jsonb //
-}
-
-entity "**oauth_users**" {
- + ""user_id"": //text [PK]//
- --
-}
-
-entity "**raw_store_events**" {
- + ""platform"": //text [PK]//
- + ""store_id"": //text [PK]//
- + ""event_timestamp"": //timestamp with time zone [PK]//
- + ""event_id"": //text [PK]//
- --
- *""raw_payload"": //jsonb //
-}
-
-entity "**schema_migrations**" {
- + ""version"": //bigint [PK]//
- --
- *""dirty"": //boolean //
-}
-
-entity "**sync_group_listing_drafts**" {
- + ""account_id"": //integer [PK][FK]//
- + ""order_index"": //integer [PK]//
- --
- ""platform"": //platform //
- ""shop_id"": //text //
- ""listing_id"": //text //
-}
-
-entity "**sync_group_listings**" {
- + ""sync_group_id"": //integer [PK][FK]//
- + ""order_index"": //integer [PK]//
- --
- *""platform"": //platform //
- *""shop_id"": //text //
- *""listing_id"": //text //
-}
-
-entity "**sync_groups**" {
- + ""sync_group_id"": //serial [PK]//
- --
- *""account_id"": //integer [FK]//
-}
-
-entity "**tiktok_store_events**" {
- + ""store_id"": //text [PK][FK]//
- + ""event_timestamp"": //timestamp with time zone [PK][FK]//
- + ""event_id"": //text [PK][FK]//
- --
- *""platform"": //text [FK]//
-}
-
-entity "**wix_store_events**" {
- + ""store_id"": //text [PK][FK]//
- + ""event_timestamp"": //timestamp with time zone [PK][FK]//
- + ""event_id"": //text [PK][FK]//
- --
- *""platform"": //text [FK]//
-}
-
-"**accounts**" }-- "**oauth_users**"
-
-"**etsy_access_tokens**" ||-|| "**etsy_users**"
-
-"**etsy_oauth_requests**" }-- "**accounts**"
-
-"**etsy_store_events**" }-- "**raw_store_events**"
-
-"**etsy_store_events**" }-- "**raw_store_events**"
-
-"**etsy_store_events**" }-- "**raw_store_events**"
-
-"**etsy_store_events**" }-- "**raw_store_events**"
-
-"**etsy_users**" }-- "**accounts**"
-
-"**mock_accounts**" }-- "**oauth_users**"
-
-"**mock_accounts**" }-- "**oauth_users**"
-
-"**mock_store_amazon**" }-- "**mock_accounts**"
-
-"**mock_store_amazon**" }-- "**mock_accounts**"
-
-"**mock_store_amazon**" }-- "**mock_accounts**"
-
-"**mock_store_amazon**" }-- "**mock_accounts**"
-
-"**mock_store_amazon**" }-- "**mock_accounts**"
-
-"**mock_store_amazon**" }-- "**mock_accounts**"
-
-"**mock_store_amazon**" }-- "**oauth_users**"
-
-"**mock_store_big_cartel**" }-- "**mock_accounts**"
-
-"**mock_store_big_cartel**" }-- "**mock_accounts**"
-
-"**mock_store_big_cartel**" }-- "**mock_accounts**"
-
-"**mock_store_big_cartel**" }-- "**mock_accounts**"
-
-"**mock_store_big_cartel**" }-- "**mock_accounts**"
-
-"**mock_store_big_cartel**" }-- "**mock_accounts**"
-
-"**mock_store_big_cartel**" }-- "**oauth_users**"
-
-"**mock_store_ebay**" }-- "**mock_accounts**"
-
-"**mock_store_ebay**" }-- "**mock_accounts**"
-
-"**mock_store_ebay**" }-- "**mock_accounts**"
-
-"**mock_store_ebay**" }-- "**mock_accounts**"
-
-"**mock_store_ebay**" }-- "**mock_accounts**"
-
-"**mock_store_ebay**" }-- "**mock_accounts**"
-
-"**mock_store_ebay**" }-- "**oauth_users**"
-
-"**mock_store_ecwid**" }-- "**mock_accounts**"
-
-"**mock_store_ecwid**" }-- "**mock_accounts**"
-
-"**mock_store_ecwid**" }-- "**mock_accounts**"
-
-"**mock_store_ecwid**" }-- "**mock_accounts**"
-
-"**mock_store_ecwid**" }-- "**mock_accounts**"
-
-"**mock_store_ecwid**" }-- "**mock_accounts**"
-
-"**mock_store_ecwid**" }-- "**oauth_users**"
-
-"**mock_store_shopify**" }-- "**mock_accounts**"
-
-"**mock_store_shopify**" }-- "**mock_accounts**"
-
-"**mock_store_shopify**" }-- "**mock_accounts**"
-
-"**mock_store_shopify**" }-- "**mock_accounts**"
-
-"**mock_store_shopify**" }-- "**mock_accounts**"
-
-"**mock_store_shopify**" }-- "**mock_accounts**"
-
-"**mock_store_shopify**" }-- "**oauth_users**"
-
-"**mock_store_square_online**" }-- "**mock_accounts**"
-
-"**mock_store_square_online**" }-- "**mock_accounts**"
-
-"**mock_store_square_online**" }-- "**mock_accounts**"
-
-"**mock_store_square_online**" }-- "**mock_accounts**"
-
-"**mock_store_square_online**" }-- "**mock_accounts**"
-
-"**mock_store_square_online**" }-- "**mock_accounts**"
-
-"**mock_store_square_online**" }-- "**oauth_users**"
-
-"**mock_store_squarespace**" }-- "**mock_accounts**"
-
-"**mock_store_squarespace**" }-- "**mock_accounts**"
-
-"**mock_store_squarespace**" }-- "**mock_accounts**"
-
-"**mock_store_squarespace**" }-- "**mock_accounts**"
-
-"**mock_store_squarespace**" }-- "**mock_accounts**"
-
-"**mock_store_squarespace**" }-- "**mock_accounts**"
-
-"**mock_store_squarespace**" }-- "**oauth_users**"
-
-"**mock_store_tiktok**" }-- "**mock_accounts**"
-
-"**mock_store_tiktok**" }-- "**mock_accounts**"
-
-"**mock_store_tiktok**" }-- "**mock_accounts**"
-
-"**mock_store_tiktok**" }-- "**mock_accounts**"
-
-"**mock_store_tiktok**" }-- "**mock_accounts**"
-
-"**mock_store_tiktok**" }-- "**mock_accounts**"
-
-"**mock_store_tiktok**" }-- "**oauth_users**"
-
-"**mock_store_walmart_marketplace**" }-- "**mock_accounts**"
-
-"**mock_store_walmart_marketplace**" }-- "**mock_accounts**"
-
-"**mock_store_walmart_marketplace**" }-- "**mock_accounts**"
-
-"**mock_store_walmart_marketplace**" }-- "**mock_accounts**"
-
-"**mock_store_walmart_marketplace**" }-- "**mock_accounts**"
-
-"**mock_store_walmart_marketplace**" }-- "**mock_accounts**"
-
-"**mock_store_walmart_marketplace**" }-- "**oauth_users**"
-
-"**mock_store_wix**" }-- "**mock_accounts**"
-
-"**mock_store_wix**" }-- "**mock_accounts**"
-
-"**mock_store_wix**" }-- "**mock_accounts**"
-
-"**mock_store_wix**" }-- "**mock_accounts**"
-
-"**mock_store_wix**" }-- "**mock_accounts**"
-
-"**mock_store_wix**" }-- "**mock_accounts**"
-
-"**mock_store_wix**" }-- "**oauth_users**"
-
-"**mock_store_woo_commerce**" }-- "**mock_accounts**"
-
-"**mock_store_woo_commerce**" }-- "**mock_accounts**"
-
-"**mock_store_woo_commerce**" }-- "**mock_accounts**"
-
-"**mock_store_woo_commerce**" }-- "**mock_accounts**"
-
-"**mock_store_woo_commerce**" }-- "**mock_accounts**"
-
-"**mock_store_woo_commerce**" }-- "**mock_accounts**"
-
-"**mock_store_woo_commerce**" }-- "**oauth_users**"
-
-"**mock_store_zoho**" }-- "**mock_accounts**"
-
-"**mock_store_zoho**" }-- "**mock_accounts**"
-
-"**mock_store_zoho**" }-- "**mock_accounts**"
-
-"**mock_store_zoho**" }-- "**mock_accounts**"
-
-"**mock_store_zoho**" }-- "**mock_accounts**"
-
-"**mock_store_zoho**" }-- "**mock_accounts**"
-
-"**mock_store_zoho**" }-- "**oauth_users**"
-
-"**mock_sync_group_listings**" }-- "**mock_sync_groups**"
-
-"**mock_sync_groups**" }-- "**mock_accounts**"
-
-"**mock_sync_groups**" }-- "**mock_accounts**"
-
-"**oauth_tokens**" }-- "**oauth_users**"
-
-"**sync_group_listing_drafts**" }-- "**accounts**"
-
-"**sync_group_listing_drafts**" }-- "**accounts**"
-
-"**sync_group_listings**" }-- "**sync_groups**"
-
-"**sync_group_listings**" }-- "**sync_groups**"
-
-"**sync_groups**" }-- "**accounts**"
-
-"**tiktok_store_events**" }-- "**raw_store_events**"
-
-"**tiktok_store_events**" }-- "**raw_store_events**"
-
-"**tiktok_store_events**" }-- "**raw_store_events**"
-
-"**tiktok_store_events**" }-- "**raw_store_events**"
-
-"**wix_store_events**" }-- "**raw_store_events**"
-
-"**wix_store_events**" }-- "**raw_store_events**"
-
-"**wix_store_events**" }-- "**raw_store_events**"
-
-"**wix_store_events**" }-- "**raw_store_events**"
-@enduml
diff --git a/diagrams/database_schema_mock.svg b/diagrams/database_schema_mock.svg
new file mode 100644
index 0000000..aede540
--- /dev/null
+++ b/diagrams/database_schema_mock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/diagrams/database_schema_mock.uml b/diagrams/database_schema_mock.uml
new file mode 100644
index 0000000..d94b0d3
--- /dev/null
+++ b/diagrams/database_schema_mock.uml
@@ -0,0 +1,570 @@
+@startuml
+hide circle
+skinparam linetype ortho
+set separator none
+
+entity "**accounts**" {
+ + ""account_id"": //serial [PK]//
+ --
+ *""user_id"": //text [FK]//
+}
+
+entity "**shop_amazon**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_amazon_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_amazon_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_big_cartel**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_big_cartel_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_big_cartel_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_ebay**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_ebay_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_ebay_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_ecwid**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_ecwid_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_ecwid_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_shopify**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_shopify_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_shopify_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_square_online**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_square_online_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_square_online_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_squarespace**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_squarespace_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_squarespace_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_tiktok**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_tiktok_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_tiktok_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_walmart_marketplace**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_walmart_marketplace_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_walmart_marketplace_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_wix**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_wix_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_wix_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_woo_commerce**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_woo_commerce_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_woo_commerce_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**shop_zoho**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""shop_id"": //text [PK]//
+ --
+ *""user_id"": //text [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_zoho_listings**" {
+ + ""shop_id"": //text [PK]//
+ + ""listing_id"": //text [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+entity "**shop_zoho_sync_group_listings**" {
+ --
+ *""account_id"": //integer [FK]//
+ *""sync_group_id"": //integer [FK]//
+ *""shop_id"": //text [FK]//
+ *""listing_id"": //text //
+ *""order_index"": //integer //
+}
+
+entity "**sync_group_listing_order_indexes**" {
+ --
+ *""sync_group_id"": //integer [FK]//
+ *""order_index"": //integer //
+}
+
+entity "**sync_groups**" {
+ + ""sync_group_id"": //serial [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""name"": //text //
+}
+
+"**accounts**" }-- "**public.oauth_users**"
+
+"**shop_amazon**" }-- "**accounts**"
+
+"**shop_amazon**" }-- "**accounts**"
+
+"**shop_amazon**" }-- "**public.oauth_users**"
+
+"**shop_amazon_listings**" }-- "**accounts**"
+
+"**shop_amazon_listings**" }-- "**shop_amazon**"
+
+"**shop_amazon_sync_group_listings**" }-- "**accounts**"
+
+"**shop_amazon_sync_group_listings**" }-- "**shop_amazon**"
+
+"**shop_amazon_sync_group_listings**" }-- "**shop_amazon_listings**"
+
+"**shop_amazon_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_amazon_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_big_cartel**" }-- "**accounts**"
+
+"**shop_big_cartel**" }-- "**accounts**"
+
+"**shop_big_cartel**" }-- "**public.oauth_users**"
+
+"**shop_big_cartel_listings**" }-- "**accounts**"
+
+"**shop_big_cartel_listings**" }-- "**shop_big_cartel**"
+
+"**shop_big_cartel_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_big_cartel_sync_group_listings**" }-- "**accounts**"
+
+"**shop_big_cartel_sync_group_listings**" }-- "**shop_big_cartel**"
+
+"**shop_big_cartel_sync_group_listings**" }-- "**shop_big_cartel_listings**"
+
+"**shop_big_cartel_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_ebay**" }-- "**accounts**"
+
+"**shop_ebay**" }-- "**accounts**"
+
+"**shop_ebay**" }-- "**public.oauth_users**"
+
+"**shop_ebay_listings**" }-- "**accounts**"
+
+"**shop_ebay_listings**" }-- "**shop_ebay**"
+
+"**shop_ebay_sync_group_listings**" }-- "**accounts**"
+
+"**shop_ebay_sync_group_listings**" }-- "**shop_ebay**"
+
+"**shop_ebay_sync_group_listings**" }-- "**shop_ebay_listings**"
+
+"**shop_ebay_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_ebay_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_ecwid**" }-- "**accounts**"
+
+"**shop_ecwid**" }-- "**accounts**"
+
+"**shop_ecwid**" }-- "**public.oauth_users**"
+
+"**shop_ecwid_listings**" }-- "**accounts**"
+
+"**shop_ecwid_listings**" }-- "**shop_ecwid**"
+
+"**shop_ecwid_sync_group_listings**" }-- "**accounts**"
+
+"**shop_ecwid_sync_group_listings**" }-- "**shop_ecwid**"
+
+"**shop_ecwid_sync_group_listings**" }-- "**shop_ecwid_listings**"
+
+"**shop_ecwid_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_ecwid_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_shopify**" }-- "**accounts**"
+
+"**shop_shopify**" }-- "**accounts**"
+
+"**shop_shopify**" }-- "**public.oauth_users**"
+
+"**shop_shopify_listings**" }-- "**accounts**"
+
+"**shop_shopify_listings**" }-- "**shop_shopify**"
+
+"**shop_shopify_sync_group_listings**" }-- "**accounts**"
+
+"**shop_shopify_sync_group_listings**" }-- "**shop_shopify**"
+
+"**shop_shopify_sync_group_listings**" }-- "**shop_shopify_listings**"
+
+"**shop_shopify_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_shopify_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_square_online**" }-- "**accounts**"
+
+"**shop_square_online**" }-- "**accounts**"
+
+"**shop_square_online**" }-- "**public.oauth_users**"
+
+"**shop_square_online_listings**" }-- "**accounts**"
+
+"**shop_square_online_listings**" }-- "**shop_square_online**"
+
+"**shop_square_online_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_square_online_sync_group_listings**" }-- "**accounts**"
+
+"**shop_square_online_sync_group_listings**" }-- "**shop_square_online**"
+
+"**shop_square_online_sync_group_listings**" }-- "**shop_square_online_listings**"
+
+"**shop_square_online_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_squarespace**" }-- "**accounts**"
+
+"**shop_squarespace**" }-- "**accounts**"
+
+"**shop_squarespace**" }-- "**public.oauth_users**"
+
+"**shop_squarespace_listings**" }-- "**accounts**"
+
+"**shop_squarespace_listings**" }-- "**shop_squarespace**"
+
+"**shop_squarespace_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_squarespace_sync_group_listings**" }-- "**accounts**"
+
+"**shop_squarespace_sync_group_listings**" }-- "**shop_squarespace**"
+
+"**shop_squarespace_sync_group_listings**" }-- "**shop_squarespace_listings**"
+
+"**shop_squarespace_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_tiktok**" }-- "**accounts**"
+
+"**shop_tiktok**" }-- "**accounts**"
+
+"**shop_tiktok**" }-- "**public.oauth_users**"
+
+"**shop_tiktok_listings**" }-- "**accounts**"
+
+"**shop_tiktok_listings**" }-- "**shop_tiktok**"
+
+"**shop_tiktok_sync_group_listings**" }-- "**accounts**"
+
+"**shop_tiktok_sync_group_listings**" }-- "**shop_tiktok**"
+
+"**shop_tiktok_sync_group_listings**" }-- "**shop_tiktok_listings**"
+
+"**shop_tiktok_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_tiktok_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_walmart_marketplace**" }-- "**accounts**"
+
+"**shop_walmart_marketplace**" }-- "**accounts**"
+
+"**shop_walmart_marketplace**" }-- "**public.oauth_users**"
+
+"**shop_walmart_marketplace_listings**" }-- "**accounts**"
+
+"**shop_walmart_marketplace_listings**" }-- "**shop_walmart_marketplace**"
+
+"**shop_walmart_marketplace_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_walmart_marketplace_sync_group_listings**" }-- "**shop_walmart_marketplace**"
+
+"**shop_walmart_marketplace_sync_group_listings**" }-- "**shop_walmart_marketplace_listings**"
+
+"**shop_walmart_marketplace_sync_group_listings**" }-- "**accounts**"
+
+"**shop_walmart_marketplace_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_wix**" }-- "**accounts**"
+
+"**shop_wix**" }-- "**accounts**"
+
+"**shop_wix**" }-- "**public.oauth_users**"
+
+"**shop_wix_listings**" }-- "**accounts**"
+
+"**shop_wix_listings**" }-- "**shop_wix**"
+
+"**shop_wix_sync_group_listings**" }-- "**accounts**"
+
+"**shop_wix_sync_group_listings**" }-- "**shop_wix**"
+
+"**shop_wix_sync_group_listings**" }-- "**shop_wix_listings**"
+
+"**shop_wix_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_wix_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_woo_commerce**" }-- "**accounts**"
+
+"**shop_woo_commerce**" }-- "**accounts**"
+
+"**shop_woo_commerce**" }-- "**public.oauth_users**"
+
+"**shop_woo_commerce_listings**" }-- "**accounts**"
+
+"**shop_woo_commerce_listings**" }-- "**shop_woo_commerce**"
+
+"**shop_woo_commerce_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**shop_woo_commerce_sync_group_listings**" }-- "**accounts**"
+
+"**shop_woo_commerce_sync_group_listings**" }-- "**shop_woo_commerce**"
+
+"**shop_woo_commerce_sync_group_listings**" }-- "**shop_woo_commerce_listings**"
+
+"**shop_woo_commerce_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_zoho**" }-- "**accounts**"
+
+"**shop_zoho**" }-- "**accounts**"
+
+"**shop_zoho**" }-- "**public.oauth_users**"
+
+"**shop_zoho_listings**" }-- "**accounts**"
+
+"**shop_zoho_listings**" }-- "**shop_zoho**"
+
+"**shop_zoho_sync_group_listings**" }-- "**accounts**"
+
+"**shop_zoho_sync_group_listings**" }-- "**shop_zoho**"
+
+"**shop_zoho_sync_group_listings**" }-- "**shop_zoho_listings**"
+
+"**shop_zoho_sync_group_listings**" }-- "**sync_groups**"
+
+"**shop_zoho_sync_group_listings**" }-- "**sync_group_listing_order_indexes**"
+
+"**sync_group_listing_order_indexes**" }-- "**sync_groups**"
+
+"**sync_groups**" }-- "**accounts**"
+@enduml
diff --git a/diagrams/database_schema_public.svg b/diagrams/database_schema_public.svg
new file mode 100644
index 0000000..edee36a
--- /dev/null
+++ b/diagrams/database_schema_public.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/diagrams/database_schema_public.uml b/diagrams/database_schema_public.uml
new file mode 100644
index 0000000..12a35fc
--- /dev/null
+++ b/diagrams/database_schema_public.uml
@@ -0,0 +1,150 @@
+@startuml
+hide circle
+skinparam linetype ortho
+set separator none
+
+entity "**accounts**" {
+ + ""account_id"": //serial [PK]//
+ --
+ *""email"": //text //
+ *""verified"": //boolean //
+ *""user_id"": //text [FK]//
+}
+
+entity "**etsy_access_tokens**" {
+ + ""user_id"": //integer [PK][FK]//
+ --
+ *""access_token"": //text //
+ *""refresh_token"": //text //
+ *""access_token_expiration"": //timestamp with time zone //
+ *""refresh_token_expiration"": //timestamp with time zone //
+}
+
+entity "**etsy_oauth_requests**" {
+ + ""state"": //bytea [PK]//
+ --
+ *""code_verifier"": //bytea //
+ *""expiration"": //timestamp with time zone //
+ *""account_id"": //integer [FK]//
+}
+
+entity "**etsy_store_events**" {
+ + ""store_id"": //text [PK]//
+ + ""event_timestamp"": //timestamp with time zone [PK]//
+ + ""event_id"": //text [PK]//
+ --
+ *""platform"": //text [FK]//
+}
+
+entity "**etsy_users**" {
+ + ""user_id"": //integer [PK]//
+ --
+ *""account_id"": //integer [FK]//
+ *""shop_id"": //integer //
+}
+
+entity "**oauth_login_states**" {
+ + ""state"": //bytea [PK]//
+ --
+ *""expiration"": //timestamp with time zone //
+ *""target_uri"": //text //
+}
+
+entity "**oauth_tokens**" {
+ + ""access_token"": //text [PK]//
+ --
+ *""token_type"": //text //
+ *""refresh_token"": //text //
+ *""expiry"": //timestamp with time zone //
+ *""id_token_issuer"": //text //
+ *""id_token_audience"": //text[] //
+ *""id_token_subject"": //text [FK]//
+ *""id_token_expiry"": //timestamp with time zone //
+ *""id_token_issued_at"": //timestamp with time zone //
+ *""id_token_nonce"": //text //
+ *""id_token_access_token_hash"": //text //
+ *""claims"": //jsonb //
+}
+
+entity "**oauth_users**" {
+ + ""user_id"": //text [PK]//
+ --
+}
+
+entity "**raw_store_events**" {
+ + ""platform"": //text [PK]//
+ + ""store_id"": //text [PK]//
+ + ""event_timestamp"": //timestamp with time zone [PK]//
+ + ""event_id"": //text [PK]//
+ --
+ *""raw_payload"": //jsonb //
+}
+
+entity "**schema_migrations**" {
+ + ""version"": //bigint [PK]//
+ --
+ *""dirty"": //boolean //
+}
+
+entity "**sync_group_listing_drafts**" {
+ + ""account_id"": //integer [PK][FK]//
+ + ""order_index"": //integer [PK]//
+ --
+ ""platform"": //platform //
+ ""shop_id"": //text //
+ ""listing_id"": //text //
+}
+
+entity "**sync_group_listings**" {
+ + ""sync_group_id"": //integer [PK][FK]//
+ + ""order_index"": //integer [PK]//
+ --
+ *""platform"": //platform //
+ *""shop_id"": //text //
+ *""listing_id"": //text //
+}
+
+entity "**sync_groups**" {
+ + ""sync_group_id"": //serial [PK]//
+ --
+ *""account_id"": //integer [FK]//
+}
+
+entity "**tiktok_store_events**" {
+ + ""store_id"": //text [PK]//
+ + ""event_timestamp"": //timestamp with time zone [PK]//
+ + ""event_id"": //text [PK]//
+ --
+ *""platform"": //text [FK]//
+}
+
+entity "**wix_store_events**" {
+ + ""store_id"": //text [PK]//
+ + ""event_timestamp"": //timestamp with time zone [PK]//
+ + ""event_id"": //text [PK]//
+ --
+ *""platform"": //text [FK]//
+}
+
+"**accounts**" }-- "**oauth_users**"
+
+"**etsy_access_tokens**" ||-|| "**etsy_users**"
+
+"**etsy_oauth_requests**" }-- "**accounts**"
+
+"**etsy_store_events**" }-- "**raw_store_events**"
+
+"**etsy_users**" }-- "**accounts**"
+
+"**oauth_tokens**" }-- "**oauth_users**"
+
+"**sync_group_listing_drafts**" }-- "**accounts**"
+
+"**sync_group_listings**" }-- "**sync_groups**"
+
+"**sync_groups**" }-- "**accounts**"
+
+"**tiktok_store_events**" }-- "**raw_store_events**"
+
+"**wix_store_events**" }-- "**raw_store_events**"
+@enduml
diff --git a/internal/domains/accounts/platform.go b/internal/domains/accounts/platform.go
index 0c833a9..629e911 100644
--- a/internal/domains/accounts/platform.go
+++ b/internal/domains/accounts/platform.go
@@ -10,22 +10,46 @@ type (
)
const (
- Etsy Platform = "Etsy"
- Tiktok Platform = "Tiktok"
- Wix Platform = "Wix"
+ Etsy Platform = "Etsy"
+ Tiktok Platform = "Tiktok"
+ Wix Platform = "Wix"
+ Ebay Platform = "ebay"
+ WalmartMarketplace Platform = "walmart_marketplace"
+ Amazon Platform = "amazon"
+ BigCartel Platform = "big_cartel"
+ Ecwid Platform = "ecwid"
+ Zoho Platform = "zoho"
+ SquareOnline Platform = "square_online"
+ Squarespace Platform = "squarespace"
+ WooCommerce Platform = "woo_commerce"
+ Shopify Platform = "shopify"
+)
+
+var (
+ allValues = []Platform{
+ Etsy,
+ Tiktok,
+ Wix,
+ Ebay,
+ WalmartMarketplace,
+ Amazon,
+ BigCartel,
+ Ecwid,
+ Zoho,
+ SquareOnline,
+ Squarespace,
+ WooCommerce,
+ Shopify,
+ }
)
func NewPlatform(s string) (Platform, error) {
- switch strings.ToLower(s) {
- case strings.ToLower(string(Etsy)):
- return Etsy, nil
- case strings.ToLower(string(Tiktok)):
- return Tiktok, nil
- case strings.ToLower(string(Wix)):
- return Wix, nil
- default:
- return "", fmt.Errorf("unrecognized constant: %q", s)
+ for _, v := range allValues {
+ if strings.ToLower(s) == strings.ToLower(string(v)) {
+ return v, nil
+ }
}
+ return "", fmt.Errorf("unrecognized constant: %q", s)
}
// sql.Scanner implementation
diff --git a/main.go b/main.go
index 218d1b1..cd1e587 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
package main
-//go:generate planter postgres://planter:planter@localhost:5432/inventory_2?sslmode=disable -o diagrams/database_schema.uml
+//go:generate planter postgres://planter:planter@localhost:5432/inventory_2?sslmode=disable -o diagrams/database_schema_public.uml
+//go:generate planter postgres://planter:planter@localhost:5432/inventory_2?sslmode=disable -s mock -o diagrams/database_schema_mock.uml
//go:generate plantuml diagrams/*.uml -tsvg
//go:generate plantuml diagrams/etsy/*.uml -tsvg
//go:generate npx @tailwindcss/cli -i styles/source.css -o styles/index.css