diff --git a/.changeset/strong-peas-begin.md b/.changeset/strong-peas-begin.md new file mode 100644 index 0000000..6ca24d6 --- /dev/null +++ b/.changeset/strong-peas-begin.md @@ -0,0 +1,5 @@ +--- +"saleor-app-products-feed": patch +--- + +Query for the product details run now in paralell to speed up overall feed generation diff --git a/apps/products-feed/graphql/queries/FetchProductCursors.graphql b/apps/products-feed/graphql/queries/FetchProductCursors.graphql new file mode 100644 index 0000000..66dc793 --- /dev/null +++ b/apps/products-feed/graphql/queries/FetchProductCursors.graphql @@ -0,0 +1,9 @@ +query FetchProductCursors($first:Int!, $after: String, $channel: String!){ + productVariants(first:$first, after: $after, channel: $channel){ + pageInfo{ + hasNextPage + startCursor + endCursor + } + } +} diff --git a/apps/products-feed/src/lib/google-feed/fetch-product-data.ts b/apps/products-feed/src/lib/google-feed/fetch-product-data.ts index d3d11c4..b8eebc7 100644 --- a/apps/products-feed/src/lib/google-feed/fetch-product-data.ts +++ b/apps/products-feed/src/lib/google-feed/fetch-product-data.ts @@ -2,10 +2,64 @@ import { url } from "inspector"; import { Client } from "urql"; import { createLogger } from "@saleor/apps-shared"; import { + FetchProductCursorsDocument, FetchProductDataForFeedDocument, GoogleFeedProductVariantFragment, } from "../../../generated/graphql"; +const getCursors = async ({ client, channel }: { client: Client; channel: string }) => { + let result = await client + .query(FetchProductCursorsDocument, { channel: channel as string, first: 100 }) + .toPromise(); + + // First page is queried without the `after` param, so we start an array with `undefined` + const cursors: Array = [undefined]; + + while (result.data?.productVariants?.pageInfo.hasNextPage) { + result = await client + .query(FetchProductCursorsDocument, { + channel: channel as string, + first: 100, + after: result.data.productVariants.pageInfo.endCursor, + }) + .toPromise(); + + const endCursor = result.data?.productVariants?.pageInfo.endCursor; + + if (endCursor) { + cursors.push(endCursor); + } + } + return cursors; +}; + +const fetchVariants = async ({ + client, + after, + channel, +}: { + client: Client; + after?: string; + channel: string; +}): Promise => { + const logger = createLogger({ saleorApiUrl: url, channel, fn: "fetchVariants" }); + + const result = await client + .query(FetchProductDataForFeedDocument, { + channel: channel as string, + first: 100, + after, + }) + .toPromise(); + + if (result.error) { + logger.error(`Error during the GraphqlAPI call: ${result.error.message}`); + return []; + } + + return result.data?.productVariants?.edges.map((e) => e.node) || []; +}; + interface FetchProductDataArgs { client: Client; channel: string; @@ -14,40 +68,13 @@ interface FetchProductDataArgs { export const fetchProductData = async ({ client, channel }: FetchProductDataArgs) => { const logger = createLogger({ saleorApiUrl: url, channel, route: "Google Product Feed" }); - let result = await client - .query(FetchProductDataForFeedDocument, { channel: channel as string, first: 100 }) - .toPromise(); + const cursors = await getCursors({ client, channel }); - if (result.error) { - logger.error(`Error during the GraphqlAPI call: ${result.error.message}`); - throw new Error("Error during the GraphQL API call"); - } + logger.debug(`Query generated ${cursors.length} cursors`); - let variants: GoogleFeedProductVariantFragment[] = - result.data?.productVariants?.edges.map((e) => e.node) || []; + const promises = cursors.map((cursor) => fetchVariants({ client, after: cursor, channel })); - while (result.data?.productVariants?.pageInfo.hasNextPage) { - logger.debug("Fetching the next page of products"); + const results = await Promise.all(promises); - result = await client - .query(FetchProductDataForFeedDocument, { - channel: channel as string, - first: 100, - after: result.data.productVariants.pageInfo.endCursor, - }) - .toPromise(); - - if (result.error) { - logger.error(`Error during the GraphqlAPI call: ${result.error.message}`); - throw new Error("Error during the GraphQL API call"); - } - - if (!result.data?.productVariants?.edges.length) { - logger.warn("Fetching the second page of results resulted in no entries"); - break; - } - variants = variants?.concat(result.data?.productVariants?.edges.map((e) => e.node)); - } - - return variants; + return results.flat(); };