diff --git a/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/input.graphql b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/input.graphql new file mode 100644 index 00000000..9f10c4b5 --- /dev/null +++ b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/input.graphql @@ -0,0 +1,27 @@ +query Input { + cart { + lines { + id + quantity + merchandise { + __typename + ... on ProductVariant { + id + title + componentReferences: metafield( + namespace: "bundles" + key: "component_references" + ) { + value + } + componentQuantities: metafield( + namespace: "bundles" + key: "component_quantities" + ) { + value + } + } + } + } + } +} diff --git a/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/package.json b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/package.json new file mode 100644 index 00000000..42446d18 --- /dev/null +++ b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/package.json @@ -0,0 +1,31 @@ +{ + "name": "test-demo", + "version": "0.0.1", + "license": "UNLICENSED", + "scripts": { + "shopify": "npm exec -- shopify", + "typegen": "npm exec -- shopify app function typegen", + "build": "npm exec -- shopify app function build", + "preview": "npm exec -- shopify app function run", + "test": "vitest" + }, + "codegen": { + "schema": "schema.graphql", + "documents": "input.graphql", + "generates": { + "./generated/api.ts": { + "plugins": [ + "typescript", + "typescript-operations" + ] + } + } + }, + "devDependencies": { + "vitest": "^0.29.8" + }, + "dependencies": { + "@shopify/shopify_function": "0.0.3", + "javy": "0.1.0" + } +} diff --git a/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/schema.graphql b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/schema.graphql new file mode 100644 index 00000000..232cad10 --- /dev/null +++ b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/schema.graphql @@ -0,0 +1,1806 @@ +schema { + query: Input + mutation: MutationRoot +} + +""" +Exactly one field of input must be provided, and all others omitted. +""" +directive @oneOf on INPUT_OBJECT + +""" +Represents a generic custom attribute. +""" +type Attribute { + """ + Key or name of the attribute. + """ + key: String! + + """ + Value of the attribute. + """ + value: String +} + +""" +Represents information about the buyer that is interacting with the cart. +""" +type BuyerIdentity { + """ + The customer associated with the cart. + """ + customer: Customer + + """ + The email address of the buyer that is interacting with the cart. + """ + email: String + + """ + Whether the buyer authenticated with a customer account. + """ + isAuthenticated: Boolean! + + """ + The phone number of the buyer that is interacting with the cart. + """ + phone: String + + """ + The purchasing company associated with the cart. + """ + purchasingCompany: PurchasingCompany +} + +""" +A cart represents the merchandise that a buyer intends to purchase, and the cost associated with the cart. +""" +type Cart { + """ + The attributes associated with the cart. Attributes are represented as key-value pairs. + """ + attribute( + """ + The key of the attribute to retrieve. + """ + key: String + ): Attribute + + """ + Information about the buyer that is interacting with the cart. + """ + buyerIdentity: BuyerIdentity + + """ + A list of lines containing information about the items the customer intends to purchase. + """ + lines: [CartLine!]! +} + +""" +Represents information about the merchandise in the cart. +""" +type CartLine { + """ + Retrieve a cart line attribute by key. + + Cart line attributes are also known as line item properties in Liquid. + """ + attribute( + """ + The key of the attribute to retrieve. + """ + key: String + ): Attribute + + """ + The cost of the merchandise line that the buyer will pay at checkout. + """ + cost: CartLineCost! + + """ + The ID of the cart line. + """ + id: ID! + + """ + The merchandise that the buyer intends to purchase. + """ + merchandise: Merchandise! + + """ + The quantity of the merchandise that the customer intends to purchase. + """ + quantity: Int! + + """ + The selling plan associated with the cart line and the effect that each + selling plan has on variants when they're purchased. + """ + sellingPlanAllocation: SellingPlanAllocation +} + +""" +The cost of the merchandise line that the buyer will pay at checkout. +""" +type CartLineCost { + """ + The amount of the merchandise line. + """ + amountPerQuantity: MoneyV2! + + """ + The compare at amount of the merchandise line. + """ + compareAtAmountPerQuantity: MoneyV2 + + """ + The cost of the merchandise line before line-level discounts. + """ + subtotalAmount: MoneyV2! + + """ + The total cost of the merchandise line. + """ + totalAmount: MoneyV2! +} + +input CartLineInput { + """ + The ID of the cart line. + """ + cartLineId: ID! + + """ + The quantity of the cart line to be merged.The max quantity is 50. + """ + quantity: Int! +} + +""" +An operation to apply to the Cart. +""" +input CartOperation @oneOf { + expand: ExpandOperation + merge: MergeOperation +} + +""" +A customization which applies cart transformations to the merchandise lines. +""" +type CartTransform implements HasMetafields { + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield +} + +""" +Represents whether the product is a member of the given collection. +""" +type CollectionMembership { + """ + The ID of the collection. + """ + collectionId: ID! + + """ + Whether the product is a member of the collection. + """ + isMember: Boolean! +} + +""" +Represents information about a company which is also a customer of the shop. +""" +type Company implements HasMetafields { + """ + The date and time ([ISO 8601 format](http://en.wikipedia.org/wiki/ISO_8601)) at which the company was created in Shopify. + """ + createdAt: DateTime! + + """ + A unique externally-supplied ID for the company. + """ + externalId: String + + """ + The ID of the company. + """ + id: ID! + + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield + + """ + The name of the company. + """ + name: String! + + """ + The date and time ([ISO 8601 format](http://en.wikipedia.org/wiki/ISO_8601)) at which the company was last modified. + """ + updatedAt: DateTime! +} + +""" +A company's main point of contact. +""" +type CompanyContact { + """ + The date and time ([ISO 8601 format](http://en.wikipedia.org/wiki/ISO_8601)) + at which the company contact was created in Shopify. + """ + createdAt: DateTime! + + """ + The ID of the company. + """ + id: ID! + + """ + The company contact's locale (language). + """ + locale: String + + """ + The company contact's job title. + """ + title: String + + """ + The date and time ([ISO 8601 format](http://en.wikipedia.org/wiki/ISO_8601)) + at which the company contact was last modified. + """ + updatedAt: DateTime! +} + +""" +A company's location. +""" +type CompanyLocation implements HasMetafields { + """ + The date and time ([ISO 8601 format](http://en.wikipedia.org/wiki/ISO_8601)) + at which the company location was created in Shopify. + """ + createdAt: DateTime! + + """ + A unique externally-supplied ID for the company. + """ + externalId: String + + """ + The ID of the company. + """ + id: ID! + + """ + The preferred locale of the company location. + """ + locale: String + + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield + + """ + The name of the company location. + """ + name: String! + + """ + The date and time ([ISO 8601 format](http://en.wikipedia.org/wiki/ISO_8601)) + at which the company location was last modified. + """ + updatedAt: DateTime! +} + +""" +The three-letter currency codes that represent the world currencies used in +stores. These include standard ISO 4217 codes, legacy codes, +and non-standard codes. +""" +enum CurrencyCode { + """ + United Arab Emirates Dirham (AED). + """ + AED + + """ + Afghan Afghani (AFN). + """ + AFN + + """ + Albanian Lek (ALL). + """ + ALL + + """ + Armenian Dram (AMD). + """ + AMD + + """ + Netherlands Antillean Guilder. + """ + ANG + + """ + Angolan Kwanza (AOA). + """ + AOA + + """ + Argentine Pesos (ARS). + """ + ARS + + """ + Australian Dollars (AUD). + """ + AUD + + """ + Aruban Florin (AWG). + """ + AWG + + """ + Azerbaijani Manat (AZN). + """ + AZN + + """ + Bosnia and Herzegovina Convertible Mark (BAM). + """ + BAM + + """ + Barbadian Dollar (BBD). + """ + BBD + + """ + Bangladesh Taka (BDT). + """ + BDT + + """ + Bulgarian Lev (BGN). + """ + BGN + + """ + Bahraini Dinar (BHD). + """ + BHD + + """ + Burundian Franc (BIF). + """ + BIF + + """ + Bermudian Dollar (BMD). + """ + BMD + + """ + Brunei Dollar (BND). + """ + BND + + """ + Bolivian Boliviano (BOB). + """ + BOB + + """ + Brazilian Real (BRL). + """ + BRL + + """ + Bahamian Dollar (BSD). + """ + BSD + + """ + Bhutanese Ngultrum (BTN). + """ + BTN + + """ + Botswana Pula (BWP). + """ + BWP + + """ + Belarusian Ruble (BYN). + """ + BYN + + """ + Belarusian Ruble (BYR). + """ + BYR @deprecated(reason: "`BYR` is deprecated. Use `BYN` available from version `2021-01` onwards instead.") + + """ + Belize Dollar (BZD). + """ + BZD + + """ + Canadian Dollars (CAD). + """ + CAD + + """ + Congolese franc (CDF). + """ + CDF + + """ + Swiss Francs (CHF). + """ + CHF + + """ + Chilean Peso (CLP). + """ + CLP + + """ + Chinese Yuan Renminbi (CNY). + """ + CNY + + """ + Colombian Peso (COP). + """ + COP + + """ + Costa Rican Colones (CRC). + """ + CRC + + """ + Cape Verdean escudo (CVE). + """ + CVE + + """ + Czech Koruny (CZK). + """ + CZK + + """ + Djiboutian Franc (DJF). + """ + DJF + + """ + Danish Kroner (DKK). + """ + DKK + + """ + Dominican Peso (DOP). + """ + DOP + + """ + Algerian Dinar (DZD). + """ + DZD + + """ + Egyptian Pound (EGP). + """ + EGP + + """ + Eritrean Nakfa (ERN). + """ + ERN + + """ + Ethiopian Birr (ETB). + """ + ETB + + """ + Euro (EUR). + """ + EUR + + """ + Fijian Dollars (FJD). + """ + FJD + + """ + Falkland Islands Pounds (FKP). + """ + FKP + + """ + United Kingdom Pounds (GBP). + """ + GBP + + """ + Georgian Lari (GEL). + """ + GEL + + """ + Ghanaian Cedi (GHS). + """ + GHS + + """ + Gibraltar Pounds (GIP). + """ + GIP + + """ + Gambian Dalasi (GMD). + """ + GMD + + """ + Guinean Franc (GNF). + """ + GNF + + """ + Guatemalan Quetzal (GTQ). + """ + GTQ + + """ + Guyanese Dollar (GYD). + """ + GYD + + """ + Hong Kong Dollars (HKD). + """ + HKD + + """ + Honduran Lempira (HNL). + """ + HNL + + """ + Croatian Kuna (HRK). + """ + HRK + + """ + Haitian Gourde (HTG). + """ + HTG + + """ + Hungarian Forint (HUF). + """ + HUF + + """ + Indonesian Rupiah (IDR). + """ + IDR + + """ + Israeli New Shekel (NIS). + """ + ILS + + """ + Indian Rupees (INR). + """ + INR + + """ + Iraqi Dinar (IQD). + """ + IQD + + """ + Iranian Rial (IRR). + """ + IRR + + """ + Icelandic Kronur (ISK). + """ + ISK + + """ + Jersey Pound. + """ + JEP + + """ + Jamaican Dollars (JMD). + """ + JMD + + """ + Jordanian Dinar (JOD). + """ + JOD + + """ + Japanese Yen (JPY). + """ + JPY + + """ + Kenyan Shilling (KES). + """ + KES + + """ + Kyrgyzstani Som (KGS). + """ + KGS + + """ + Cambodian Riel. + """ + KHR + + """ + Kiribati Dollar (KID). + """ + KID + + """ + Comorian Franc (KMF). + """ + KMF + + """ + South Korean Won (KRW). + """ + KRW + + """ + Kuwaiti Dinar (KWD). + """ + KWD + + """ + Cayman Dollars (KYD). + """ + KYD + + """ + Kazakhstani Tenge (KZT). + """ + KZT + + """ + Laotian Kip (LAK). + """ + LAK + + """ + Lebanese Pounds (LBP). + """ + LBP + + """ + Sri Lankan Rupees (LKR). + """ + LKR + + """ + Liberian Dollar (LRD). + """ + LRD + + """ + Lesotho Loti (LSL). + """ + LSL + + """ + Lithuanian Litai (LTL). + """ + LTL + + """ + Latvian Lati (LVL). + """ + LVL + + """ + Libyan Dinar (LYD). + """ + LYD + + """ + Moroccan Dirham. + """ + MAD + + """ + Moldovan Leu (MDL). + """ + MDL + + """ + Malagasy Ariary (MGA). + """ + MGA + + """ + Macedonia Denar (MKD). + """ + MKD + + """ + Burmese Kyat (MMK). + """ + MMK + + """ + Mongolian Tugrik. + """ + MNT + + """ + Macanese Pataca (MOP). + """ + MOP + + """ + Mauritanian Ouguiya (MRU). + """ + MRU + + """ + Mauritian Rupee (MUR). + """ + MUR + + """ + Maldivian Rufiyaa (MVR). + """ + MVR + + """ + Malawian Kwacha (MWK). + """ + MWK + + """ + Mexican Pesos (MXN). + """ + MXN + + """ + Malaysian Ringgits (MYR). + """ + MYR + + """ + Mozambican Metical. + """ + MZN + + """ + Namibian Dollar. + """ + NAD + + """ + Nigerian Naira (NGN). + """ + NGN + + """ + Nicaraguan Córdoba (NIO). + """ + NIO + + """ + Norwegian Kroner (NOK). + """ + NOK + + """ + Nepalese Rupee (NPR). + """ + NPR + + """ + New Zealand Dollars (NZD). + """ + NZD + + """ + Omani Rial (OMR). + """ + OMR + + """ + Panamian Balboa (PAB). + """ + PAB + + """ + Peruvian Nuevo Sol (PEN). + """ + PEN + + """ + Papua New Guinean Kina (PGK). + """ + PGK + + """ + Philippine Peso (PHP). + """ + PHP + + """ + Pakistani Rupee (PKR). + """ + PKR + + """ + Polish Zlotych (PLN). + """ + PLN + + """ + Paraguayan Guarani (PYG). + """ + PYG + + """ + Qatari Rial (QAR). + """ + QAR + + """ + Romanian Lei (RON). + """ + RON + + """ + Serbian dinar (RSD). + """ + RSD + + """ + Russian Rubles (RUB). + """ + RUB + + """ + Rwandan Franc (RWF). + """ + RWF + + """ + Saudi Riyal (SAR). + """ + SAR + + """ + Solomon Islands Dollar (SBD). + """ + SBD + + """ + Seychellois Rupee (SCR). + """ + SCR + + """ + Sudanese Pound (SDG). + """ + SDG + + """ + Swedish Kronor (SEK). + """ + SEK + + """ + Singapore Dollars (SGD). + """ + SGD + + """ + Saint Helena Pounds (SHP). + """ + SHP + + """ + Sierra Leonean Leone (SLL). + """ + SLL + + """ + Somali Shilling (SOS). + """ + SOS + + """ + Surinamese Dollar (SRD). + """ + SRD + + """ + South Sudanese Pound (SSP). + """ + SSP + + """ + Sao Tome And Principe Dobra (STD). + """ + STD @deprecated(reason: "`STD` is deprecated. Use `STN` available from version `2022-07` onwards instead.") + + """ + Sao Tome And Principe Dobra (STN). + """ + STN + + """ + Syrian Pound (SYP). + """ + SYP + + """ + Swazi Lilangeni (SZL). + """ + SZL + + """ + Thai baht (THB). + """ + THB + + """ + Tajikistani Somoni (TJS). + """ + TJS + + """ + Turkmenistani Manat (TMT). + """ + TMT + + """ + Tunisian Dinar (TND). + """ + TND + + """ + Tongan Pa'anga (TOP). + """ + TOP + + """ + Turkish Lira (TRY). + """ + TRY + + """ + Trinidad and Tobago Dollars (TTD). + """ + TTD + + """ + Taiwan Dollars (TWD). + """ + TWD + + """ + Tanzanian Shilling (TZS). + """ + TZS + + """ + Ukrainian Hryvnia (UAH). + """ + UAH + + """ + Ugandan Shilling (UGX). + """ + UGX + + """ + United States Dollars (USD). + """ + USD + + """ + Uruguayan Pesos (UYU). + """ + UYU + + """ + Uzbekistan som (UZS). + """ + UZS + + """ + Venezuelan Bolivares (VED). + """ + VED + + """ + Venezuelan Bolivares (VEF). + """ + VEF @deprecated(reason: "`VEF` is deprecated. Use `VES` available from version `2020-10` onwards instead.") + + """ + Venezuelan Bolivares (VES). + """ + VES + + """ + Vietnamese đồng (VND). + """ + VND + + """ + Vanuatu Vatu (VUV). + """ + VUV + + """ + Samoan Tala (WST). + """ + WST + + """ + Central African CFA Franc (XAF). + """ + XAF + + """ + East Caribbean Dollar (XCD). + """ + XCD + + """ + West African CFA franc (XOF). + """ + XOF + + """ + CFP Franc (XPF). + """ + XPF + + """ + Unrecognized currency. + """ + XXX + + """ + Yemeni Rial (YER). + """ + YER + + """ + South African Rand (ZAR). + """ + ZAR + + """ + Zambian Kwacha (ZMW). + """ + ZMW +} + +""" +A custom product. +""" +type CustomProduct { + """ + Whether the merchandise is a gift card. + """ + isGiftCard: Boolean! + + """ + Whether the merchandise requires shipping. + """ + requiresShipping: Boolean! + + """ + The localized title of the product in the customer’s locale. + """ + title: String! + + """ + The weight of the product variant in the unit system specified with `weight_unit`. + """ + weight: Float + + """ + Unit of measurement for weight. + """ + weightUnit: WeightUnit! +} + +""" +Represents a customer with the shop. +""" +type Customer implements HasMetafields { + """ + The total amount of money spent by the customer. Converted from the shop's + currency to the currency of the cart using a market rate. + """ + amountSpent: MoneyV2! + + """ + The customer’s name, email or phone number. + """ + displayName: String! + + """ + The customer’s email address. + """ + email: String + + """ + Whether the customer has any of the given tags. + """ + hasAnyTag( + """ + The tags to search for. + """ + tags: [String!]! = [] + ): Boolean! + + """ + Whether the customer has the given tags. + """ + hasTags( + """ + The tags to check. + """ + tags: [String!]! = [] + ): [HasTagResponse!]! + + """ + A unique identifier for the customer. + """ + id: ID! + + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield + + """ + The number of orders made by the customer. + """ + numberOfOrders: Int! +} + +""" +Represents an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)-encoded date and time string. +For example, 3:50 pm on September 7, 2019 in the time zone of UTC (Coordinated Universal Time) is +represented as `"2019-09-07T15:50:00Z`". +""" +scalar DateTime + +""" +A signed decimal number, which supports arbitrary precision and is serialized as a string. + +Example values: `"29.99"`, `"29.999"`. +""" +scalar Decimal + +input ExpandOperation { + """ + The cart line id to expand. + """ + cartLineId: ID! + + """ + The cart items to expand. + """ + expandedCartItems: [ExpandedItem!]! + + """ + The price adjustment to the group. + """ + price: PriceAdjustment +} + +input ExpandedItem { + """ + The merchandise id of the expanded item. + """ + merchandiseId: ID! + + """ + The quantity of the expanded item.The max quantity is 50. + """ + quantity: Int! +} + +""" +Transformations to apply to the Cart. +""" +input FunctionResult { + """ + Cart operations to run on Cart. + """ + operations: [CartOperation!]! +} + +""" +Represents a gate configuration. +""" +type GateConfiguration implements HasMetafields { + """ + An optional string identifier. + """ + appId: String + + """ + A non-unique string used to group gate configurations. + """ + handle: String + + """ + The ID of the gate configuration. + """ + id: ID! + + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield +} + +""" +Represents a connection from a subject to a gate configuration. +""" +type GateSubject { + """ + The bound gate configuration. + """ + configuration( + """ + The appId of the gate configurations to search for. + """ + appId: String @deprecated(reason: "Use GateSubject.handle to filter gates instead.") + ): GateConfiguration! + + """ + The ID of the gate subject. + """ + id: ID! +} + +""" +Gate subjects associated to the specified resource. +""" +interface HasGates { + """ + Returns active gate subjects bound to the resource. + """ + gates( + """ + The handle of the gate configurations to search for. + """ + handle: String + ): [GateSubject!]! +} + +""" +Represents information about the metafields associated to the specified resource. +""" +interface HasMetafields { + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield +} + +""" +Represents whether the current object has the given tag. +""" +type HasTagResponse { + """ + Whether the current object has the tag. + """ + hasTag: Boolean! + + """ + The tag. + """ + tag: String! +} + +""" +Represents a unique identifier, often used to refetch an object. +The ID type appears in a JSON response as a String, but it is not intended to be human-readable. + +Example value: `"gid://shopify/Product/10079785100"` +""" +scalar ID + +""" +The image of an object. +""" +input ImageInput { + """ + The URL of the image. + """ + url: URL! +} + +type Input { + """ + The cart. + """ + cart: Cart! + + """ + The CartTransform containing the function. + """ + cartTransform: CartTransform! +} + +""" +The merchandise to be purchased at checkout. +""" +union Merchandise = CustomProduct | ProductVariant + +input MergeOperation { + """ + The list of cart lines to merge. + """ + cartLines: [CartLineInput!]! + + """ + The image of the group. + """ + image: ImageInput + + """ + The product variant that models the group of lines. + """ + parentVariantId: ID! + + """ + The price adjustment to the group. + """ + price: PriceAdjustment + + """ + The name of the group of lines to merge. If it isn't specified, it will use the parent_variant' name. + """ + title: String +} + +""" +[Metafields](https://shopify.dev/apps/metafields) +enable you to attach additional information to a +Shopify resource, such as a [Product](https://shopify.dev/api/admin-graphql/latest/objects/product) +or a [Collection](https://shopify.dev/api/admin-graphql/latest/objects/collection). +For more information about the Shopify resources that you can attach metafields to, refer to +[HasMetafields](https://shopify.dev/api/admin/graphql/reference/common-objects/HasMetafields). +""" +type Metafield { + """ + The type of data that the metafield stores in the `value` field. + Refer to the list of [supported types](https://shopify.dev/apps/metafields/types). + """ + type: String! + + """ + The data to store in the metafield. The data is always stored as a string, regardless of the metafield's type. + """ + value: String! +} + +""" +A monetary value with currency. +""" +type MoneyV2 { + """ + Decimal money amount. + """ + amount: Decimal! + + """ + Currency of the money. + """ + currencyCode: CurrencyCode! +} + +""" +The root mutation for the API. +""" +type MutationRoot { + """ + Handles the function result. + """ + handleResult( + """ + The result of the function. + """ + result: FunctionResult! + ): Void! +} + +input PriceAdjustment { + """ + The percentage price decrease of the price adjustment. + """ + percentageDecrease: PriceAdjustmentValue +} + +input PriceAdjustmentValue { + """ + The value of the price adjustment. + """ + value: Decimal! +} + +""" +Represents a product. +""" +type Product implements HasGates & HasMetafields { + """ + Returns active gate subjects bound to the resource. + """ + gates( + """ + The handle of the gate configurations to search for. + """ + handle: String + ): [GateSubject!]! + + """ + A unique human-friendly string of the product's title. + """ + handle: String! + + """ + Whether the product has any of the given tags. + """ + hasAnyTag( + """ + The tags to check. + """ + tags: [String!]! = [] + ): Boolean! + + """ + Whether the product has the given tags. + """ + hasTags( + """ + The tags to check. + """ + tags: [String!]! = [] + ): [HasTagResponse!]! + + """ + A globally-unique identifier. + """ + id: ID! + + """ + Whether the product is in any of the given collections. + """ + inAnyCollection( + """ + The IDs of the collections to check. + """ + ids: [ID!]! = [] + ): Boolean! + + """ + Whether the product is in the given collections. + """ + inCollections( + """ + The IDs of the collections to check. + """ + ids: [ID!]! = [] + ): [CollectionMembership!]! + + """ + Whether the product is a gift card. + """ + isGiftCard: Boolean! + + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield + + """ + The product type specified by the merchant. + """ + productType: String + + """ + The localized title of the product in the customer’s locale. + """ + title: String! + + """ + The name of the product's vendor. + """ + vendor: String +} + +""" +Represents a product variant. +""" +type ProductVariant implements HasMetafields { + """ + A globally-unique identifier. + """ + id: ID! + + """ + Returns a metafield by namespace and key that belongs to the resource. + """ + metafield( + """ + The key for the metafield. + """ + key: String! + + """ + The namespace for the metafield. If omitted, the app-reserved namespace will be used. + """ + namespace: String + ): Metafield + + """ + The product that this variant belongs to. + """ + product: Product! + + """ + Whether the merchandise requires shipping. + """ + requiresShipping: Boolean! + + """ + An identifier for the product variant in the shop. Required in order to connect to a fulfillment service. + """ + sku: String + + """ + The localized title of the product variant in the customer’s locale. + """ + title: String + + """ + The weight of the product variant in the unit system specified with `weight_unit`. + """ + weight: Float + + """ + Unit of measurement for weight. + """ + weightUnit: WeightUnit! +} + +""" +Represents information about the buyer that is interacting with the cart. +""" +type PurchasingCompany { + """ + The company associated to the order or draft order. + """ + company: Company! + + """ + The company contact associated to the order or draft order. + """ + contact: CompanyContact + + """ + The company location associated to the order or draft order. + """ + location: CompanyLocation! +} + +""" +Represents how products and variants can be sold and purchased. +""" +type SellingPlan { + """ + The description of the selling plan. + """ + description: String + + """ + A globally-unique identifier. + """ + id: ID! + + """ + The name of the selling plan. For example, '6 weeks of prepaid granola, delivered weekly'. + """ + name: String! + + """ + Whether purchasing the selling plan will result in multiple deliveries. + """ + recurringDeliveries: Boolean! +} + +""" +Represents an association between a variant and a selling plan. Selling plan +allocations describe the options offered for each variant, and the price of the +variant when purchased with a selling plan. +""" +type SellingPlanAllocation { + """ + A list of price adjustments, with a maximum of two. When there are two, the + first price adjustment goes into effect at the time of purchase, while the + second one starts after a certain number of orders. A price adjustment + represents how a selling plan affects pricing when a variant is purchased with + a selling plan. Prices display in the customer's currency if the shop is + configured for it. + """ + priceAdjustments: [SellingPlanAllocationPriceAdjustment!]! + + """ + A representation of how products and variants can be sold and purchased. For + example, an individual selling plan could be '6 weeks of prepaid granola, + delivered weekly'. + """ + sellingPlan: SellingPlan! +} + +""" +The resulting prices for variants when they're purchased with a specific selling plan. +""" +type SellingPlanAllocationPriceAdjustment { + """ + The effective price for a single delivery. For example, for a prepaid + subscription plan that includes 6 deliveries at the price of $48.00, the per + delivery price is $8.00. + """ + perDeliveryPrice: MoneyV2! + + """ + The price of the variant when it's purchased with a selling plan For example, + for a prepaid subscription plan that includes 6 deliveries of $10.00 granola, + where the customer gets 20% off, the price is 6 x $10.00 x 0.80 = $48.00. + """ + price: MoneyV2! +} + +""" +Represents an [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and +[RFC 3987](https://datatracker.ietf.org/doc/html/rfc3987)-compliant URI string. + +For example, `"https://johns-apparel.myshopify.com"` is a valid URL. It includes a scheme (`https`) and a host +(`johns-apparel.myshopify.com`). +""" +scalar URL + +""" +A void type that can be used to return a null value from a mutation. +""" +scalar Void + +""" +Units of measurement for weight. +""" +enum WeightUnit { + """ + Metric system unit of mass. + """ + GRAMS + + """ + 1 kilogram equals 1000 grams. + """ + KILOGRAMS + + """ + Imperial system unit of mass. + """ + OUNCES + + """ + 1 pound equals 16 ounces. + """ + POUNDS +} \ No newline at end of file diff --git a/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/shopify.function.extension.toml b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/shopify.function.extension.toml new file mode 100644 index 00000000..f42f3798 --- /dev/null +++ b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/shopify.function.extension.toml @@ -0,0 +1,8 @@ +name = "test-demo" +type = "cart_transform" +title = "cart transform" +api_version = "unstable" + +[build] +command = "" +path = "dist/function.wasm" diff --git a/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/src/index.test.ts b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/src/index.test.ts new file mode 100644 index 00000000..dbd10846 --- /dev/null +++ b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/src/index.test.ts @@ -0,0 +1,16 @@ +import { describe, it, expect } from 'vitest'; +import cartTransform from './index'; +import { FunctionResult } from '../generated/api'; + +describe('cart transform function', () => { + it('returns no operations', () => { + const result = cartTransform({ + cart: { + lines: [] + } + }); + const expected: FunctionResult = { operations: [] }; + + expect(result).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/src/index.ts b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/src/index.ts new file mode 100644 index 00000000..0875fe06 --- /dev/null +++ b/sample-apps/bundles-cart-transform/extensions/cart-expand-ts/src/index.ts @@ -0,0 +1,83 @@ +import type { + InputQuery, + FunctionResult, + Scalars, + CartOperation, + ExpandOperation, +} from "../generated/api"; + +type RequireStrict = Required<{ + [K in keyof T]: NonNullable; +}>; + +type Merchandise = InputQuery["cart"]["lines"][number]["merchandise"]; +type ProductVariant = RequireStrict< + Extract +>; + +const NO_CHANGES: FunctionResult = { + operations: [], +}; + +export default (input: InputQuery): FunctionResult => { + const operations = input.cart.lines.reduce>( + (acc, cartLine) => { + const expandOperation = optionallyBuildExpandOperation(cartLine); + + if (expandOperation) { + return [...acc, { expand: expandOperation }]; + } + + return acc; + }, + [] + ); + + return operations.length > 0 ? { operations } : NO_CHANGES; +}; + +function optionallyBuildExpandOperation({ id: cartLineId, merchandise }): ExpandOperation | null { + if (canExpand(merchandise)) { + const componentReferences: Scalars["ID"][] = JSON.parse( + merchandise.componentReferences.value + ); + const componentQuantities: Scalars["Int"][] = JSON.parse( + merchandise.componentQuantities.value + ); + + if (!validateMetafields(componentReferences, componentQuantities)) { + throw new Error("Invalid bundle composition"); + } + + return { + cartLineId, + expandedCartItems: componentReferences.map((merchandiseId, index) => ({ + merchandiseId, + quantity: componentQuantities[index], + })), + }; + } + + return null; +} + +function canExpand(merchandise: Merchandise): merchandise is ProductVariant { + return ( + merchandise.__typename === "ProductVariant" && + !!merchandise.componentQuantities && + !!merchandise.componentReferences + ); +} + +/** + * Returns true if component references have matching quantities and there is at least one component. + */ +function validateMetafields( + componentReferences: Scalars["ID"][], + componentQuantities: Scalars["Int"][] +) { + return ( + componentReferences.length === componentQuantities.length && + componentReferences.length > 0 + ); +}