support datafields in projection
This commit is contained in:
1 parent
b128608316
commit
d04679daf5
4 files changed
+64
-7
No files matched your search
@@ -115,6 +115,23 @@ func Apply(s Schema, source map[string]any) (Projection, error) {
|
|||||||
truthy = DefaultTrueValues()
|
truthy = DefaultTrueValues()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var attrs json.RawMessage
|
||||||
|
if len(s.AttributesFields) > 0 {
|
||||||
|
m := make(map[string]any)
|
||||||
|
for _, key := range s.AttributesFields {
|
||||||
|
if val, ok := source[key]; ok && val != nil {
|
||||||
|
m[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(m) > 0 {
|
||||||
|
b, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return Projection{}, fmt.Errorf("%w: marshal attributes: %w", ErrProjectionBuild, err)
|
||||||
|
}
|
||||||
|
attrs = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p := Projection{
|
p := Projection{
|
||||||
ItemID: uint32Of(source, s.ItemIdField),
|
ItemID: uint32Of(source, s.ItemIdField),
|
||||||
ID: id,
|
ID: id,
|
||||||
@@ -122,6 +139,7 @@ func Apply(s Schema, source map[string]any) (Projection, error) {
|
|||||||
Title: stringOf(source, s.TitleField),
|
Title: stringOf(source, s.TitleField),
|
||||||
PriceIncVat: price,
|
PriceIncVat: price,
|
||||||
Currency: cur,
|
Currency: cur,
|
||||||
|
Attributes: attrs,
|
||||||
TaxClass: tax,
|
TaxClass: tax,
|
||||||
InventoryTracked: boolOf(source, s.InventoryTrackedF, truthy),
|
InventoryTracked: boolOf(source, s.InventoryTrackedF, truthy),
|
||||||
DropShip: boolOf(source, s.DropShipF, truthy),
|
DropShip: boolOf(source, s.DropShipF, truthy),
|
||||||
|
|||||||
@@ -37,6 +37,39 @@ func TestBuilderJSONNumber(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuilderAttributes(t *testing.T) {
|
||||||
|
s := DefaultSchema()
|
||||||
|
s.AttributesFields = []string{"cart", "color"}
|
||||||
|
src := map[string]any{
|
||||||
|
"sku": "X-1",
|
||||||
|
"price_inc_vat": int64(100),
|
||||||
|
"updated_at": int64(1700000000000),
|
||||||
|
"cart": map[string]any{"data": "some-cart-data"},
|
||||||
|
"color": "red",
|
||||||
|
"ignored": "should-not-be-in-attrs",
|
||||||
|
}
|
||||||
|
p, err := Apply(s, src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Apply: %v", err)
|
||||||
|
}
|
||||||
|
var m map[string]any
|
||||||
|
if err := json.Unmarshal(p.Attributes, &m); err != nil {
|
||||||
|
t.Fatalf("unmarshal Attributes: %v", err)
|
||||||
|
}
|
||||||
|
if len(m) != 2 {
|
||||||
|
t.Errorf("expected 2 attributes, got %d", len(m))
|
||||||
|
}
|
||||||
|
if cartVal, ok := m["cart"].(map[string]any); !ok || cartVal["data"] != "some-cart-data" {
|
||||||
|
t.Errorf("cart attribute: got %v", m["cart"])
|
||||||
|
}
|
||||||
|
if m["color"] != "red" {
|
||||||
|
t.Errorf("color attribute: got %v", m["color"])
|
||||||
|
}
|
||||||
|
if _, ok := m["ignored"]; ok {
|
||||||
|
t.Errorf("ignored attribute was extracted: %v", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Updated=UseSourceField: missing source updated_at MUST be a loud error,
|
// Updated=UseSourceField: missing source updated_at MUST be a loud error,
|
||||||
// not a stamp-now fallback. (Foot-gun 3 resolution: silence here invalidates
|
// not a stamp-now fallback. (Foot-gun 3 resolution: silence here invalidates
|
||||||
// every consumer's cache on rebuild.)
|
// every consumer's cache on rebuild.)
|
||||||
|
|||||||
@@ -92,13 +92,14 @@ type Projection struct {
|
|||||||
// TaxClass uses a string identifier (see platform/tax class constants).
|
// TaxClass uses a string identifier (see platform/tax class constants).
|
||||||
// Empty string means "unclassified"; the consumer's tax.Provider is what
|
// Empty string means "unclassified"; the consumer's tax.Provider is what
|
||||||
// resolves the identifier to a numeric rate.
|
// resolves the identifier to a numeric rate.
|
||||||
TaxClass string `json:"tax_class"`
|
Attributes json.RawMessage `json:"attributes,omitempty"`
|
||||||
InventoryTracked bool `json:"inventory_tracked"`
|
TaxClass string `json:"tax_class"`
|
||||||
DropShip bool `json:"drop_ship"`
|
InventoryTracked bool `json:"inventory_tracked"`
|
||||||
Category string `json:"category,omitempty"`
|
DropShip bool `json:"drop_ship"`
|
||||||
Brand string `json:"brand,omitempty"`
|
Category string `json:"category,omitempty"`
|
||||||
Image string `json:"image,omitempty"`
|
Brand string `json:"brand,omitempty"`
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
Image string `json:"image,omitempty"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayloadKey is the envelope key producers use when publishing a slice of
|
// PayloadKey is the envelope key producers use when publishing a slice of
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ type Schema struct {
|
|||||||
CategoryField string `json:"category_field,omitempty"`
|
CategoryField string `json:"category_field,omitempty"`
|
||||||
BrandField string `json:"brand_field,omitempty"`
|
BrandField string `json:"brand_field,omitempty"`
|
||||||
ImageField string `json:"image_field,omitempty"`
|
ImageField string `json:"image_field,omitempty"`
|
||||||
|
// AttributesFields names the keys in the source map[string]any that
|
||||||
|
// should be extracted and built into the Projection's json.RawMessage Attributes field.
|
||||||
|
AttributesFields []string `json:"attributes_fields,omitempty"`
|
||||||
// ItemIdField is the source-side key carrying the producer's catalog
|
// ItemIdField is the source-side key carrying the producer's catalog
|
||||||
// item id (uint32-equivalent). DefaultSchema points at "id" (matching
|
// item id (uint32-equivalent). DefaultSchema points at "id" (matching
|
||||||
// the product service's /api/get/{sku} shape). Producers that omit
|
// the product service's /api/get/{sku} shape). Producers that omit
|
||||||
@@ -127,6 +130,7 @@ func DefaultSchema() Schema {
|
|||||||
// Standard product service carries the integer catalog id under "id".
|
// Standard product service carries the integer catalog id under "id".
|
||||||
ItemIdField: "id",
|
ItemIdField: "id",
|
||||||
UpdatedAtField: "updated_at",
|
UpdatedAtField: "updated_at",
|
||||||
|
AttributesFields: []string{"data"},
|
||||||
|
|
||||||
FixedCurrency: "", // read from source if present
|
FixedCurrency: "", // read from source if present
|
||||||
DefaultTaxClass: "standard",
|
DefaultTaxClass: "standard",
|
||||||
@@ -165,6 +169,7 @@ func BygglagretSchema() Schema {
|
|||||||
ImageField: "PrimaryImage",
|
ImageField: "PrimaryImage",
|
||||||
ItemIdField: "item_id",
|
ItemIdField: "item_id",
|
||||||
UpdatedAtField: "LastModifiedMs",
|
UpdatedAtField: "LastModifiedMs",
|
||||||
|
AttributesFields: []string{"data"},
|
||||||
|
|
||||||
FixedCurrency: "SEK",
|
FixedCurrency: "SEK",
|
||||||
DefaultTaxClass: "standard",
|
DefaultTaxClass: "standard",
|
||||||
|
|||||||
Reference in new issue
Block a user