first drafts for inventory sync page

This commit is contained in:
2026-01-09 18:03:54 -07:00
parent 846f56e50b
commit a7c8fe4d64
40 changed files with 1310 additions and 121 deletions
+119 -35
View File
@@ -1,5 +1,8 @@
package accounts
// to generate StoreWithContext
//go:generate concurry -s Store
import (
"context"
"errors"
@@ -15,20 +18,51 @@ type (
db *pgxpool.Pool
}
StoreWithContext struct {
ctx context.Context
db *Store
}
Account struct {
UserID string
ID int64
Email string
AccountIDs
OAuthUser
Email string
}
OAuthUser struct {
UserID string
}
AccountShop struct {
AccountShopIDs
Platform Platform
Name string
}
Listing struct {
AccountIDs
Platform Platform
ShopID string
ListingID int64
SKU string
Name string
Description string
Count int64
}
// ids
AccountIDs struct {
AccountID int64
}
AccountShopIDs struct {
AccountIDs
ShopID string
}
Platform string
)
const (
Etsy Platform = "Etsy"
Tiktok Platform = "Tiktok"
Wix Platform = "Wix"
)
func NewStore(db *pgxpool.Pool) *Store {
@@ -38,10 +72,7 @@ func NewStore(db *pgxpool.Pool) *Store {
}
func (db *Store) WithContext(ctx context.Context) *StoreWithContext {
return &StoreWithContext{
ctx: ctx,
db: db,
}
return NewStoreWithContext(ctx, db)
}
func (db *Store) CreateAccount(ctx context.Context, userID, email string) (Account, error) {
@@ -78,16 +109,20 @@ func (db *Store) CreateAccount(ctx context.Context, userID, email string) (Accou
}
return Account{
UserID: userID,
ID: acctID,
Email: email,
OAuthUser: OAuthUser{
UserID: userID,
},
AccountIDs: AccountIDs{
AccountID: acctID,
},
Email: email,
}, nil
}
func (db *Store) GetAccount(ctx context.Context, id int64) (Account, error) {
rows, err := db.db.Query(
ctx,
"SELECT email FROM accounts WHERE account_id = @account_id",
"SELECT email, user_id FROM accounts WHERE account_id = @account_id",
pgx.NamedArgs{
"account_id": id,
},
@@ -96,7 +131,12 @@ func (db *Store) GetAccount(ctx context.Context, id int64) (Account, error) {
return Account{}, fmt.Errorf("failed to perform query: %w", err)
}
email, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[string])
type Row struct {
Email string
User_id string
}
r, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByNameLax[Row])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return Account{}, consts.ErrNotFound
@@ -106,8 +146,13 @@ func (db *Store) GetAccount(ctx context.Context, id int64) (Account, error) {
}
return Account{
ID: id,
Email: email,
AccountIDs: AccountIDs{
AccountID: id,
},
OAuthUser: OAuthUser{
r.User_id,
},
Email: r.Email,
}, nil
}
@@ -143,16 +188,20 @@ func (db *Store) GetAccountByUserID(ctx context.Context, userID string) (Account
}
return Account{
UserID: userID,
ID: r.Account_ID,
Email: r.Email,
AccountIDs: AccountIDs{
AccountID: r.Account_ID,
},
OAuthUser: OAuthUser{
UserID: userID,
},
Email: r.Email,
}, nil
}
func (db *Store) GetAccountByEmail(ctx context.Context, email string) (Account, error) {
rows, err := db.db.Query(
ctx,
"SELECT account_id FROM accounts WHERE email = @email",
"SELECT account_id, user_id FROM accounts WHERE email = @email",
pgx.NamedArgs{
"email": email,
},
@@ -161,7 +210,12 @@ func (db *Store) GetAccountByEmail(ctx context.Context, email string) (Account,
return Account{}, fmt.Errorf("failed to perform query: %w", err)
}
id, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[int64])
type Row struct {
Account_id int64
User_id string
}
r, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByNameLax[Row])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return Account{}, consts.ErrNotFound
@@ -171,7 +225,12 @@ func (db *Store) GetAccountByEmail(ctx context.Context, email string) (Account,
}
return Account{
ID: id,
AccountIDs: AccountIDs{
AccountID: r.Account_id,
},
OAuthUser: OAuthUser{
UserID: r.User_id,
},
Email: email,
}, nil
}
@@ -213,28 +272,53 @@ func (db *Store) GetUserAndAccountByAccessToken(ctx context.Context, accessToken
return OAuthUser{}, nil, fmt.Errorf("failed to scan row: %w", err)
}
user := OAuthUser{UserID: r.User_ID}
var acct *Account
if r.Account_ID != nil {
acct = &Account{
UserID: r.User_ID,
ID: *r.Account_ID,
Email: *r.Email,
AccountIDs: AccountIDs{
AccountID: *r.Account_ID,
},
OAuthUser: user,
Email: *r.Email,
}
}
return OAuthUser{UserID: r.User_ID}, acct, nil
return user, acct, nil
}
func (db *StoreWithContext) CreateAccount(userID, email string) (Account, error) {
return db.db.CreateAccount(db.ctx, userID, email)
func (db *Store) GetShops(ctx context.Context, acctID int64) ([]AccountShop, error) {
var shops []AccountShop
for _, v := range devShops {
if v.AccountID != acctID {
continue
}
shops = append(shops, v)
}
return shops, nil
}
func (db *StoreWithContext) GetAccount(id int64) (Account, error) {
return db.db.GetAccount(db.ctx, id)
func (db *Store) GetListingsForShop(ctx context.Context, acctID int64, shopID string) ([]Listing, error) {
var vs []Listing
for _, v := range devListings {
if v.AccountID != acctID {
continue
}
if v.ShopID != shopID {
continue
}
vs = append(vs, v)
}
return vs, nil
}
func (db *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) {
acct, err := db.db.GetAccountByUserID(db.ctx, userID)
// additional context
func (db *Store) GetAccountPointerByUserID(ctx context.Context, userID string) (*Account, error) {
acct, err := db.GetAccountByUserID(ctx, userID)
if err == nil {
return &acct, nil
}
+243
View File
@@ -0,0 +1,243 @@
package accounts
var (
devShops = []AccountShop{
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "2",
},
Platform: Etsy,
Name: "Etsy 1",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "5",
},
Platform: Etsy,
Name: "Etsy 2",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "8",
},
Platform: Etsy,
Name: "Etsy 3",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "11",
},
Platform: Tiktok,
Name: "Tiktok 1",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "14",
},
Platform: Tiktok,
Name: "Tiktok 2",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "17",
},
Platform: Tiktok,
Name: "Tiktok 3",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "20",
},
Platform: Wix,
Name: "Wix 1",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "23",
},
Platform: Wix,
Name: "Wix 2",
},
AccountShop{
AccountShopIDs: AccountShopIDs{
AccountIDs: AccountIDs{
AccountID: 6,
},
ShopID: "26",
},
Platform: Wix,
Name: "Wix 3",
},
}
devListings = []Listing{
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Etsy,
ShopID: "1",
ListingID: 1,
SKU: "sku 1",
Name: "name 1",
Description: "description 1",
Count: 51,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Etsy,
ShopID: "1",
ListingID: 2,
SKU: "sku 2",
Name: "name 2",
Description: "description 2",
Count: 52,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Etsy,
ShopID: "1",
ListingID: 3,
SKU: "sku 3",
Name: "name 3",
Description: "description 3",
Count: 53,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Tiktok,
ShopID: "4",
ListingID: 4,
SKU: "sku 4",
Name: "name 4",
Description: "description 4",
Count: 54,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Tiktok,
ShopID: "4",
ListingID: 5,
SKU: "sku 5",
Name: "name 5",
Description: "description 5",
Count: 55,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Tiktok,
ShopID: "4",
ListingID: 6,
SKU: "sku 6",
Name: "name 6",
Description: "description 6",
Count: 56,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Wix,
ShopID: "7",
ListingID: 7,
SKU: "sku 7",
Name: "name 7",
Description: "description 7",
Count: 57,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Wix,
ShopID: "7",
ListingID: 8,
SKU: "sku 8",
Name: "name 8",
Description: "description 8",
Count: 58,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Wix,
ShopID: "7",
ListingID: 9,
SKU: "sku 9",
Name: "name 9",
Description: "description 9",
Count: 59,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Etsy,
ShopID: "2",
ListingID: 10,
SKU: "sku 10",
Name: "name 10",
Description: "description 10",
Count: 60,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Etsy,
ShopID: "2",
ListingID: 11,
SKU: "sku 11",
Name: "name 11",
Description: "description 11",
Count: 61,
},
{
AccountIDs: AccountIDs{
AccountID: 6,
},
Platform: Etsy,
ShopID: "2",
ListingID: 12,
SKU: "sku 12",
Name: "name 12",
Description: "description 12",
Count: 62,
},
}
)
@@ -0,0 +1,52 @@
// Code generated by concurry DO NOT EDIT.
// https://github.com/angelbeltran/concurry
// concurry
package accounts
import (
"context"
)
type StoreWithContext struct {
ctx context.Context
*Store
}
func NewStoreWithContext(ctx context.Context, v *Store) *StoreWithContext {
return &StoreWithContext{
ctx: ctx,
Store: v,
}
}
func (v_ctx *StoreWithContext) CreateAccount(userID string, email string) (Account, error) {
return v_ctx.Store.CreateAccount(v_ctx.ctx, userID, email)
}
func (v_ctx *StoreWithContext) GetAccount(id int64) (Account, error) {
return v_ctx.Store.GetAccount(v_ctx.ctx, id)
}
func (v_ctx *StoreWithContext) GetAccountByUserID(userID string) (Account, error) {
return v_ctx.Store.GetAccountByUserID(v_ctx.ctx, userID)
}
func (v_ctx *StoreWithContext) GetAccountByEmail(email string) (Account, error) {
return v_ctx.Store.GetAccountByEmail(v_ctx.ctx, email)
}
func (v_ctx *StoreWithContext) GetUserAndAccountByAccessToken(accessToken string) (OAuthUser, *Account, error) {
return v_ctx.Store.GetUserAndAccountByAccessToken(v_ctx.ctx, accessToken)
}
func (v_ctx *StoreWithContext) GetShops(acctID int64) ([]AccountShop, error) {
return v_ctx.Store.GetShops(v_ctx.ctx, acctID)
}
func (v_ctx *StoreWithContext) GetListingsForShop(acctID int64, shopID string) ([]Listing, error) {
return v_ctx.Store.GetListingsForShop(v_ctx.ctx, acctID, shopID)
}
func (v_ctx *StoreWithContext) GetAccountPointerByUserID(userID string) (*Account, error) {
return v_ctx.Store.GetAccountPointerByUserID(v_ctx.ctx, userID)
}
+2 -13
View File
@@ -20,6 +20,7 @@ import (
)
//go:generate oapi-codegen -generate types,client -package generated_client -o generated_client/client.go openapi.3.0.2.json
//go:generate concurry -s Platform
// TODO:
// - [x] document the flow in the README.md
@@ -36,11 +37,6 @@ type (
apiSharedSecret string
db *pgxpool.Pool
}
PlatformWithContext struct {
ctx context.Context
p *Platform
}
)
// oauth scopes
@@ -77,10 +73,7 @@ func NewPlatform(oAuthRedirectURI func(acctID int64) string, apiKeystring, apiSh
}
func (p *Platform) WithContext(ctx context.Context) *PlatformWithContext {
return &PlatformWithContext{
ctx: ctx,
p: p,
}
return NewPlatformWithContext(ctx, p)
}
func (p *Platform) GenerateConnectionURLForNewAccount(ctx context.Context, acctID int64) (*url.URL, error) {
@@ -113,10 +106,6 @@ func (p *Platform) GenerateConnectionURLForNewAccount(ctx context.Context, acctI
}, nil
}
func (p *PlatformWithContext) GenerateConnectionURLForNewAccount(acctID int64) (*url.URL, error) {
return p.p.GenerateConnectionURLForNewAccount(p.ctx, acctID)
}
// HandleNewAuthCode handles the auth code to get api access
func (p *Platform) HandleNewAuthCode(ctx context.Context, acctID int64, state, authCode string) (bool, error) {
// look up matching oauth request
@@ -0,0 +1,63 @@
// Code generated by concurry DO NOT EDIT.
// https://github.com/angelbeltran/concurry
// concurry
package etsy
import (
"context"
"github.com/google/uuid"
"net/url"
"time"
)
type PlatformWithContext struct {
ctx context.Context
*Platform
}
func NewPlatformWithContext(ctx context.Context, v *Platform) *PlatformWithContext {
return &PlatformWithContext{
ctx: ctx,
Platform: v,
}
}
func (v_ctx *PlatformWithContext) GenerateConnectionURLForNewAccount(acctID int64) (*url.URL, error) {
return v_ctx.Platform.GenerateConnectionURLForNewAccount(v_ctx.ctx, acctID)
}
func (v_ctx *PlatformWithContext) HandleNewAuthCode(acctID int64, state string, authCode string) (bool, error) {
return v_ctx.Platform.HandleNewAuthCode(v_ctx.ctx, acctID, state, authCode)
}
func (v_ctx *PlatformWithContext) parseAccessCodeResponseBody(body []byte) (string, string, time.Time, int64, error) {
return v_ctx.Platform.parseAccessCodeResponseBody(v_ctx.ctx, body)
}
func (v_ctx *PlatformWithContext) getNewUserShopID(accessToken string, userID int64) (int64, error) {
return v_ctx.Platform.getNewUserShopID(v_ctx.ctx, accessToken, userID)
}
func (v_ctx *PlatformWithContext) GetUserPointerByAccountID(acctID int64) (*EtsyUser, error) {
return v_ctx.Platform.GetUserPointerByAccountID(v_ctx.ctx, acctID)
}
func (v_ctx *PlatformWithContext) saveNewEtsyUser(user EtsyUser, tokens etsyAccessTokens) error {
return v_ctx.Platform.saveNewEtsyUser(v_ctx.ctx, user, tokens)
}
func (v_ctx *PlatformWithContext) createNewOAuthRequest(acctID int64) (oauth2Request, error) {
return v_ctx.Platform.createNewOAuthRequest(v_ctx.ctx, acctID)
}
func (v_ctx *PlatformWithContext) getOauthRequest(state uuid.UUID) (oauth2Request, bool, error) {
return v_ctx.Platform.getOauthRequest(v_ctx.ctx, state)
}
func (v_ctx *PlatformWithContext) InvalidateState(state string) error {
return v_ctx.Platform.InvalidateState(v_ctx.ctx, state)
}
func (v_ctx *PlatformWithContext) deleteOauthRequest(state uuid.UUID) error {
return v_ctx.Platform.deleteOauthRequest(v_ctx.ctx, state)
}
-4
View File
@@ -73,10 +73,6 @@ func (p *Platform) GetUserPointerByAccountID(ctx context.Context, acctID int64)
}, nil
}
func (p *PlatformWithContext) GetUserPointerByAccountID(acctID int64) (*EtsyUser, error) {
return p.p.GetUserPointerByAccountID(p.ctx, acctID)
}
func (p *Platform) saveNewEtsyUser(
ctx context.Context,
user EtsyUser,
+1 -21
View File
@@ -16,12 +16,6 @@ type (
db *pgxpool.Pool
}
// StoreWithContext allows currying all Store methods by context.Context
StoreWithContext struct {
ctx context.Context
db *Store
}
Event struct {
Platform string
StoreID string
@@ -38,10 +32,7 @@ func NewStore(db *pgxpool.Pool) *Store {
}
func (db *Store) WithContext(ctx context.Context) *StoreWithContext {
return &StoreWithContext{
ctx: ctx,
db: db,
}
return NewStoreWithContext(ctx, db)
}
func (db *Store) Save(ctx context.Context, e *Event) error {
@@ -81,10 +72,6 @@ func (db *Store) Save(ctx context.Context, e *Event) error {
return nil
}
func (db *StoreWithContext) Save(e *Event) error {
return db.db.Save(db.ctx, e)
}
func (db *Store) LoadEventsForStore(
ctx context.Context,
platform string,
@@ -125,10 +112,3 @@ func (db *Store) LoadEventsForStore(
return evts, nil
}
func (db *StoreWithContext) LoadEventsForStore(
platform string,
storeID string,
) ([]Event, error) {
return db.db.LoadEventsForStore(db.ctx, platform, storeID)
}
@@ -0,0 +1,28 @@
// Code generated by concurry DO NOT EDIT.
// https://github.com/angelbeltran/concurry
// concurry
package raw_events
import (
"context"
)
type StoreWithContext struct {
ctx context.Context
*Store
}
func NewStoreWithContext(ctx context.Context, v *Store) *StoreWithContext {
return &StoreWithContext{
ctx: ctx,
Store: v,
}
}
func (v_ctx *StoreWithContext) Save(e *Event) error {
return v_ctx.Store.Save(v_ctx.ctx, e)
}
func (v_ctx *StoreWithContext) LoadEventsForStore(platform string, storeID string) ([]Event, error) {
return v_ctx.Store.LoadEventsForStore(v_ctx.ctx, platform, storeID)
}