Skip to content

Commit 7100789

Browse files
committed
source-shopify-native: add ProductVariant Metafields
Add ProductVariantMetafields class with custom QUERY fragment including variants and metafields structure. Output is an individual variant records with metafields attached, using variant ID as primary key
1 parent ecdcf14 commit 7100789

File tree

6 files changed

+164
-1
lines changed

6 files changed

+164
-1
lines changed

source-shopify-native/acmeCo/flow.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ collections:
7676
schema: product_metafields.schema.yaml
7777
key:
7878
- /id
79+
acmeCo/product_variant_metafields:
80+
schema: product_variant_metafields.schema.yaml
81+
key:
82+
- /id
7983
acmeCo/product_variants:
8084
schema: product_variants.schema.yaml
8185
key:
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
$defs:
3+
Meta:
4+
properties:
5+
op:
6+
default: u
7+
description: "Operation type (c: Create, u: Update, d: Delete)"
8+
enum:
9+
- c
10+
- u
11+
- d
12+
title: Op
13+
type: string
14+
row_id:
15+
default: -1
16+
description: "Row ID of the Document, counting up from zero, or -1 if not known"
17+
title: Row Id
18+
type: integer
19+
title: Meta
20+
type: object
21+
additionalProperties: true
22+
properties:
23+
_meta:
24+
$ref: "#/$defs/Meta"
25+
description: Document metadata
26+
id:
27+
title: Id
28+
type: string
29+
required:
30+
- id
31+
title: ShopifyGraphQLResource
32+
type: object
33+
x-infer-schema: true

source-shopify-native/source_shopify_native/graphql/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from .products.variants import ProductVariants
1717
from .products.media import ProductMedia
1818
from .products.metafields import ProductMetafields
19+
from .products.metafields import ProductVariantMetafields
1920
from .orders.agreements import OrderAgreements
2021
from .orders.fulfillment_orders import FulfillmentOrders
2122
from .orders.fulfillments import Fulfillments
@@ -43,6 +44,7 @@
4344
"ProductVariants",
4445
"ProductMedia",
4546
"ProductMetafields",
47+
"ProductVariantMetafields",
4648
"OrderAgreements",
4749
"FulfillmentOrders",
4850
"Fulfillments",

source-shopify-native/source_shopify_native/graphql/products/metafields.py

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime
22
from logging import Logger
3-
from typing import AsyncGenerator
3+
import json
4+
from typing import Any, AsyncGenerator
45
from ..metafields import MetafieldsResource
56

67

@@ -24,3 +25,121 @@ def process_result(
2425
return MetafieldsResource._process_metafields_result(
2526
log, lines, ProductMetafields.PARENT_ID_KEY
2627
)
28+
29+
class ProductVariantMetafields(MetafieldsResource):
30+
NAME = "product_variant_metafields"
31+
PARENT_ID_KEY = "gid://shopify/ProductVariant/"
32+
QUERY = """
33+
variants {
34+
edges {
35+
node {
36+
id
37+
legacyResourceId
38+
createdAt
39+
updatedAt
40+
metafields {
41+
edges {
42+
node {
43+
id
44+
legacyResourceId
45+
namespace
46+
key
47+
value
48+
type
49+
definition {
50+
id
51+
description
52+
namespace
53+
type {
54+
category
55+
name
56+
}
57+
}
58+
ownerType
59+
createdAt
60+
updatedAt
61+
}
62+
}
63+
}
64+
}
65+
}
66+
}
67+
"""
68+
69+
@staticmethod
70+
def build_query(start: datetime, end: datetime) -> str:
71+
return ProductVariantMetafields.build_query_with_fragment(
72+
"products",
73+
"UPDATED_AT",
74+
start,
75+
end,
76+
)
77+
78+
@staticmethod
79+
async def process_result(
80+
log: Logger, lines: AsyncGenerator[bytes, None]
81+
) -> AsyncGenerator[dict, None]:
82+
VARIANTS_KEY = "variants"
83+
METAFIELDS_KEY = "metafields"
84+
current_product = None
85+
current_variant = None
86+
87+
async for line in lines:
88+
record: dict[str, Any] = json.loads(line)
89+
id: str = record.get("id", "")
90+
91+
if "gid://shopify/Product/" in id:
92+
if current_product:
93+
# Yield any remaining variant from previous product
94+
if current_variant:
95+
yield current_variant
96+
current_variant = None
97+
98+
current_product = record
99+
100+
elif "gid://shopify/ProductVariant/" in id:
101+
if not current_product:
102+
log.error("Found a variant before finding a product.")
103+
raise RuntimeError()
104+
elif record.get("__parentId", "") != current_product.get("id", ""):
105+
log.error(
106+
"Variant's parent id does not match the current product's id. Check if the JSONL response from Shopify is not ordered correctly.",
107+
{
108+
"variant.id": id,
109+
"variant.__parentId": record.get("__parentId"),
110+
"current_product.id": current_product.get("id"),
111+
},
112+
)
113+
raise RuntimeError()
114+
115+
# Yield previous variant if exists
116+
if current_variant:
117+
yield current_variant
118+
119+
current_variant = record
120+
current_variant[METAFIELDS_KEY] = []
121+
122+
elif "gid://shopify/Metafield/" in id:
123+
if not current_variant:
124+
log.error("Found a metafield before finding a variant.")
125+
raise RuntimeError()
126+
elif record.get("__parentId", "") != current_variant.get("id", ""):
127+
log.error(
128+
"Metafield's parent id does not match the current variant's id.",
129+
{
130+
"metafield.id": record.get("id"),
131+
"metafield.__parentId": record.get("__parentId"),
132+
"current_variant.id": current_variant.get("id"),
133+
},
134+
)
135+
raise RuntimeError()
136+
137+
current_variant[METAFIELDS_KEY].append(record)
138+
139+
else:
140+
log.error("Unidentified line in JSONL response.", record)
141+
raise RuntimeError()
142+
143+
# Yield final variant if exists
144+
if current_variant:
145+
yield current_variant

source-shopify-native/source_shopify_native/resources.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
gql.ProductMedia,
3838
gql.ProductMetafields,
3939
gql.ProductVariants,
40+
gql.ProductVariantMetafields,
4041
gql.FulfillmentOrders,
4142
gql.Fulfillments,
4243
gql.Orders,

source-shopify-native/test.flow.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ captures:
3939
name: product_variants
4040
interval: PT5M
4141
target: acmeCo/product_variants
42+
- resource:
43+
name: product_variant_metafields
44+
interval: PT5M
45+
target: acmeCo/product_variant_metafields
4246
- resource:
4347
name: fulfillment_orders
4448
interval: PT5M

0 commit comments

Comments
 (0)