Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/product comparison api #2627

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion packages/api/src/__generated__/schema.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions packages/api/src/platforms/vtex/resolvers/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ export const StoreProduct: Record<string, Resolver<Root>> & {
...propertyValueAttributes,
]
},
hasSpecifications: ({isVariantOf}) => Boolean(
isVariantOf.skuSpecifications?.length
),
skuSpecifications: ({isVariantOf: { skuSpecifications }}) => skuSpecifications ?? [],
specificationGroups: ({isVariantOf: { specificationGroups }}) => specificationGroups,
releaseDate: ({ isVariantOf: { releaseDate } }) => releaseDate ?? '',
advertisement: ({ isVariantOf: { advertisement } }) => advertisement,
}
28 changes: 28 additions & 0 deletions packages/api/src/platforms/vtex/resolvers/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
QuerySellersArgs,
QueryShippingArgs,
QueryRedirectArgs,
QueryProductsArgs,
} from '../../../__generated__/schema'
import type { CategoryTree } from '../clients/commerce/types/CategoryTree'
import type { Context } from '../index'
Expand Down Expand Up @@ -202,6 +203,33 @@ export const Query = {
})),
}
},
products: async (
_: unknown,
{ productIds }: QueryProductsArgs,
ctx: Context
) => {
const {
clients: { search },
} = ctx

if(!productIds.length) {
return []
}

const query = `id:${productIds.join(';')}`
const products = await search.products({
page: 0,
count: productIds.length,
query,
})

return products.products
.map((product) => product.items.map((sku) => enhanceSku(sku, product)))
.flat()
.filter(
(sku) => productIds.includes(sku.itemId) && sku.sellers.length > 0
)
},
allCollections: async (
_: unknown,
{ first, after: maybeAfter }: QueryAllCollectionsArgs,
Expand Down
42 changes: 42 additions & 0 deletions packages/api/src/typeDefs/product.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,48 @@ type StoreProduct {
Advertisement information about the product.
"""
advertisement: Advertisement
"""
Indicates whether the product has specifications.
"""
hasSpecifications: Boolean
"""
Indicate the specifications of a product.
"""
skuSpecifications: [SkuSpecification!]!
"""
Indicate the specifications of a group of SKUs.
"""
specificationGroups: [SpecificationGroup!]!
}

type SkuSpecification {
field: SKUSpecificationField!
values: [SKUSpecificationValue!]!
}

type SKUSpecificationValue {
name: String!
id: String
fieldId: String
originalName: String
}

type SKUSpecificationField {
name: String!
originalName: String
id: String
}

type SpecificationGroup {
name: String!
originalName: String!
specifications: [Specification!]!
}

type Specification {
name: String!
originalName: String!
values: [String!]!
}

"""
Expand Down
9 changes: 9 additions & 0 deletions packages/api/src/typeDefs/query.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ type Query {
): StoreProductConnection!
@cacheControl(scope: "public", sMaxAge: 120, staleWhileRevalidate: 3600)

"""
Returns information about selected products.
"""
products(
productIds: [String!]!

): [StoreProduct!]!
@cacheControl(scope: "public", sMaxAge: 120, staleWhileRevalidate: 3600)

"""
Returns information about all collections.
"""
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/typeDefs/skuVariants.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type SkuVariants {
availableVariations(dominantVariantName: String): FormattedVariants

"""
All possible variant combinations of the current product. It also includes the data for each variant.
All available options for each SKU variant property, indexed by their name.
"""
allVariantProducts: [StoreProduct!]
}
Expand Down
30 changes: 27 additions & 3 deletions packages/core/@generated/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import * as types from './graphql'
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
'\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n':
'\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n hasSpecifications\n\n advertisement {\n adId\n adResponseId\n }\n }\n':
types.ProductSummary_ProductFragmentDoc,
'\n fragment Filter_facets on StoreFacet {\n ... on StoreFacetRange {\n key\n label\n\n min {\n selected\n absolute\n }\n\n max {\n selected\n absolute\n }\n\n __typename\n }\n ... on StoreFacetBoolean {\n key\n label\n values {\n label\n value\n selected\n quantity\n }\n\n __typename\n }\n }\n':
types.Filter_FacetsFragmentDoc,
'\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n\t\t\tskuVariants {\n activeVariations\n slugsMap\n availableVariations\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n':
'\n fragment ProductDetailsFragment_product on StoreProduct {\n id: productID\n sku\n name\n gtin\n description\n unitMultiplier\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n name\n productID\n }\n }\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n # Contains necessary info to add this item to cart\n ...CartProductItem\n }\n':
types.ProductDetailsFragment_ProductFragmentDoc,
'\n fragment ProductSKUMatrixSidebarFragment_product on StoreProduct {\n id: productID\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n allVariantProducts {\n\t\t\t\t\tsku\n name\n image {\n url\n alternateName\n }\n offers {\n highPrice\n lowPrice\n lowPriceWithTaxes\n offerCount\n priceCurrency\n offers {\n listPrice\n listPriceWithTaxes\n sellingPrice\n priceCurrency\n price\n priceWithTaxes\n priceValidUntil\n itemCondition\n availability\n quantity\n }\n }\n additionalProperty {\n propertyID\n value\n name\n valueReference\n }\n }\n }\n }\n }\n':
types.ProductSkuMatrixSidebarFragment_ProductFragmentDoc,
Expand Down Expand Up @@ -48,10 +48,16 @@ const documents = {
types.ClientAllVariantProductsQueryDocument,
'\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n':
types.ClientManyProductsQueryDocument,
'\n query ClientManyProductsComparisonQuery(\n $productIds: [String!]!\n \n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n } \n':
types.ClientManyProductsComparisonQueryDocument,
'\n fragment ProductComparisonFragment_product on StoreProduct {\n ...ProductDetailsFragment_product,\n\n skuSpecifications {\n field {\n id\n name\n originalName\n }\n values {\n name\n id\n fieldId\n originalName\n }\n }\n\n specificationGroups {\n name\n originalName\n specifications {\n name\n originalName\n values\n }\n }\n }\n':
types.ProductComparisonFragment_ProductFragmentDoc,
'\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n fuzzy\n }\n':
types.ClientProductGalleryQueryDocument,
'\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n':
types.ClientProductQueryDocument,
'\n query ClientManyProductsSelectedQuery(\n $productIds: [String!]!\n\n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n }\n':
types.ClientManyProductsSelectedQueryDocument,
'\n query ClientSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n ...ProductSummary_product\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n':
types.ClientSearchSuggestionsQueryDocument,
'\n query ClientTopSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientTopSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n }\n }\n }\n':
Expand All @@ -66,7 +72,7 @@ const documents = {
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n advertisement {\n adId\n adResponseId\n }\n }\n'
source: '\n fragment ProductSummary_product on StoreProduct {\n id: productID\n slug\n sku\n brand {\n brandName: name\n }\n name\n gtin\n\n isVariantOf {\n productGroupID\n name\n }\n\n image {\n url\n alternateName\n }\n\n brand {\n name\n }\n\n offers {\n lowPrice\n lowPriceWithTaxes\n offers {\n availability\n price\n listPrice\n listPriceWithTaxes\n quantity\n seller {\n identifier\n }\n }\n }\n\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n\n hasSpecifications\n\n advertisement {\n adId\n adResponseId\n }\n }\n'
): typeof import('./graphql').ProductSummary_ProductFragmentDoc
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
Expand Down Expand Up @@ -170,6 +176,18 @@ export function gql(
export function gql(
source: '\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n'
): typeof import('./graphql').ClientManyProductsQueryDocument
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n query ClientManyProductsComparisonQuery(\n $productIds: [String!]!\n \n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n } \n'
): typeof import('./graphql').ClientManyProductsComparisonQueryDocument
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n fragment ProductComparisonFragment_product on StoreProduct {\n ...ProductDetailsFragment_product,\n\n skuSpecifications {\n field {\n id\n name\n originalName\n }\n values {\n name\n id\n fieldId\n originalName\n }\n }\n\n specificationGroups {\n name\n originalName\n specifications {\n name\n originalName\n values\n }\n }\n }\n'
): typeof import('./graphql').ProductComparisonFragment_ProductFragmentDoc
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand All @@ -182,6 +200,12 @@ export function gql(
export function gql(
source: '\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n'
): typeof import('./graphql').ClientProductQueryDocument
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n query ClientManyProductsSelectedQuery(\n $productIds: [String!]!\n\n ) {\n products(\n productIds: $productIds\n ) {\n ...ProductComparisonFragment_product\n }\n }\n'
): typeof import('./graphql').ClientManyProductsSelectedQueryDocument
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading
Loading