diff --git a/.changeset/plenty-roses-count.md b/.changeset/plenty-roses-count.md new file mode 100644 index 0000000000..59e6f9477f --- /dev/null +++ b/.changeset/plenty-roses-count.md @@ -0,0 +1,14 @@ +--- +'@shopify/shopify-app-session-storage-postgresql': patch +'@shopify/shopify-app-session-storage-dynamodb': patch +'@shopify/shopify-app-session-storage-mongodb': patch +'@shopify/shopify-app-session-storage-memory': patch +'@shopify/shopify-app-session-storage-prisma': patch +'@shopify/shopify-app-session-storage-sqlite': patch +'@shopify/shopify-app-session-storage-mysql': patch +'@shopify/shopify-app-session-storage-redis': patch +'@shopify/shopify-app-session-storage-kv': patch +'@shopify/shopify-app-session-storage': patch +--- + +Updated the dependency on `@shopify/shopify-api` diff --git a/.changeset/thin-feet-care.md b/.changeset/thin-feet-care.md new file mode 100644 index 0000000000..853f9e6077 --- /dev/null +++ b/.changeset/thin-feet-care.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-express': major +--- + +Updated `@shopify/shopify-api` to the latest major version. Please follow [the v9 migration guide](https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/migrating-to-v9.md) to update your app. diff --git a/.changeset/thirty-apples-think.md b/.changeset/thirty-apples-think.md new file mode 100644 index 0000000000..4441b16594 --- /dev/null +++ b/.changeset/thirty-apples-think.md @@ -0,0 +1,7 @@ +--- +'@shopify/shopify-app-remix': minor +--- + +Adding support for the new clients from `@shopify/admin-api-client` and `@shopify/storefront-api-client` that can leverage `@shopify/api-codegen-preset` to automatically type GraphQL operations using Codegen. + +For more information on how to add types to your queries, see [the `@shopify/api-codegen-preset` documentation](https://github.com/Shopify/shopify-api-js/tree/main/packages/api-codegen-preset). diff --git a/packages/shopify-app-express/package.json b/packages/shopify-app-express/package.json index beee9fc1f8..99a504400e 100644 --- a/packages/shopify-app-express/package.json +++ b/packages/shopify-app-express/package.json @@ -30,7 +30,7 @@ "Storefront API" ], "dependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2", "@shopify/shopify-app-session-storage-memory": "^2.0.2", "cookie-parser": "^1.4.6", diff --git a/packages/shopify-app-express/src/__tests__/integration/oauth.test.ts b/packages/shopify-app-express/src/__tests__/integration/oauth.test.ts index f6395d2851..bb9d29c599 100644 --- a/packages/shopify-app-express/src/__tests__/integration/oauth.test.ts +++ b/packages/shopify-app-express/src/__tests__/integration/oauth.test.ts @@ -335,7 +335,7 @@ function assertOAuthRequests( expect({ method: 'POST', url: `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/graphql.json`, - body: expect.stringContaining(query), + body: expect.objectContaining({query: expect.stringContaining(query)}), }).toMatchMadeHttpRequest(), ); diff --git a/packages/shopify-app-express/src/__tests__/integration/webhooks.test.ts b/packages/shopify-app-express/src/__tests__/integration/webhooks.test.ts index f74259ea27..6520d2975a 100644 --- a/packages/shopify-app-express/src/__tests__/integration/webhooks.test.ts +++ b/packages/shopify-app-express/src/__tests__/integration/webhooks.test.ts @@ -127,7 +127,9 @@ describe('webhook integration', () => { expect({ method: 'POST', url: `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/graphql.json`, - body: expect.stringContaining(query), + body: expect.objectContaining({ + query: expect.stringContaining(query), + }), }).toMatchMadeHttpRequest(), ); diff --git a/packages/shopify-app-express/src/__tests__/test-helper.ts b/packages/shopify-app-express/src/__tests__/test-helper.ts index e89bbf38f6..3a58eb5315 100644 --- a/packages/shopify-app-express/src/__tests__/test-helper.ts +++ b/packages/shopify-app-express/src/__tests__/test-helper.ts @@ -73,7 +73,7 @@ interface AssertHttpRequestParams { export function mockShopifyResponse(body: MockBody, init?: MockParams) { fetchMock.mockResponse( typeof body === 'string' ? body : JSON.stringify(body), - init, + mockResponseInit(init), ); } @@ -84,13 +84,22 @@ export function mockShopifyResponses( ([body, init]) => { const bodyString = typeof body === 'string' ? body : JSON.stringify(body); - return init ? [bodyString, init] : [bodyString, {}]; + return [bodyString, mockResponseInit(init)]; }, ); fetchMock.mockResponses(...parsedResponses); } +function mockResponseInit(init?: MockParams): MockParams { + const initObj = init ?? {}; + + return { + ...initObj, + headers: {'Content-Type': 'application/json', ...initObj.headers}, + }; +} + declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace jest { diff --git a/packages/shopify-app-express/src/auth/auth-callback.ts b/packages/shopify-app-express/src/auth/auth-callback.ts index e46e85154a..37ad7251f7 100644 --- a/packages/shopify-app-express/src/auth/auth-callback.ts +++ b/packages/shopify-app-express/src/auth/auth-callback.ts @@ -2,7 +2,7 @@ import {Request, Response} from 'express'; import { BotActivityDetected, CookieNotFound, - gdprTopics, + privacyTopics, InvalidOAuthError, Session, Shopify, @@ -83,7 +83,7 @@ async function registerWebhooks( } for (const response of responsesByTopic[topic]) { - if (!response.success && !gdprTopics.includes(topic)) { + if (!response.success && !privacyTopics.includes(topic)) { const result: any = response.result; if (result.errors) { diff --git a/packages/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts b/packages/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts index 646e8902b1..da6c3c4a32 100644 --- a/packages/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts +++ b/packages/shopify-app-express/src/middlewares/__tests__/ensure-installed-on-shop.test.ts @@ -167,7 +167,10 @@ describe('ensureInstalledOnShop', () => { )}`, ]; - mockShopifyResponse({}); + mockShopifyResponse({ + data: {}, + extensions: {}, + }); await shopify.config.sessionStorage.storeSession(session); await request(app) diff --git a/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts b/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts index 4e5f55fd68..686e0e7d70 100644 --- a/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts +++ b/packages/shopify-app-express/src/middlewares/__tests__/validate-authenticated-session.test.ts @@ -266,7 +266,10 @@ describe('validateAuthenticatedSession', () => { }); it('finds a session with the right cookie', async () => { - mockShopifyResponse({}); + mockShopifyResponse({ + data: {}, + extensions: {}, + }); const response = await request(app) .get('/test/shop?shop=my-shop.myshopify.io') diff --git a/packages/shopify-app-remix/docs/generated/generated_docs_data.json b/packages/shopify-app-remix/docs/generated/generated_docs_data.json index 8cfb28e0c6..eecf09ad23 100644 --- a/packages/shopify-app-remix/docs/generated/generated_docs_data.json +++ b/packages/shopify-app-remix/docs/generated/generated_docs_data.json @@ -296,7 +296,7 @@ "filePath": "/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { @@ -312,7 +312,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "/server/clients/admin/rest.ts", @@ -1547,7 +1547,7 @@ "filePath": "/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { @@ -1563,7 +1563,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "/server/clients/admin/rest.ts", @@ -1581,7 +1581,7 @@ "filePath": "/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Method for interacting with the Shopify Storefront GraphQL API\n\nIf you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n\n\n\n\n", "examples": [ { @@ -1597,7 +1597,7 @@ ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" } } } @@ -2254,14 +2254,14 @@ { "filePath": "/server/types.ts", "syntaxKind": "MethodSignature", - "name": "__@iterator@644", + "name": "__@iterator@716", "value": "() => IterableIterator", "description": "Iterator" }, { "filePath": "/server/types.ts", "syntaxKind": "MethodSignature", - "name": "__@unscopables@646", + "name": "__@unscopables@718", "value": "() => { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }", "description": "Returns an object whose properties have the value 'true'\r\nwhen they will be absent when used in a 'with' statement." }, @@ -2599,7 +2599,7 @@ "filePath": "/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { @@ -2615,7 +2615,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "/server/clients/admin/rest.ts", @@ -3212,7 +3212,7 @@ "filePath": "/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { @@ -3228,7 +3228,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "/server/clients/admin/rest.ts", @@ -3359,7 +3359,7 @@ "filePath": "/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Method for interacting with the Shopify Storefront GraphQL API\n\nIf you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n\n\n\n\n", "examples": [ { @@ -3375,7 +3375,7 @@ ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" } } } @@ -3629,7 +3629,7 @@ "filePath": "/server/config-types.ts", "syntaxKind": "PropertySignature", "name": "scopes", - "value": "AuthScopes | string[]", + "value": "string[] | AuthScopes", "description": "", "isOptional": true }, @@ -3815,7 +3815,7 @@ "filePath": "/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { @@ -3831,7 +3831,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "/server/clients/admin/rest.ts", @@ -4972,7 +4972,7 @@ "filePath": "/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Method for interacting with the Shopify Storefront GraphQL API\n\nIf you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n\n\n\n\n", "examples": [ { @@ -4988,7 +4988,7 @@ ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "AuthenticatePublicLegacy": { "filePath": "/server/authenticate/public/types.ts", @@ -5393,14 +5393,14 @@ { "filePath": "/server/types.ts", "syntaxKind": "MethodSignature", - "name": "__@iterator@644", + "name": "__@iterator@716", "value": "() => IterableIterator", "description": "Iterator" }, { "filePath": "/server/types.ts", "syntaxKind": "MethodSignature", - "name": "__@unscopables@646", + "name": "__@unscopables@718", "value": "() => { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }", "description": "Returns an object whose properties have the value 'true'\r\nwhen they will be absent when used in a 'with' statement." }, @@ -6287,7 +6287,7 @@ "filePath": "/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", "examples": [ { @@ -6303,7 +6303,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "/server/clients/admin/rest.ts", @@ -6502,7 +6502,7 @@ "filePath": "/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", - "value": "GraphQLClient", + "value": "GraphQLClient", "description": "Method for interacting with the Shopify Storefront GraphQL API\n\nIf you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n\n\n\n\n", "examples": [ { @@ -6518,7 +6518,7 @@ ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" } } } diff --git a/packages/shopify-app-remix/docs/generated/generated_static_pages.json b/packages/shopify-app-remix/docs/generated/generated_static_pages.json index 723d84ffd8..820614e730 100644 --- a/packages/shopify-app-remix/docs/generated/generated_static_pages.json +++ b/packages/shopify-app-remix/docs/generated/generated_static_pages.json @@ -66,7 +66,14 @@ "language": "tsx" } ] - } + }, + "sectionCard": [ + { + "url": "/docs/api/shopify-app-remix/guide-graphql-types", + "name": "Typing GraphQL operations", + "type": "tutorial" + } + ] }, { "type": "Generic", @@ -161,6 +168,130 @@ } ] }, + { + "id": "guide-graphql-types", + "title": "Typing GraphQL operations", + "description": "The GraphQL clients provided in this package can use [Codegen](https://the-guild.dev/graphql/codegen) to automatically parse and create types for your queries and mutations.\n\nBy installing a few packages in your app, you can use the `graphql-codegen` script, which will look for strings with the `#graphql` tag and extract types from them.\n\nIf your IDE supports it, you will also get syntax highlighting and auto-complete features when writing your queries.", + "sections": [ + { + "type": "Markdown", + "anchorLink": "example", + "title": "See it in action", + "sectionContent": "\nIn this example, we use the `graphql-codegen` script to parse a query in the `/app/routes/new.tsx` file.\n\nNote how VSCode shows the types for both the return type of `response.json()`, and the `variables` option in the `graphql` function.\n\n\n " + }, + { + "type": "Generic", + "anchorLink": "install", + "title": "Installing packages", + "sectionContent": "To use the `graphql-codegen` script, you will need to install a few packages in your app.\n\nThey will include the scripts to run, and the types that will be overridden by the results of parsing your operations.", + "codeblock": { + "title": "Installing packages", + "tabs": [ + { + "title": "npm", + "language": "sh", + "code": "npm add --save-dev @shopify/api-codegen-preset\nnpm add @shopify/admin-api-client @shopify/storefront-api-client\n" + }, + { + "title": "yarn", + "language": "sh", + "code": "yarn add --dev @shopify/api-codegen-preset\nyarn add @shopify/admin-api-client @shopify/storefront-api-client\n" + }, + { + "title": "pnpm", + "language": "sh", + "code": "pnpm add --save-dev @shopify/api-codegen-preset\npnpm add @shopify/admin-api-client @shopify/storefront-api-client\n" + } + ] + } + }, + { + "type": "Generic", + "anchorLink": "configure", + "title": "Setting up .graphqlrc.ts", + "sectionContent": "Before you can parse operations, you'll need to create a `.graphqlrc.ts` file in your project, and configure it to use the `@shopify/api-codegen-preset`.\n\n> Caution: Parsing will not work on `.graphql` documents, because the preset can only apply types from JavaScript and TypeScript const strings.", + "codeblock": { + "title": "Codegen configuration example", + "tabs": [ + { + "title": "/.graphqlrc.ts", + "language": "ts", + "code": "import {shopifyApiProject, ApiType} from '@shopify/api-codegen-preset';\n\nexport default {\n // For syntax highlighting / auto-complete when writing operations\n schema: 'https://shopify.dev/admin-graphql-direct-proxy/2023-10',\n documents: ['./app/**/*.{js,ts,jsx,tsx}'],\n projects: {\n // To produce variable / return types for Admin API operations\n default: shopifyApiProject({\n apiType: ApiType.Admin,\n apiVersion: '2023-10',\n documents: ['./app/**/*.{js,ts,jsx,tsx}'],\n outputDir: './app/types',\n }),\n },\n};\n" + } + ] + }, + "sectionCard": [ + { + "url": "https://github.com/Shopify/shopify-api-js/tree/main/packages/api-codegen-preset#configuration", + "name": "Configuration options", + "subtitle": "Learn more about the available configurations.", + "type": "github" + } + ] + }, + { + "type": "Generic", + "anchorLink": "set-up-script", + "title": "Setting up the script", + "sectionContent": "To generate types, you'll need to add an entry for `graphql-codegen` in the `\"scripts\"` section of your `package.json` file.", + "codeblock": { + "title": "Setting up the script", + "tabs": [ + { + "title": "/package.json", + "language": "json", + "code": "{\n \"scripts\": {\n \"graphql-codegen\": \"graphql-codegen\"\n }\n}\n" + } + ] + } + }, + { + "type": "Generic", + "anchorLink": "run", + "title": "Generating types", + "sectionContent": "When you run `graphql-codegen`, it will look in all your configured documents for strings marked with a `#graphql` tag.\n\nIDEs that support the `.graphqlrc.ts` file will provide syntax highlighting for your operations, as well as typing.\n\n> Tip: You can pass in a `--watch` flag to the script, which will update your types every time you save a file.", + "codeblock": { + "title": "Running graphql-codegen", + "tabs": [ + { + "title": "npm", + "language": "sh", + "code": "npm run graphql-codegen\n" + }, + { + "title": "yarn", + "language": "sh", + "code": "yarn graphql-codegen\n" + }, + { + "title": "pnpm", + "language": "sh", + "code": "pnpm run graphql-codegen\n" + } + ] + } + }, + { + "type": "Resource", + "title": "Resources", + "anchorLink": "resources", + "resources": [ + { + "name": "Admin API", + "url": "/docs/api/shopify-app-remix/apis/admin-api", + "type": "shopify", + "subtitle": "Make requests to the Admin API" + }, + { + "name": "Storefront API", + "url": "/docs/api/shopify-app-remix/apis/storefront-api", + "type": "shopify", + "subtitle": "Make requests to the Storefront API" + } + ] + } + ] + }, { "id": "shopify-app-remix", "title": "Shopify App package for Remix", diff --git a/packages/shopify-app-remix/docs/staticPages/admin.doc.ts b/packages/shopify-app-remix/docs/staticPages/admin.doc.ts index 64c7e4c4a0..29db5497ac 100644 --- a/packages/shopify-app-remix/docs/staticPages/admin.doc.ts +++ b/packages/shopify-app-remix/docs/staticPages/admin.doc.ts @@ -14,9 +14,9 @@ const data: LandingTemplateSchema = { anchorLink: 'auth', title: 'Authenticating requests', sectionContent: - "To authenticate admin requests you can call `authenticate.admin(request)` in a loader or an action." + + 'To authenticate admin requests you can call `authenticate.admin(request)` in a loader or an action.' + "\n\nIf there's a session for this user, then this loader will return null. If there's no session for the user, then the loader will throw the appropriate redirect Response." + - "\n\n> Tip: If you are authenticating more than one route, then we recommend using [Remix layout routes](https://remix.run/docs/en/1.18.1/file-conventions/routes-files#layout-routes) to automatically authenticate them.", + '\n\n> Tip: If you are authenticating more than one route, then we recommend using [Remix layout routes](https://remix.run/docs/en/1.18.1/file-conventions/routes-files#layout-routes) to automatically authenticate them.', codeblock: { title: 'Authenticating requests', tabs: [ @@ -34,8 +34,8 @@ const data: LandingTemplateSchema = { title: 'Headers', sectionContent: "The OAuth process can't happen inside the admin iframe, and this package is capable of detecting that scenario and properly redirecting using the [Remix `ErrorBoundary`](https://remix.run/docs/en/main/guides/errors) export to set the correct headers for App Bridge." + - "\n\nUse the abstractions provided by this package in your authenticated routes, to automatically set up the error and headers boundaries to redirect outside the iframe when needed." + - "\n\n> Tip: You can also add this to a [Remix layout](https://remix.run/docs/en/main/file-conventions/route-files-v2) if you want to authenticate more than one route, but make sure to call the Shopify boundary methods whenever you need to add your own exports.", + '\n\nUse the abstractions provided by this package in your authenticated routes, to automatically set up the error and headers boundaries to redirect outside the iframe when needed.' + + '\n\n> Tip: You can also add this to a [Remix layout](https://remix.run/docs/en/main/file-conventions/route-files-v2) if you want to authenticate more than one route, but make sure to call the Shopify boundary methods whenever you need to add your own exports.', codeblock: { title: 'Configure header boundaries', tabs: [ @@ -81,6 +81,13 @@ const data: LandingTemplateSchema = { }, ], }, + sectionCard: [ + { + url: '/docs/api/shopify-app-remix/guide-graphql-types', + name: 'Typing GraphQL operations', + type: 'tutorial', + }, + ], }, { type: 'Generic', diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/.graphqlrc.ts b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/.graphqlrc.ts new file mode 100644 index 0000000000..727e206620 --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/.graphqlrc.ts @@ -0,0 +1,16 @@ +import {shopifyApiProject, ApiType} from '@shopify/api-codegen-preset'; + +export default { + // For syntax highlighting / auto-complete when writing operations + schema: 'https://shopify.dev/admin-graphql-direct-proxy/2023-10', + documents: ['./app/**/*.{js,ts,jsx,tsx}'], + projects: { + // To produce variable / return types for Admin API operations + default: shopifyApiProject({ + apiType: ApiType.Admin, + apiVersion: '2023-10', + documents: ['./app/**/*.{js,ts,jsx,tsx}'], + outputDir: './app/types', + }), + }, +}; diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.npm.example.sh b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.npm.example.sh new file mode 100644 index 0000000000..31d8c45a3d --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.npm.example.sh @@ -0,0 +1,2 @@ +npm add --save-dev @shopify/api-codegen-preset +npm add @shopify/admin-api-client @shopify/storefront-api-client diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.pnpm.example.sh b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.pnpm.example.sh new file mode 100644 index 0000000000..c1033dd508 --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.pnpm.example.sh @@ -0,0 +1,2 @@ +pnpm add --save-dev @shopify/api-codegen-preset +pnpm add @shopify/admin-api-client @shopify/storefront-api-client diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.yarn.example.sh b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.yarn.example.sh new file mode 100644 index 0000000000..1e4b491dbf --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/install.yarn.example.sh @@ -0,0 +1,2 @@ +yarn add --dev @shopify/api-codegen-preset +yarn add @shopify/admin-api-client @shopify/storefront-api-client diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/package.json b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/package.json new file mode 100644 index 0000000000..1c1a6e81b7 --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/package.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "graphql-codegen": "graphql-codegen" + } +} diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.npm.example.sh b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.npm.example.sh new file mode 100644 index 0000000000..3750706a7a --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.npm.example.sh @@ -0,0 +1 @@ +npm run graphql-codegen diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.pnpm.example.sh b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.pnpm.example.sh new file mode 100644 index 0000000000..6de9d524b6 --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.pnpm.example.sh @@ -0,0 +1 @@ +pnpm run graphql-codegen diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.yarn.example.sh b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.yarn.example.sh new file mode 100644 index 0000000000..5aba6e39e2 --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/graphql-types/run.yarn.example.sh @@ -0,0 +1 @@ +yarn graphql-codegen diff --git a/packages/shopify-app-remix/docs/staticPages/graphql-types.doc.ts b/packages/shopify-app-remix/docs/staticPages/graphql-types.doc.ts new file mode 100644 index 0000000000..e7212b7b0b --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/graphql-types.doc.ts @@ -0,0 +1,148 @@ +import {LandingTemplateSchema} from '@shopify/generate-docs'; + +const data: LandingTemplateSchema = { + id: 'guide-graphql-types', + title: 'Typing GraphQL operations', + description: + 'The GraphQL clients provided in this package can use [Codegen](https://the-guild.dev/graphql/codegen) to automatically parse and create types for your queries and mutations.' + + '\n\nBy installing a few packages in your app, you can use the `graphql-codegen` script, which will look for strings with the `#graphql` tag and extract types from them.' + + '\n\nIf your IDE supports it, you will also get syntax highlighting and auto-complete features when writing your queries.', + sections: [ + { + type: 'Markdown', + anchorLink: 'example', + title: 'See it in action', + sectionContent: ` +In this example, we use the \`graphql-codegen\` script to parse a query in the \`/app/routes/new.tsx\` file. + +Note how VSCode shows the types for both the return type of \`response.json()\`, and the \`variables\` option in the \`graphql\` function. + + + `, + }, + { + type: 'Generic', + anchorLink: 'install', + title: 'Installing packages', + sectionContent: + 'To use the `graphql-codegen` script, you will need to install a few packages in your app.' + + '\n\nThey will include the scripts to run, and the types that will be overridden by the results of parsing your operations.', + codeblock: { + title: 'Installing packages', + tabs: [ + { + title: 'npm', + language: 'sh', + code: './examples/guides/graphql-types/install.npm.example.sh', + }, + { + title: 'yarn', + language: 'sh', + code: './examples/guides/graphql-types/install.yarn.example.sh', + }, + { + title: 'pnpm', + language: 'sh', + code: './examples/guides/graphql-types/install.pnpm.example.sh', + }, + ], + }, + }, + { + type: 'Generic', + anchorLink: 'configure', + title: 'Setting up .graphqlrc.ts', + sectionContent: + "Before you can parse operations, you'll need to create a `.graphqlrc.ts` file in your project, and configure it to use the `@shopify/api-codegen-preset`." + + '\n\n> Caution: Parsing will not work on `.graphql` documents, because the preset can only apply types from JavaScript and TypeScript const strings.', + codeblock: { + title: 'Codegen configuration example', + tabs: [ + { + title: '/.graphqlrc.ts', + language: 'ts', + code: './examples/guides/graphql-types/.graphqlrc.ts', + }, + ], + }, + sectionCard: [ + { + url: 'https://github.com/Shopify/shopify-api-js/tree/main/packages/api-codegen-preset#configuration', + name: 'Configuration options', + subtitle: 'Learn more about the available configurations.', + type: 'github', + }, + ], + }, + { + type: 'Generic', + anchorLink: 'set-up-script', + title: 'Setting up the script', + sectionContent: + 'To generate types, you\'ll need to add an entry for `graphql-codegen` in the `"scripts"` section of your `package.json` file.', + codeblock: { + title: 'Setting up the script', + tabs: [ + { + title: '/package.json', + language: 'json', + code: './examples/guides/graphql-types/package.json', + }, + ], + }, + }, + { + type: 'Generic', + anchorLink: 'run', + title: 'Generating types', + sectionContent: + 'When you run `graphql-codegen`, it will look in all your configured documents for strings marked with a `#graphql` tag.' + + '\n\nIDEs that support the `.graphqlrc.ts` file will provide syntax highlighting for your operations, as well as typing.' + + '\n\n> Tip: You can pass in a `--watch` flag to the script, which will update your types every time you save a file.', + codeblock: { + title: 'Running graphql-codegen', + tabs: [ + { + title: 'npm', + language: 'sh', + code: './examples/guides/graphql-types/run.npm.example.sh', + }, + { + title: 'yarn', + language: 'sh', + code: './examples/guides/graphql-types/run.yarn.example.sh', + }, + { + title: 'pnpm', + language: 'sh', + code: './examples/guides/graphql-types/run.pnpm.example.sh', + }, + ], + }, + }, + { + type: 'Resource', + title: 'Resources', + anchorLink: 'resources', + resources: [ + { + name: 'Admin API', + url: '/docs/api/shopify-app-remix/apis/admin-api', + type: 'shopify', + subtitle: 'Make requests to the Admin API', + }, + { + name: 'Storefront API', + url: '/docs/api/shopify-app-remix/apis/storefront-api', + type: 'shopify', + subtitle: 'Make requests to the Storefront API', + }, + ], + }, + ], +}; + +export default data; diff --git a/packages/shopify-app-remix/package.json b/packages/shopify-app-remix/package.json index 22f9f88541..d424e474ab 100644 --- a/packages/shopify-app-remix/package.json +++ b/packages/shopify-app-remix/package.json @@ -68,8 +68,10 @@ }, "dependencies": { "@remix-run/server-runtime": "^2.0.0", - "@shopify/shopify-api": "^8.1.1", + "@shopify/admin-api-client": "^0.2.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2", + "@shopify/storefront-api-client": "^0.2.1", "isbot": "^3.7.1", "semver": "^7.5.4", "tslib": "^2.6.2" diff --git a/packages/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts b/packages/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts index 2c83ad614c..fcdefe8023 100644 --- a/packages/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts +++ b/packages/shopify-app-remix/src/server/__test-helpers/expect-admin-api-client.ts @@ -96,7 +96,9 @@ export function expectAdminApiClient( headers: {'X-Shopify-Access-Token': actualSession.accessToken!}, }, ), - response: new Response(JSON.stringify({shop: {name: 'Test shop'}})), + response: new Response( + JSON.stringify({data: {shop: {name: 'Test shop'}}}), + ), }); // WHEN @@ -104,7 +106,7 @@ export function expectAdminApiClient( // THEN expect(response.status).toEqual(200); - expect(await response.json()).toEqual({shop: {name: 'Test shop'}}); + expect(await response.json()).toEqual({data: {shop: {name: 'Test shop'}}}); }); it('returns a session object as part of the context', async () => { diff --git a/packages/shopify-app-remix/src/server/__test-helpers/expect-storefront-api-client.ts b/packages/shopify-app-remix/src/server/__test-helpers/expect-storefront-api-client.ts index bf6a4dd3e4..aa2c014cc9 100644 --- a/packages/shopify-app-remix/src/server/__test-helpers/expect-storefront-api-client.ts +++ b/packages/shopify-app-remix/src/server/__test-helpers/expect-storefront-api-client.ts @@ -16,7 +16,7 @@ export function expectStorefrontApiClient( it('Storefront client can perform GraphQL Requests', async () => { // GIVEN const {storefront, actualSession} = await factory(); - const apiResponse = {blogs: {nodes: [{id: 1}]}}; + const apiResponse = {data: {blogs: {nodes: [{id: 1}]}}}; await mockExternalRequest({ request: new Request( `https://${TEST_SHOP}/api/${LATEST_API_VERSION}/graphql.json`, diff --git a/packages/shopify-app-remix/src/server/__test-helpers/request-mock.ts b/packages/shopify-app-remix/src/server/__test-helpers/request-mock.ts index fa8804deee..ea8df63ae2 100644 --- a/packages/shopify-app-remix/src/server/__test-helpers/request-mock.ts +++ b/packages/shopify-app-remix/src/server/__test-helpers/request-mock.ts @@ -45,7 +45,10 @@ async function mockParams(response: Response): Promise { status: response.status, statusText: response.statusText, url: response.url, - headers: Object.fromEntries(response.headers.entries()), + headers: { + ...Object.fromEntries(response.headers.entries()), + 'content-type': 'application/json', + }, }, }; } diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts index d242e3b639..8d189ce97b 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts @@ -105,26 +105,7 @@ describe('admin.authenticate context', () => { it('redirects to auth when request receives a 401 response and not embedded', async () => { // GIVEN const {admin, session} = await setUpNonEmbeddedFlow(); - const requestMock = await mockRequest(); - - // WHEN - const response = await getThrownResponse( - async () => action(admin, session), - requestMock, - ); - - // THEN - expect(response.status).toEqual(302); - - const {hostname, pathname} = new URL(response.headers.get('Location')!); - expect(hostname).toEqual(TEST_SHOP); - expect(pathname).toEqual('/admin/oauth/authorize'); - }); - - it('redirects to auth when request receives a 401 response and not embedded', async () => { - // GIVEN - const {admin, session} = await setUpNonEmbeddedFlow(); - const requestMock = await mockRequest(); + const requestMock = await mockRequest(401); // WHEN const response = await getThrownResponse( @@ -158,7 +139,7 @@ describe('admin.authenticate context', () => { it('redirects to exit iframe when request receives a 401 response and embedded', async () => { // GIVEN const {admin, session} = await setUpEmbeddedFlow(); - const requestMock = await mockRequest(); + const requestMock = await mockRequest(401); // WHEN const response = await getThrownResponse( @@ -173,7 +154,7 @@ describe('admin.authenticate context', () => { it('returns app bridge redirection headers when request receives a 401 response on fetch requests', async () => { // GIVEN const {admin, session} = await setUpFetchFlow(); - const requestMock = await mockRequest(); + const requestMock = await mockRequest(401); // WHEN const response = await getThrownResponse( @@ -243,21 +224,21 @@ async function setUpNonEmbeddedFlow() { }; } -async function mockRestRequest(status = 401) { +async function mockRestRequest(status) { const requestMock = new Request( `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/customers.json`, ); await mockExternalRequest({ request: requestMock, - response: new Response(undefined, {status}), + response: new Response('{}', {status}), }); return requestMock; } function mockGraphqlRequest(apiVersion = LATEST_API_VERSION) { - return async function (status = 401) { + return async function (status) { const requestMock = new Request( `https://${TEST_SHOP}/admin/api/${apiVersion}/graphql.json`, {method: 'POST'}, diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts index 23f0f33ef7..22b7e53130 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts @@ -80,10 +80,10 @@ describe('authorize.admin doc request path', () => { await mockExternalRequest({ request: new Request(GRAPHQL_URL, {method: 'POST'}), - response: new Response( - JSON.stringify({errors: [{message: 'Something went wrong!'}]}), - {status: 500, statusText: 'Internal Server Error'}, - ), + response: new Response('', { + status: 500, + statusText: 'Internal Server Error', + }), }); // WHEN @@ -96,7 +96,7 @@ describe('authorize.admin doc request path', () => { expect(response.status).toBe(500); expect(config.logger?.log).toHaveBeenCalledWith( LogSeverity.Error, - expect.stringContaining('Something went wrong!'), + expect.stringContaining('Internal Server Error'), ); }); @@ -109,7 +109,7 @@ describe('authorize.admin doc request path', () => { await mockExternalRequest({ request: new Request(GRAPHQL_URL, {method: 'POST'}), response: new Response( - JSON.stringify({errors: ['Something went wrong!']}), + JSON.stringify({errors: [{message: 'Something went wrong!'}]}), ), }); diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts index 7ecd5de988..20b87cbad1 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts @@ -223,15 +223,13 @@ export class AuthCodeFlowStrategy< session, }); - await client.query({ - data: `#graphql - query shopifyAppShopName { - shop { - name - } + await client.request(`#graphql + query shopifyAppShopName { + shop { + name } - `, - }); + } + `); } private async oauthCallbackError( diff --git a/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts b/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts index 39b9528501..b3dfd241ad 100644 --- a/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts @@ -97,9 +97,7 @@ describe('Webhook registration', () => { body: 'webhookSubscriptionCreate', }), response: new Response( - JSON.stringify({ - body: mockResponses.HTTP_WEBHOOK_CREATE_ERROR_RESPONSE, - }), + JSON.stringify(mockResponses.HTTP_WEBHOOK_CREATE_ERROR_RESPONSE), ), }, ); diff --git a/packages/shopify-app-remix/src/server/clients/admin/graphql.ts b/packages/shopify-app-remix/src/server/clients/admin/graphql.ts index 13ea7ee35c..3f4c688b42 100644 --- a/packages/shopify-app-remix/src/server/clients/admin/graphql.ts +++ b/packages/shopify-app-remix/src/server/clients/admin/graphql.ts @@ -1,14 +1,9 @@ -import {flatHeaders} from '@shopify/shopify-api/runtime'; +import {AdminOperations} from '@shopify/admin-api-client'; -import {GraphQLClient, GraphQLQueryOptions} from '../types'; +import {GraphQLClient} from '../types'; import {AdminClientOptions} from './types'; -export type GraphqlQueryFunction = ( - query: string, - options?: GraphQLQueryOptions, -) => Promise; - // eslint-disable-next-line no-warning-comments // TODO: This is actually just a call through to the Shopify API client, but with a different API. We should eventually // move this over to the library layer. While doing that, we should also allow the apiVersion to be passed into the REST @@ -17,7 +12,7 @@ export function graphqlClientFactory({ params, handleClientError, session, -}: AdminClientOptions): GraphQLClient { +}: AdminClientOptions): GraphQLClient { return async function query(operation, options) { const client = new params.api.clients.Graphql({ session, @@ -26,15 +21,13 @@ export function graphqlClientFactory({ try { // We convert the incoming response to a Response object to bring this client closer to the Remix client. - const apiResponse = await client.query({ - data: {query: operation, variables: options?.variables}, - tries: options?.tries, - extraHeaders: options?.headers, + const apiResponse = await client.request(operation, { + variables: options?.variables, + retries: options?.tries ? options.tries - 1 : 0, + headers: options?.headers, }); - return new Response(JSON.stringify(apiResponse.body), { - headers: flatHeaders(apiResponse.headers), - }); + return new Response(JSON.stringify(apiResponse)); } catch (error) { if (handleClientError) { throw await handleClientError({error, params, session}); diff --git a/packages/shopify-app-remix/src/server/clients/admin/types.ts b/packages/shopify-app-remix/src/server/clients/admin/types.ts index db8571c72c..5d49d1f001 100644 --- a/packages/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/shopify-app-remix/src/server/clients/admin/types.ts @@ -1,4 +1,5 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; +import {AdminOperations} from '@shopify/admin-api-client'; import {BasicParams} from '../../types'; import {GraphQLClient} from '../types'; @@ -161,5 +162,5 @@ export interface AdminApiContext< * } * ``` */ - graphql: GraphQLClient; + graphql: GraphQLClient; } diff --git a/packages/shopify-app-remix/src/server/clients/storefront/factory.ts b/packages/shopify-app-remix/src/server/clients/storefront/factory.ts index b19f1782e0..1127ff75be 100644 --- a/packages/shopify-app-remix/src/server/clients/storefront/factory.ts +++ b/packages/shopify-app-remix/src/server/clients/storefront/factory.ts @@ -1,4 +1,3 @@ -import {flatHeaders} from '@shopify/shopify-api/runtime'; import {Session} from '@shopify/shopify-api'; import {BasicParams} from '../../types'; @@ -21,15 +20,13 @@ export function storefrontClientFactory({ apiVersion: options.apiVersion, }); - const apiResponse = await client.query({ - data: {query, variables: options?.variables}, - tries: options.tries, - extraHeaders: options.headers, + const apiResponse = await client.request(query, { + variables: options?.variables, + retries: options?.tries ? options.tries - 1 : 0, + headers: options?.headers, }); - return new Response(JSON.stringify(apiResponse.body), { - headers: flatHeaders(apiResponse.headers), - }); + return new Response(JSON.stringify(apiResponse)); }, }; } diff --git a/packages/shopify-app-remix/src/server/clients/storefront/types.ts b/packages/shopify-app-remix/src/server/clients/storefront/types.ts index 42e2fde77c..8e22eb9b7d 100644 --- a/packages/shopify-app-remix/src/server/clients/storefront/types.ts +++ b/packages/shopify-app-remix/src/server/clients/storefront/types.ts @@ -1,3 +1,5 @@ +import {StorefrontOperations} from '@shopify/storefront-api-client'; + import {GraphQLClient} from '../types'; export interface StorefrontContext { @@ -25,5 +27,5 @@ export interface StorefrontContext { * } * ``` */ - graphql: GraphQLClient; + graphql: GraphQLClient; } diff --git a/packages/shopify-app-remix/src/server/clients/types.ts b/packages/shopify-app-remix/src/server/clients/types.ts index 1bbc348c6d..81cf793639 100644 --- a/packages/shopify-app-remix/src/server/clients/types.ts +++ b/packages/shopify-app-remix/src/server/clients/types.ts @@ -1,17 +1,27 @@ +import type { + AllOperations, + ReturnData, + FetchResponseBody, + ApiClientRequestOptions, + ResponseWithType, +} from '@shopify/admin-api-client'; import {ApiVersion} from '@shopify/shopify-api'; -interface QueryVariables { - [key: string]: any; -} - -export interface GraphQLQueryOptions { - variables?: QueryVariables; +export interface GraphQLQueryOptions< + Operation extends keyof Operations, + Operations extends AllOperations, +> { + variables?: ApiClientRequestOptions['variables']; apiVersion?: ApiVersion; headers?: {[key: string]: any}; tries?: number; } -export type GraphQLClient = ( - query: string, - options?: GraphQLQueryOptions, -) => Promise; +export type GraphQLClient = < + Operation extends keyof Operations, +>( + query: Operation, + options?: GraphQLQueryOptions, +) => Promise< + ResponseWithType>> +>; diff --git a/packages/shopify-app-session-storage-dynamodb/package.json b/packages/shopify-app-session-storage-dynamodb/package.json index 4dd9f4ea85..fe0e79730b 100644 --- a/packages/shopify-app-session-storage-dynamodb/package.json +++ b/packages/shopify-app-session-storage-dynamodb/package.json @@ -37,13 +37,13 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { "@shopify/eslint-plugin": "^42.1.0", "@shopify/prettier-config": "^1.1.2", - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2", "@shopify/shopify-app-session-storage-test-utils": "^1.0.2", "eslint": "^8.55.0", diff --git a/packages/shopify-app-session-storage-kv/package.json b/packages/shopify-app-session-storage-kv/package.json index 9a1a67c499..aa0ddad529 100644 --- a/packages/shopify-app-session-storage-kv/package.json +++ b/packages/shopify-app-session-storage-kv/package.json @@ -36,7 +36,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-memory/package.json b/packages/shopify-app-session-storage-memory/package.json index 53a7075293..8bc52d314c 100644 --- a/packages/shopify-app-session-storage-memory/package.json +++ b/packages/shopify-app-session-storage-memory/package.json @@ -33,7 +33,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-mongodb/package.json b/packages/shopify-app-session-storage-mongodb/package.json index 619992dd1c..ac88441bed 100644 --- a/packages/shopify-app-session-storage-mongodb/package.json +++ b/packages/shopify-app-session-storage-mongodb/package.json @@ -35,7 +35,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-mysql/package.json b/packages/shopify-app-session-storage-mysql/package.json index 6585122591..d6e79f8a39 100644 --- a/packages/shopify-app-session-storage-mysql/package.json +++ b/packages/shopify-app-session-storage-mysql/package.json @@ -36,7 +36,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-postgresql/package.json b/packages/shopify-app-session-storage-postgresql/package.json index a96709418b..cc8d7ce2c9 100644 --- a/packages/shopify-app-session-storage-postgresql/package.json +++ b/packages/shopify-app-session-storage-postgresql/package.json @@ -37,7 +37,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-prisma/package.json b/packages/shopify-app-session-storage-prisma/package.json index abbecbf2c8..7847ae9df4 100644 --- a/packages/shopify-app-session-storage-prisma/package.json +++ b/packages/shopify-app-session-storage-prisma/package.json @@ -35,13 +35,13 @@ }, "peerDependencies": { "@prisma/client": "^4.13.0", - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2", "prisma": "^4.13.0" }, "devDependencies": { "@prisma/client": "^4.13.0", - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2", "prisma": "^4.13.0", "@shopify/eslint-plugin": "^42.1.0", diff --git a/packages/shopify-app-session-storage-redis/package.json b/packages/shopify-app-session-storage-redis/package.json index fad28a9415..2f1f372843 100644 --- a/packages/shopify-app-session-storage-redis/package.json +++ b/packages/shopify-app-session-storage-redis/package.json @@ -36,7 +36,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-sqlite/package.json b/packages/shopify-app-session-storage-sqlite/package.json index 154e89ae51..a6355a61c0 100644 --- a/packages/shopify-app-session-storage-sqlite/package.json +++ b/packages/shopify-app-session-storage-sqlite/package.json @@ -36,7 +36,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage-test-utils/package.json b/packages/shopify-app-session-storage-test-utils/package.json index b660251869..dcd977e6ab 100644 --- a/packages/shopify-app-session-storage-test-utils/package.json +++ b/packages/shopify-app-session-storage-test-utils/package.json @@ -38,7 +38,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1", + "@shopify/shopify-api": "^9.0.1", "@shopify/shopify-app-session-storage": "^2.0.2" }, "devDependencies": { diff --git a/packages/shopify-app-session-storage/package.json b/packages/shopify-app-session-storage/package.json index fdec288b02..091f0c1308 100644 --- a/packages/shopify-app-session-storage/package.json +++ b/packages/shopify-app-session-storage/package.json @@ -34,7 +34,7 @@ "tslib": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^8.1.1" + "@shopify/shopify-api": "^9.0.1" }, "devDependencies": { "@shopify/eslint-plugin": "^42.1.0", diff --git a/yarn.lock b/yarn.lock index 23e6a5118e..b9a5416c4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2780,6 +2780,13 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@shopify/admin-api-client@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@shopify/admin-api-client/-/admin-api-client-0.2.1.tgz#98f10a9732b6d742189af5ccb7bb4dd781485acd" + integrity sha512-Oeh247CRXqSk5ktrEoBRN/BA5AGEliiyKJeTSGaIZpnMYDSXb2FNsmLaZY49/jTklmMouwoqh7FnYE5Vw8ZyQw== + dependencies: + "@shopify/graphql-client" "^0.9.1" + "@shopify/babel-preset@^24.1.4": version "24.1.5" resolved "https://registry.yarnpkg.com/@shopify/babel-preset/-/babel-preset-24.1.5.tgz#76cfef62bb8a4d9769559f3f2505148a55d13488" @@ -2841,6 +2848,11 @@ globby "^11.1.0" typescript "^4.8.3" +"@shopify/graphql-client@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@shopify/graphql-client/-/graphql-client-0.9.1.tgz#0c37a4143c0dd26837e82b8fd13e1b64fabf90dc" + integrity sha512-yx6PIHY8u4O0ijUQccmIpjoyM1JSbe5RkrSyXSbjhsS+nla0WAJDIMw4R01mjx3dFnVIcGONhRX/QrUq97uWig== + "@shopify/loom-cli@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@shopify/loom-cli/-/loom-cli-1.1.0.tgz#e92f423a26f00c33b7c247a65667a8ff9bd1e8f8" @@ -2978,12 +2990,14 @@ jest-matcher-utils "^26.6.2" react-reconciler "^0.28.0" -"@shopify/shopify-api@^8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@shopify/shopify-api/-/shopify-api-8.1.1.tgz#d3f400a27d9fe2eaa7647bbd33134b75fe52463a" - integrity sha512-0JO3Mhv9Sb8VKPw/LUdacHJj9wbT/txyByr2TF03yjqoV++G3NxNMUAA1tcpoSvOAv20KlQjwpoFnXqBmPFW7Q== +"@shopify/shopify-api@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@shopify/shopify-api/-/shopify-api-9.0.1.tgz#7f236d94d902b3671c6a11f11f3561f98ddc7422" + integrity sha512-ToqT5zt/YA+VbFcLAoEVr6grs6v+Y9io5kuULy9Zhm5RClFEhkpRLM4U+vrrV5XjWYC6pcxs00WjpSi0onryrw== dependencies: + "@shopify/admin-api-client" "^0.2.1" "@shopify/network" "^3.2.1" + "@shopify/storefront-api-client" "^0.2.1" compare-versions "^5.0.3" isbot "^3.6.10" jose "^4.9.1" @@ -2991,6 +3005,13 @@ tslib "^2.0.3" uuid "^9.0.0" +"@shopify/storefront-api-client@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@shopify/storefront-api-client/-/storefront-api-client-0.2.1.tgz#17f98d34cc218ff3e688bb812421f5c51e3ab451" + integrity sha512-e0yk0rwxGd6Dr7ArBHD1MuDd74RRkW03xb2YS90PZEP3mHcMCVAng2lm733tqooA6iNaooAKsy+O0HTKH2JgUA== + dependencies: + "@shopify/graphql-client" "^0.9.1" + "@shopify/typescript-configs@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@shopify/typescript-configs/-/typescript-configs-5.1.0.tgz#f6e8fdd3291bf0a406578b2c6eb21f8c542d3c0a"