Skip to content

Commit 541abf1

Browse files
authored
Merge pull request #623 from Shopify/mg/05-07-update_validation_function_templates
Update Validation function templates
2 parents 299102f + aebb492 commit 541abf1

16 files changed

+480
-181
lines changed

checkout/javascript/cart-checkout-validation/default/schema.graphql

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ Scale the Functions resource limits based on the field's length.
1313
"""
1414
directive @scaleLimits(rate: Float!) on FIELD_DEFINITION
1515

16+
"""
17+
Requires that exactly one field must be supplied and that field must not be `null`.
18+
"""
19+
directive @oneOf on INPUT_OBJECT
20+
1621
"""
1722
A custom property. Attributes are used to store additional information about a Shopify resource, such as
1823
products, customers, or orders. Attributes are stored as key-value pairs.
@@ -352,6 +357,27 @@ type CartLineCost {
352357
totalAmount: MoneyV2!
353358
}
354359

360+
"""
361+
The fetch target result. Your Function must return this data structure when generating the request.
362+
"""
363+
input CartValidationsGenerateFetchResult {
364+
"""
365+
The attributes associated with an HTTP request.
366+
"""
367+
request: HttpRequest
368+
}
369+
370+
"""
371+
The output of the Function run target. The object contains the validation errors
372+
that display to customers and prevent them from proceeding through checkout.
373+
"""
374+
input CartValidationsGenerateRunResult {
375+
"""
376+
The ordered list of operations to apply.
377+
"""
378+
operations: [Operation!]!
379+
}
380+
355381
"""
356382
Whether the product is in the specified collection.
357383
@@ -3012,9 +3038,9 @@ type HttpResponse {
30123038
headers: [HttpResponseHeader!]! @deprecated(reason: "Use `header` instead.")
30133039

30143040
"""
3015-
The HTTP response body parsed as JSON.
3016-
If the body is valid JSON, it will be parsed and returned as a JSON object.
3017-
If parsing fails, then raw body is returned as a string.
3041+
The HTTP response body parsed as JSON.
3042+
If the body is valid JSON, it will be parsed and returned as a JSON object.
3043+
If parsing fails, then raw body is returned as a string.
30183044
Use this field when you expect the response to be JSON, or when you're dealing
30193045
with mixed response types, meaning both JSON and non-JSON.
30203046
Using this field reduces function instruction consumption and ensures that the data is formatted in logs.
@@ -3081,7 +3107,7 @@ type Input {
30813107
your fetch target, and that is passed as input to the run target. For more
30823108
information, refer to [network access for Shopify Functions](https://shopify.dev/docs/apps/build/functions/input-output/network-access).
30833109
"""
3084-
fetchResult: HttpResponse @restrictTarget(only: ["purchase.validation.run"])
3110+
fetchResult: HttpResponse @restrictTarget(only: ["purchase.validation.run", "cart.validations.generate.run"])
30853111

30863112
"""
30873113
The regional and language settings that determine how the Function
@@ -3955,7 +3981,7 @@ type Localization {
39553981
"""
39563982
The market of the active localized experience.
39573983
"""
3958-
market: Market!
3984+
market: Market! @deprecated(reason: "This `market` field will be removed in a future version of the API.")
39593985
}
39603986

39613987
"""
@@ -4223,7 +4249,7 @@ type MailingAddress {
42234249
"""
42244250
The market of the address.
42254251
"""
4226-
market: Market
4252+
market: Market @deprecated(reason: "This `market` field will be removed in a future version of the API.")
42274253

42284254
"""
42294255
The full name of the customer, based on firstName and lastName.
@@ -4382,6 +4408,26 @@ type MoneyV2 {
43824408
The root mutation for the API.
43834409
"""
43844410
type MutationRoot {
4411+
"""
4412+
Handles the Function result for the cart.validations.generate.fetch target.
4413+
"""
4414+
cartValidationsGenerateFetch(
4415+
"""
4416+
The result of the Function.
4417+
"""
4418+
result: CartValidationsGenerateFetchResult!
4419+
): Void!
4420+
4421+
"""
4422+
Handles the Function result for the cart.validations.generate.run target.
4423+
"""
4424+
cartValidationsGenerateRun(
4425+
"""
4426+
The result of the Function.
4427+
"""
4428+
result: CartValidationsGenerateRunResult!
4429+
): Void!
4430+
43854431
"""
43864432
Handles the Function result for the purchase.validation.fetch target.
43874433
"""
@@ -4413,6 +4459,16 @@ type MutationRoot {
44134459
): Void!
44144460
}
44154461

4462+
"""
4463+
An operation to apply.
4464+
"""
4465+
input Operation @oneOf {
4466+
"""
4467+
Add a performed validation.
4468+
"""
4469+
validationAdd: ValidationAddOperation
4470+
}
4471+
44164472
"""
44174473
The goods and services that merchants offer to customers. Products can include details such as
44184474
title, vendor, and custom data stored in [metafields](https://shopify.dev/docs/apps/build/custom-data).
@@ -4802,6 +4858,32 @@ type Validation implements HasMetafields {
48024858
): Metafield
48034859
}
48044860

4861+
"""
4862+
Add a performed validation.
4863+
"""
4864+
input ValidationAddOperation {
4865+
"""
4866+
Errors.
4867+
"""
4868+
errors: [ValidationError!]!
4869+
}
4870+
4871+
"""
4872+
A Function error for a path.
4873+
"""
4874+
input ValidationError {
4875+
"""
4876+
Returns a message describing the error.
4877+
"""
4878+
message: String!
4879+
4880+
"""
4881+
Specifies the path/target for use by the UI. See [Supported checkout field targets](https://shopify.dev/docs/api/functions/reference/cart-checkout-validation/graphql#supported-checkout-field-targets)
4882+
for a list of supported targets.
4883+
"""
4884+
target: String!
4885+
}
4886+
48054887
"""
48064888
A void type that can be used to return a null value from a mutation.
48074889
"""
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
api_version = "2025-01"
1+
api_version = "2025-07"
22

33
[[extensions]]
44
name = "t:name"
@@ -8,11 +8,10 @@ type = "function"
88
description = "t:description"
99

1010
[[extensions.targeting]]
11-
target = "purchase.validation.run"
12-
input_query = "src/run.graphql"
13-
export = "run"
11+
target = "cart.validations.generate.run"
12+
input_query = "src/cart_validations_generate_run.graphql"
13+
export = "cart-validations-generate-run"
1414

1515
[extensions.build]
1616
command = ""
1717
path = "dist/function.wasm"
18-

checkout/javascript/cart-checkout-validation/default/src/run.graphql.liquid renamed to checkout/javascript/cart-checkout-validation/default/src/cart_validations_generate_run.graphql.liquid

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
query RunInput {
1+
query CartValidationsGenerateRunInput {
22
cart {
33
lines {
44
quantity
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{%- if flavor contains "vanilla-js" -%}
2+
// @ts-check
3+
4+
/**
5+
* @typedef {import("../generated/api").CartValidationsGenerateRunInput} CartValidationsGenerateRunInput
6+
* @typedef {import("../generated/api").CartValidationsGenerateRunResult} CartValidationsGenerateRunResult
7+
*/
8+
9+
/**
10+
* @param {CartValidationsGenerateRunInput} input
11+
* @returns {CartValidationsGenerateRunResult}
12+
*/
13+
export function cartValidationsGenerateRun(input) {
14+
const errors = input.cart.lines
15+
.filter(({ quantity }) => quantity > 1)
16+
.map(() => ({
17+
message: "Not possible to order more than one of each",
18+
target: "$.cart"
19+
}));
20+
21+
const operations = [
22+
{
23+
validationAdd: {
24+
errors
25+
},
26+
},
27+
];
28+
29+
return { operations };
30+
};
31+
{%- elsif flavor contains "typescript" -%}
32+
import type {
33+
CartValidationsGenerateRunInput,
34+
CartValidationsGenerateRunResult,
35+
ValidationError,
36+
} from "../generated/api";
37+
38+
export function cartValidationsGenerateRun(input: CartValidationsGenerateRunInput): CartValidationsGenerateRunResult {
39+
const errors: ValidationError[] = input.cart.lines
40+
.filter(({ quantity }) => quantity > 1)
41+
.map(() => ({
42+
message: "Not possible to order more than one of each",
43+
target: "$.cart"
44+
}));
45+
46+
const operations = [
47+
{
48+
validationAdd: {
49+
errors
50+
},
51+
},
52+
];
53+
54+
return { operations };
55+
};
56+
{%- endif -%}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
{%- if flavor contains "vanilla-js" -%}
2+
import { describe, it, expect } from 'vitest';
3+
import { cartValidationsGenerateRun } from './cart_validations_generate_run';
4+
5+
/**
6+
* @typedef {import("../generated/api").CartValidationsGenerateRunResult} CartValidationsGenerateRunResult
7+
*/
8+
9+
describe('cart checkout validation function', () => {
10+
it('returns an error when quantity exceeds one', () => {
11+
const result = cartValidationsGenerateRun({
12+
cart: {
13+
lines: [
14+
{
15+
quantity: 3
16+
}
17+
]
18+
}
19+
});
20+
const expected = /** @type {CartValidationsGenerateRunResult} */ ({
21+
operations: [
22+
{
23+
validationAdd: {
24+
errors: [
25+
{
26+
message: "Not possible to order more than one of each",
27+
target: "$.cart"
28+
}
29+
]
30+
}
31+
}
32+
]
33+
});
34+
35+
expect(result).toEqual(expected);
36+
});
37+
38+
it('returns no errors when quantity is one', () => {
39+
const result = cartValidationsGenerateRun({
40+
cart: {
41+
lines: [
42+
{
43+
quantity: 1
44+
}
45+
]
46+
}
47+
});
48+
const expected = /** @type {CartValidationsGenerateRunResult} */ ({
49+
operations: [
50+
{
51+
validationAdd: {
52+
errors: []
53+
}
54+
}
55+
]
56+
});
57+
58+
expect(result).toEqual(expected);
59+
});
60+
});
61+
62+
{%- elsif flavor contains "typescript" -%}
63+
import { describe, it, expect } from 'vitest';
64+
import { cartValidationsGenerateRun } from './cart_validations_generate_run';
65+
import { CartValidationsGenerateRunResult } from "../generated/api";
66+
67+
describe('cart checkout validation function', () => {
68+
it('returns an error when quantity exceeds one', () => {
69+
const result = cartValidationsGenerateRun({
70+
cart: {
71+
lines: [
72+
{
73+
quantity: 3
74+
}
75+
]
76+
}
77+
});
78+
const expected: CartValidationsGenerateRunResult = {
79+
operations: [
80+
{
81+
validationAdd: {
82+
errors: [
83+
{
84+
message: "Not possible to order more than one of each",
85+
target: "$.cart"
86+
}
87+
]
88+
}
89+
}
90+
]
91+
};
92+
93+
expect(result).toEqual(expected);
94+
});
95+
96+
it('returns no errors when quantity is one', () => {
97+
const result = cartValidationsGenerateRun({
98+
cart: {
99+
lines: [
100+
{
101+
quantity: 1
102+
}
103+
]
104+
}
105+
});
106+
const expected: CartValidationsGenerateRunResult = {
107+
operations: [
108+
{
109+
validationAdd: {
110+
errors: []
111+
}
112+
}
113+
]
114+
};
115+
116+
expect(result).toEqual(expected);
117+
});
118+
});
119+
{%- endif -%}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './run';
1+
export * from './cart_validations_generate_run';

0 commit comments

Comments
 (0)