@doswiftly/storefront-operations 6.1.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +197 -0
- package/README.md +12 -9
- package/package.json +1 -1
- package/schema.graphql +194 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,202 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 7.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 227629d: # Storefront GraphQL Operations 7.0 — Breaking changes + nowe rozszerzenia API
|
|
8
|
+
|
|
9
|
+
Major bump wprowadza zmiany breaking w schemie storefront GraphQL plus 3 znaczące nowe funkcjonalności: per-address tax info dla B2B, generic Meta Properties (extensible custom fields), oraz strukturalne userErrors[] z typed codes dla wszystkich mutacji.
|
|
10
|
+
|
|
11
|
+
## Breaking changes
|
|
12
|
+
|
|
13
|
+
### 1. `Product.category` (free-text) → `Product.categories` + `Product.primaryCategory` (structured)
|
|
14
|
+
|
|
15
|
+
Wcześniej `Product.category: String` zwracał free-text label — tekst nie był powiązany z entity Category. Klienci nie mogli pobrać slug/name kategorii ani zbudować breadcrumb. Po 7.0:
|
|
16
|
+
|
|
17
|
+
```graphql
|
|
18
|
+
# PRZED (6.x):
|
|
19
|
+
product { category } # → "Pop Vinyl" (free-text string, brak slug/parent)
|
|
20
|
+
|
|
21
|
+
# PO (7.0):
|
|
22
|
+
product {
|
|
23
|
+
categories { id slug name parent { slug } } # M2M lista (junction table)
|
|
24
|
+
primaryCategory { slug name } # categories[0] dla breadcrumb
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Migration**:
|
|
29
|
+
- Klient renderujący breadcrumb po category name: zamień `product.category` na `product.primaryCategory.name`.
|
|
30
|
+
- Klient filtrujący kategorie z `Product.category` jako string: użyj `product.categories[]` array.
|
|
31
|
+
- Filter `productCategory` w `ProductFilter` usunięty — używaj `category: { id }` (junction lookup, działało wcześniej).
|
|
32
|
+
|
|
33
|
+
### 2. `categories` query — `CategoryTree` → `CategoryConnection` (Relay)
|
|
34
|
+
|
|
35
|
+
Wcześniej `categories` zwracał outlier shape `{ roots, totalCount }` vs reszta schemy używała Relay Connection. Klienci próbujący `categories { nodes }` dostawali GraphQL error. Po 7.0:
|
|
36
|
+
|
|
37
|
+
```graphql
|
|
38
|
+
# PRZED (6.x):
|
|
39
|
+
categories(first: 20, rootsOnly: true) {
|
|
40
|
+
roots { id name children { id name } }
|
|
41
|
+
totalCount
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# PO (7.0):
|
|
45
|
+
categories(first: 20, rootsOnly: true) {
|
|
46
|
+
nodes { id name children { id name } }
|
|
47
|
+
edges { cursor node { id } }
|
|
48
|
+
pageInfo { hasNextPage hasPreviousPage startCursor endCursor }
|
|
49
|
+
totalCount
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Migration**:
|
|
54
|
+
- Zamień `categories.roots` na `categories.nodes`.
|
|
55
|
+
- Tree shape rebuild po stronie klienta — używaj `Category.parent` / `Category.children` field resolvers (DataLoader N+1 safe). Filtr `rootsOnly: true` zwraca tylko top-level (parent IS NULL).
|
|
56
|
+
- Pełen Relay args: `first/after/last/before` zamiast tylko `first/after`.
|
|
57
|
+
|
|
58
|
+
### 3. `Customer.defaultAddress` — czytelny stan (poprzednio zawsze null)
|
|
59
|
+
|
|
60
|
+
Wcześniej `Customer.defaultAddress` zwracał `null` mimo że adres miał `isDefault: true` w `addresses`. Niespójność łamała storefront breadcrumb i powodowała duplikaty w formularzu dostawy. Po 7.0 zwraca poprawnie wartość — single source of truth z `addresses[].isDefaultShipping` flag.
|
|
61
|
+
|
|
62
|
+
**Migration**: brak — klient po prostu zacznie dostawać poprawne wartości.
|
|
63
|
+
|
|
64
|
+
### 4. Apollo response — bez `extensions.stacktrace` w produkcji
|
|
65
|
+
|
|
66
|
+
Wcześniej w środowisku produkcyjnym GraphQL response zawierał stacktrace z absolutnymi ścieżkami systemu plików backendu. Po 7.0 production response strippuje wszystko poza `code`, `message`, `locations`, `path` + banner `extensions.environment`. Stacktrace dostępny **tylko** w dev/staging dla DX.
|
|
67
|
+
|
|
68
|
+
**Migration**: brak — klient produkcyjny nie powinien był polegać na stacktrace.
|
|
69
|
+
|
|
70
|
+
### 5. Validation errors → `userErrors[]` z typed codes (tech debt resolution)
|
|
71
|
+
|
|
72
|
+
Wcześniej walidacja DTO leciała jako `body.errors[].extensions.code = "INTERNAL_SERVER_ERROR"` envelope (wyglądało jak crash). Po 7.0 mutations zwracają strukturalne `userErrors[]` z explicit codes per validation rule:
|
|
73
|
+
|
|
74
|
+
```graphql
|
|
75
|
+
# Invalid NIP, password too short, malformed email — wszystkie zwracają payload
|
|
76
|
+
mutation {
|
|
77
|
+
customerUpdate(customer: { taxId: "1234567890" }) {
|
|
78
|
+
customer {
|
|
79
|
+
id
|
|
80
|
+
}
|
|
81
|
+
userErrors {
|
|
82
|
+
field
|
|
83
|
+
code
|
|
84
|
+
message
|
|
85
|
+
}
|
|
86
|
+
# → [{ field: ["taxId"], code: "INVALID_TAX_ID_CHECKSUM", message: "..." }]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Typed codes** dla 25 walidatorów: `INVALID_TAX_ID_CHECKSUM`, `INVALID_VAT_NUMBER`, `INVALID_REGON`, `INVALID_EMAIL_FORMAT`, `INVALID_FORMAT`, `INVALID_PHONE_FORMAT`, `INVALID_UUID`, `INVALID_URL`, `INVALID_DATE`, `INVALID_ENUM_VALUE`, `INVALID_TYPE`, `INVALID_COUNTRY_CODE`, `INVALID_POSTAL_CODE`, `TOO_SHORT`, `TOO_LONG`, `TOO_MANY`, `TOO_FEW`, `OUT_OF_RANGE`, `REQUIRED`, `INVALID_NESTED`, `VALIDATION_ERROR`, etc.
|
|
92
|
+
|
|
93
|
+
**Migration**: klient widzący poprzednio `body.errors[].extensions.code = "INTERNAL_SERVER_ERROR"` — sprawdź `data.<mutation>.userErrors[]` zamiast envelope.
|
|
94
|
+
|
|
95
|
+
## New features
|
|
96
|
+
|
|
97
|
+
### 6. `MailingAddress.taxId` + `vatNumber` — per-address B2B tax info
|
|
98
|
+
|
|
99
|
+
Adres ma teraz opcjonalne `taxId` (polski NIP, 10 cyfr z checksumą) + `vatNumber` (VAT UE, np. PL5260250274). Use case: B2B z różnymi danymi firmowymi per adres dostawy/billing (faktura na firmę matkę, dostawa na oddział z osobnym NIP-em).
|
|
100
|
+
|
|
101
|
+
```graphql
|
|
102
|
+
mutation {
|
|
103
|
+
customerAddAddress(
|
|
104
|
+
address: {
|
|
105
|
+
streetLine1: "ul. Marszałkowska 100"
|
|
106
|
+
city: "Warszawa"
|
|
107
|
+
postalCode: "00-001"
|
|
108
|
+
country: PL
|
|
109
|
+
company: "GameGoods Sp. z o.o."
|
|
110
|
+
taxId: "5260250274"
|
|
111
|
+
vatNumber: "PL5260250274"
|
|
112
|
+
}
|
|
113
|
+
) {
|
|
114
|
+
address {
|
|
115
|
+
taxId
|
|
116
|
+
vatNumber
|
|
117
|
+
}
|
|
118
|
+
userErrors {
|
|
119
|
+
code
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Walidacja format/checksum aktywna — invalid NIP zwraca `userErrors` z `INVALID_TAX_ID_CHECKSUM`.
|
|
126
|
+
|
|
127
|
+
### 7. Meta Properties — extensible custom fields per encji
|
|
128
|
+
|
|
129
|
+
Generic mechanizm rozszerzania encji (Customer/Product/ProductVariant/Order) o storefront-specific dane bez wymogu zmian schematu po stronie platformy. Per-customer birthday, per-product warranty_years, per-order gift_message — wszystko jako typed key-value entries w polymorphic table.
|
|
130
|
+
|
|
131
|
+
```graphql
|
|
132
|
+
# Customer self-edit (Bearer token klienta)
|
|
133
|
+
mutation {
|
|
134
|
+
customerMetaPropertiesSet(
|
|
135
|
+
properties: [
|
|
136
|
+
{
|
|
137
|
+
namespace: "storefront"
|
|
138
|
+
key: "birthday"
|
|
139
|
+
value: "1990-05-15"
|
|
140
|
+
type: DATE
|
|
141
|
+
}
|
|
142
|
+
{
|
|
143
|
+
namespace: "storefront"
|
|
144
|
+
key: "favorite_color"
|
|
145
|
+
value: "#FF6600"
|
|
146
|
+
type: COLOR
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
) {
|
|
150
|
+
metaProperties {
|
|
151
|
+
id
|
|
152
|
+
namespace
|
|
153
|
+
key
|
|
154
|
+
value
|
|
155
|
+
type
|
|
156
|
+
}
|
|
157
|
+
userErrors {
|
|
158
|
+
code
|
|
159
|
+
message
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Read na każdej encji
|
|
165
|
+
query {
|
|
166
|
+
customer {
|
|
167
|
+
metaProperty(namespace: "storefront", key: "birthday") {
|
|
168
|
+
value
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
product(id: "...") {
|
|
172
|
+
metaProperties(first: 10, namespace: "warranty") {
|
|
173
|
+
nodes {
|
|
174
|
+
key
|
|
175
|
+
value
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
10 typed values: `STRING/TEXT/INTEGER/DECIMAL/BOOLEAN/JSON/DATE/DATE_TIME/URL/COLOR`. Visibility flag `isPrivate` (storefront API filtruje `isPrivate=true` na public encjach jak Product/ProductVariant). Reserved namespace prefix `doswiftly:` dla platform fields.
|
|
183
|
+
|
|
184
|
+
**Faza 1 MVP scope**: Customer self-edit only (`customerMetaPropertiesSet` / `customerMetaPropertyDelete` z auth Bearer token klienta). Cross-resource writes (Product/Order/Variant) wymagają Admin layer (Faza 2).
|
|
185
|
+
|
|
186
|
+
### 8. NIP/REGON sanity guard — odrzucone all-zeros / repeated-digit patterns
|
|
187
|
+
|
|
188
|
+
Walidator NIP wcześniej akceptował `'0000000000'` (suma kontrolna 0=0). Po 7.0 odrzucamy wszystkie repeated-digit patterns (`'0000000000'`, `'1111111111'`, ..., `'9999999999'`) — to oczywiste fake test data / NULL placeholders, żaden urząd ich nie wystawia. Analogicznie REGON 9-digit i 14-digit.
|
|
189
|
+
|
|
190
|
+
### 9. Empty string clear-value semantyka (wszystkie nullable input fields)
|
|
191
|
+
|
|
192
|
+
Customer kasujący wartość w UI inputie (np. `<input value="" />`) wysyła `phone: ""` zamiast `null`. Po 7.0 backend traktuje empty string jako "wyczyść pole" (DB → null) — natywne form library behavior, klient nie wymaga specjalnej logiki rozróżniającej `null` vs `""`.
|
|
193
|
+
|
|
194
|
+
Pokrycie: wszystkie nullable string/enum fields w `CustomerCreateInput`, `CustomerUpdateInput`, `MailingAddressInput`.
|
|
195
|
+
|
|
196
|
+
## Notes
|
|
197
|
+
- 7.0 jest cumulative — kumulacja zmian z 6.x patches (B2B fields w `customerUpdate`, marketing consent flow) + breaking changes opisane wyżej.
|
|
198
|
+
- Wszystkie zmiany pokryte test coverage — backend integration tests gwarantują regression-free contract.
|
|
199
|
+
|
|
3
200
|
## 6.1.0
|
|
4
201
|
|
|
5
202
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ export function ProductList() {
|
|
|
96
96
|
| `Collection` | Collection with its products |
|
|
97
97
|
| `Collections` | All collections |
|
|
98
98
|
| `Category` | Category with hierarchy |
|
|
99
|
-
| `Categories` |
|
|
99
|
+
| `Categories` | Paginowana lista kategorii (Relay Connection) — drzewo budujesz po stronie klienta z `parent`/`children` |
|
|
100
100
|
| `Cart` | Cart contents by ID |
|
|
101
101
|
| `Customer` | Logged-in customer data |
|
|
102
102
|
| `CustomerOrders` | Customer order history |
|
|
@@ -116,23 +116,26 @@ export function ProductList() {
|
|
|
116
116
|
|
|
117
117
|
**Authentication**
|
|
118
118
|
|
|
119
|
-
- `
|
|
119
|
+
- `CustomerSignup` - Register new customer
|
|
120
120
|
- `CustomerLogin` - Login and get token
|
|
121
121
|
- `CustomerLogout` - Logout
|
|
122
|
-
- `
|
|
122
|
+
- `CustomerRefreshToken` - Refresh auth token
|
|
123
|
+
- `CustomerActivate` - Activate account via email link
|
|
124
|
+
- `CustomerMetaPropertiesSet` - Set custom key-value entries on the logged-in customer
|
|
125
|
+
- `CustomerMetaPropertyDelete` - Delete a custom key-value entry from the logged-in customer
|
|
123
126
|
|
|
124
127
|
**Customer Profile**
|
|
125
128
|
|
|
126
129
|
- `CustomerUpdate` - Update profile info
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
129
|
-
- `
|
|
130
|
-
- `
|
|
130
|
+
- `CustomerAddAddress` - Add new address
|
|
131
|
+
- `CustomerUpdateAddress` - Update address
|
|
132
|
+
- `CustomerRemoveAddress` - Remove address
|
|
133
|
+
- `CustomerSetDefaultAddress` - Set default address
|
|
131
134
|
|
|
132
135
|
**Password**
|
|
133
136
|
|
|
134
|
-
- `
|
|
135
|
-
- `
|
|
137
|
+
- `CustomerRequestPasswordReset` - Request password reset email
|
|
138
|
+
- `CustomerResetPassword` - Reset password with token
|
|
136
139
|
|
|
137
140
|
## Schema
|
|
138
141
|
|
package/package.json
CHANGED
package/schema.graphql
CHANGED
|
@@ -1182,11 +1182,14 @@ type Category {
|
|
|
1182
1182
|
sortOrder: Int!
|
|
1183
1183
|
}
|
|
1184
1184
|
|
|
1185
|
-
"""Paginated category list"""
|
|
1185
|
+
"""Paginated category list (Relay Connection)"""
|
|
1186
1186
|
type CategoryConnection {
|
|
1187
|
-
"""List of category edges"""
|
|
1187
|
+
"""List of category edges (cursor + node pairs)"""
|
|
1188
1188
|
edges: [CategoryEdge!]!
|
|
1189
1189
|
|
|
1190
|
+
"""List of category nodes (shortcut without cursor)"""
|
|
1191
|
+
nodes: [Category!]!
|
|
1192
|
+
|
|
1190
1193
|
"""Pagination info"""
|
|
1191
1194
|
pageInfo: PageInfo!
|
|
1192
1195
|
|
|
@@ -1230,15 +1233,6 @@ type CategoryFilterOption {
|
|
|
1230
1233
|
slug: String!
|
|
1231
1234
|
}
|
|
1232
1235
|
|
|
1233
|
-
"""Category tree structure"""
|
|
1234
|
-
type CategoryTree {
|
|
1235
|
-
"""Root categories"""
|
|
1236
|
-
roots: [Category!]!
|
|
1237
|
-
|
|
1238
|
-
"""Total categories count"""
|
|
1239
|
-
totalCount: Int!
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
1236
|
"""Checkout object"""
|
|
1243
1237
|
type Checkout {
|
|
1244
1238
|
"""Applied gift cards"""
|
|
@@ -2043,6 +2037,16 @@ type Customer implements Node {
|
|
|
2043
2037
|
"""Last name"""
|
|
2044
2038
|
lastName: String
|
|
2045
2039
|
|
|
2040
|
+
"""
|
|
2041
|
+
Lista meta properties (Relay Connection) — opcjonalnie scoped do namespace
|
|
2042
|
+
"""
|
|
2043
|
+
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
2044
|
+
|
|
2045
|
+
"""
|
|
2046
|
+
Pojedyncze meta property po (namespace, key) — dla zalogowanego klienta zwraca także private
|
|
2047
|
+
"""
|
|
2048
|
+
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
2049
|
+
|
|
2046
2050
|
"""Total orders count (UnsignedInt64 — BigInt-safe)"""
|
|
2047
2051
|
orderCount: UnsignedInt64!
|
|
2048
2052
|
|
|
@@ -3283,6 +3287,16 @@ type MailingAddress implements Node {
|
|
|
3283
3287
|
|
|
3284
3288
|
"""Second line of street address"""
|
|
3285
3289
|
streetLine2: String
|
|
3290
|
+
|
|
3291
|
+
"""
|
|
3292
|
+
Per-address tax ID (Polish NIP, 10 digits z checksum). B2B use case: różne dane firmowe per adres — np. faktura na firmę matkę z NIP A, dostawa na oddział z NIP B. Distinct from `Customer.taxId` (customer-level globally).
|
|
3293
|
+
"""
|
|
3294
|
+
taxId: String
|
|
3295
|
+
|
|
3296
|
+
"""
|
|
3297
|
+
Per-address EU VAT number (e.g. PL1234567890). B2B cross-border: różny VAT per adres dostawy. Distinct from `Customer.vatNumber` (customer-level globally).
|
|
3298
|
+
"""
|
|
3299
|
+
vatNumber: String
|
|
3286
3300
|
}
|
|
3287
3301
|
|
|
3288
3302
|
"""Paginated mailing addresses (Relay Connection)"""
|
|
@@ -3340,6 +3354,14 @@ input MailingAddressInput {
|
|
|
3340
3354
|
|
|
3341
3355
|
"""Second line of street address"""
|
|
3342
3356
|
streetLine2: String
|
|
3357
|
+
|
|
3358
|
+
"""
|
|
3359
|
+
Per-address Polish tax ID — NIP (10 digits with checksum). B2B use case: różne dane firmowe per adres dostawy/billing.
|
|
3360
|
+
"""
|
|
3361
|
+
taxId: String
|
|
3362
|
+
|
|
3363
|
+
"""Per-address EU VAT number (e.g. PL1234567890). Cross-border B2B."""
|
|
3364
|
+
vatNumber: String
|
|
3343
3365
|
}
|
|
3344
3366
|
|
|
3345
3367
|
"""
|
|
@@ -3412,6 +3434,119 @@ enum MenuItemType {
|
|
|
3412
3434
|
SEARCH
|
|
3413
3435
|
}
|
|
3414
3436
|
|
|
3437
|
+
"""
|
|
3438
|
+
Payload `customerMetaPropertiesSet` — bulk upsert własnych meta properties klienta
|
|
3439
|
+
"""
|
|
3440
|
+
type MetaPropertiesSetPayload {
|
|
3441
|
+
"""Upserted meta properties (puste gdy userErrors)"""
|
|
3442
|
+
metaProperties: [MetaProperty!]!
|
|
3443
|
+
|
|
3444
|
+
"""User errors (code z `MetaPropertyErrorCode` enum)"""
|
|
3445
|
+
userErrors: [UserError!]!
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
"""Pojedyncze pole dodatkowe (meta property) na encji commerce"""
|
|
3449
|
+
type MetaProperty implements Node {
|
|
3450
|
+
"""Created at"""
|
|
3451
|
+
createdAt: DateTime!
|
|
3452
|
+
|
|
3453
|
+
"""Unique identifier"""
|
|
3454
|
+
id: ID!
|
|
3455
|
+
|
|
3456
|
+
"""
|
|
3457
|
+
true = readable tylko przez authenticated context (admin/customer self). false (default) = readable również przez anonymous Storefront API.
|
|
3458
|
+
"""
|
|
3459
|
+
isPrivate: Boolean!
|
|
3460
|
+
|
|
3461
|
+
"""
|
|
3462
|
+
Key w obrębie namespace (3-64 chars). Unique per (ownerType, ownerId, namespace).
|
|
3463
|
+
"""
|
|
3464
|
+
key: String!
|
|
3465
|
+
|
|
3466
|
+
"""
|
|
3467
|
+
Namespace (3-64 chars). Zapobiega kolizji kluczy między aplikacjami. Reserved prefix: `doswiftly:` (platform).
|
|
3468
|
+
"""
|
|
3469
|
+
namespace: String!
|
|
3470
|
+
|
|
3471
|
+
"""Typ wartości — informuje klienta jak parse value."""
|
|
3472
|
+
type: MetaPropertyValueType!
|
|
3473
|
+
|
|
3474
|
+
"""Last updated at"""
|
|
3475
|
+
updatedAt: DateTime!
|
|
3476
|
+
|
|
3477
|
+
"""
|
|
3478
|
+
Wartość — zawsze String. Klient parsuje zgodnie z polem `type` (np. INTEGER → parseInt, JSON → JSON.parse).
|
|
3479
|
+
"""
|
|
3480
|
+
value: String!
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
"""Paginated meta property list (Relay Connection)"""
|
|
3484
|
+
type MetaPropertyConnection {
|
|
3485
|
+
"""Edges (cursor + node pairs)"""
|
|
3486
|
+
edges: [MetaPropertyEdge!]!
|
|
3487
|
+
|
|
3488
|
+
"""Nodes (shortcut bez cursor)"""
|
|
3489
|
+
nodes: [MetaProperty!]!
|
|
3490
|
+
|
|
3491
|
+
"""Page info"""
|
|
3492
|
+
pageInfo: PageInfo!
|
|
3493
|
+
|
|
3494
|
+
"""Total count (max po wszystkich pages)"""
|
|
3495
|
+
totalCount: Int!
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
"""Payload `customerMetaPropertyDelete`"""
|
|
3499
|
+
type MetaPropertyDeletePayload {
|
|
3500
|
+
"""ID usuniętego meta property (null gdy userErrors)"""
|
|
3501
|
+
deletedId: ID
|
|
3502
|
+
|
|
3503
|
+
"""User errors (code z `MetaPropertyErrorCode` enum)"""
|
|
3504
|
+
userErrors: [UserError!]!
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
"""Meta property edge (Relay pagination)"""
|
|
3508
|
+
type MetaPropertyEdge {
|
|
3509
|
+
"""Cursor for pagination"""
|
|
3510
|
+
cursor: String!
|
|
3511
|
+
|
|
3512
|
+
"""Meta property node"""
|
|
3513
|
+
node: MetaProperty!
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
"""Input dla pojedynczego meta property w bulk set"""
|
|
3517
|
+
input MetaPropertyInput {
|
|
3518
|
+
"""Visibility — true = admin/customer-only, false = storefront-readable"""
|
|
3519
|
+
isPrivate: Boolean = false
|
|
3520
|
+
|
|
3521
|
+
"""Key (3-64 chars)"""
|
|
3522
|
+
key: String!
|
|
3523
|
+
|
|
3524
|
+
"""Namespace (3-64 chars, NOT starting with `doswiftly:`)"""
|
|
3525
|
+
namespace: String!
|
|
3526
|
+
|
|
3527
|
+
"""Typ wartości (driver walidacji backend-side)"""
|
|
3528
|
+
type: MetaPropertyValueType!
|
|
3529
|
+
|
|
3530
|
+
"""Wartość jako String (parse zgodnie z type)"""
|
|
3531
|
+
value: String!
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
"""
|
|
3535
|
+
Typy wartości — STRING (max 255 chars), TEXT (no cap), INTEGER, DECIMAL, BOOLEAN ("true"/"false"), JSON (stringified), DATE (YYYY-MM-DD), DATE_TIME (ISO 8601), URL (http(s)://...), COLOR (#RRGGBB / #RRGGBBAA hex).
|
|
3536
|
+
"""
|
|
3537
|
+
enum MetaPropertyValueType {
|
|
3538
|
+
BOOLEAN
|
|
3539
|
+
COLOR
|
|
3540
|
+
DATE
|
|
3541
|
+
DATE_TIME
|
|
3542
|
+
DECIMAL
|
|
3543
|
+
INTEGER
|
|
3544
|
+
JSON
|
|
3545
|
+
STRING
|
|
3546
|
+
TEXT
|
|
3547
|
+
URL
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3415
3550
|
"""Monetary value with currency"""
|
|
3416
3551
|
type Money {
|
|
3417
3552
|
"""Decimal money amount"""
|
|
@@ -3509,6 +3644,12 @@ type Mutation {
|
|
|
3509
3644
|
"""Logout customer (clears auth cookie)"""
|
|
3510
3645
|
customerLogout: CustomerLogoutPayload!
|
|
3511
3646
|
|
|
3647
|
+
"""Bulk upsert własnych meta properties klienta (Bearer token wymagany)"""
|
|
3648
|
+
customerMetaPropertiesSet(properties: [MetaPropertyInput!]!): MetaPropertiesSetPayload!
|
|
3649
|
+
|
|
3650
|
+
"""Usuwa pojedyncze meta property klienta po (namespace, key)"""
|
|
3651
|
+
customerMetaPropertyDelete(key: String!, namespace: String!): MetaPropertyDeletePayload!
|
|
3652
|
+
|
|
3512
3653
|
"""Refresh access token"""
|
|
3513
3654
|
customerRefreshToken: CustomerRefreshTokenPayload!
|
|
3514
3655
|
|
|
@@ -3624,6 +3765,12 @@ type Order implements Node {
|
|
|
3624
3765
|
"""Line items count"""
|
|
3625
3766
|
itemCount: Int!
|
|
3626
3767
|
|
|
3768
|
+
"""Lista meta properties (Relay Connection)"""
|
|
3769
|
+
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
3770
|
+
|
|
3771
|
+
"""Pojedyncze meta property po (namespace, key)"""
|
|
3772
|
+
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
3773
|
+
|
|
3627
3774
|
"""Order number (human-readable)"""
|
|
3628
3775
|
orderNumber: String!
|
|
3629
3776
|
|
|
@@ -3903,9 +4050,9 @@ type Product implements Node {
|
|
|
3903
4050
|
averageRating: Float
|
|
3904
4051
|
|
|
3905
4052
|
"""
|
|
3906
|
-
|
|
4053
|
+
Wszystkie kategorie do których produkt należy (M2M przez ProductCategory junction). Posortowane po junction.sortOrder ASC. Empty list gdy produkt nie jest w żadnej kategorii.
|
|
3907
4054
|
"""
|
|
3908
|
-
|
|
4055
|
+
categories: [Category!]!
|
|
3909
4056
|
|
|
3910
4057
|
"""
|
|
3911
4058
|
Compare-at price range (Money pair). Null gdy żaden variant nie ma compareAtPrice.
|
|
@@ -3944,6 +4091,12 @@ type Product implements Node {
|
|
|
3944
4091
|
"""
|
|
3945
4092
|
isPurchasable: Boolean!
|
|
3946
4093
|
|
|
4094
|
+
"""Lista meta properties — Storefront API filtruje isPrivate=false"""
|
|
4095
|
+
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
4096
|
+
|
|
4097
|
+
"""Pojedyncze meta property — Storefront API filtruje isPrivate=false"""
|
|
4098
|
+
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
4099
|
+
|
|
3947
4100
|
"""
|
|
3948
4101
|
Per-product option definitions (Color, Size, …) with their available values. Use these to build a variant picker without aggregating `selectedOptions` manually.
|
|
3949
4102
|
"""
|
|
@@ -3957,6 +4110,11 @@ type Product implements Node {
|
|
|
3957
4110
|
"""
|
|
3958
4111
|
priceRangeWithConversion: ConvertedPriceRange
|
|
3959
4112
|
|
|
4113
|
+
"""
|
|
4114
|
+
Domyślna kategoria dla breadcrumb/nav (= categories[0] po sortOrder). Null gdy produkt nie jest w żadnej kategorii.
|
|
4115
|
+
"""
|
|
4116
|
+
primaryCategory: Category
|
|
4117
|
+
|
|
3960
4118
|
"""Similar products recommendations"""
|
|
3961
4119
|
recommendations(first: Int = 4): ProductRecommendations
|
|
3962
4120
|
|
|
@@ -4143,11 +4301,6 @@ input ProductFilter {
|
|
|
4143
4301
|
"""Filter by variant price range"""
|
|
4144
4302
|
price: PriceRangeFilter
|
|
4145
4303
|
|
|
4146
|
-
"""
|
|
4147
|
-
Filter by product category (free-text classification, stored on Product.category). Distinct from `category: CategoryFilter` which selects by structured Category entity.
|
|
4148
|
-
"""
|
|
4149
|
-
productCategory: String
|
|
4150
|
-
|
|
4151
4304
|
"""
|
|
4152
4305
|
Filter by product type enum (PHYSICAL/DIGITAL/SERVICE/SUBSCRIPTION/GIFT_CARD). Distinct from `productCategory` (free-text classification).
|
|
4153
4306
|
"""
|
|
@@ -4345,6 +4498,12 @@ type ProductVariant {
|
|
|
4345
4498
|
"""Whether variant is available for purchase"""
|
|
4346
4499
|
isAvailable: Boolean!
|
|
4347
4500
|
|
|
4501
|
+
"""Lista meta properties — Storefront API filtruje isPrivate=false"""
|
|
4502
|
+
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
4503
|
+
|
|
4504
|
+
"""Pojedyncze meta property — Storefront API filtruje isPrivate=false"""
|
|
4505
|
+
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
4506
|
+
|
|
4348
4507
|
"""Variant price (Money). Default field — industry-standard schema."""
|
|
4349
4508
|
price: Money!
|
|
4350
4509
|
|
|
@@ -4478,20 +4637,30 @@ type Query {
|
|
|
4478
4637
|
"""Get cart by ID"""
|
|
4479
4638
|
cart(id: ID!): Cart
|
|
4480
4639
|
|
|
4481
|
-
"""
|
|
4640
|
+
"""
|
|
4641
|
+
Lista kategorii (Relay Connection) z opcjonalnym filtrem rootsOnly/parentId
|
|
4642
|
+
"""
|
|
4482
4643
|
categories(
|
|
4483
|
-
"""
|
|
4644
|
+
"""Forward pagination cursor (after this element)"""
|
|
4484
4645
|
after: String
|
|
4485
4646
|
|
|
4486
|
-
"""
|
|
4487
|
-
|
|
4647
|
+
"""Backward pagination cursor (before this element)"""
|
|
4648
|
+
before: String
|
|
4649
|
+
|
|
4650
|
+
"""Forward pagination — first N elements"""
|
|
4651
|
+
first: Int
|
|
4488
4652
|
|
|
4489
|
-
"""
|
|
4653
|
+
"""Backward pagination — last N elements"""
|
|
4654
|
+
last: Int
|
|
4655
|
+
|
|
4656
|
+
"""
|
|
4657
|
+
Filter by parent category ID — używaj `null` semantically dla "roots" via `rootsOnly` flag
|
|
4658
|
+
"""
|
|
4490
4659
|
parentId: ID
|
|
4491
4660
|
|
|
4492
|
-
"""
|
|
4661
|
+
"""Tylko root categories (parentId IS NULL)"""
|
|
4493
4662
|
rootsOnly: Boolean! = false
|
|
4494
|
-
):
|
|
4663
|
+
): CategoryConnection!
|
|
4495
4664
|
|
|
4496
4665
|
"""Get category by ID or slug"""
|
|
4497
4666
|
category(
|