gqty icon indicating copy to clipboard operation
gqty copied to clipboard

gqty doesn't respect `prevSelection.unions` while composing `cacheKey`

Open Ty3uK opened this issue 3 years ago • 1 comments

Hi!

I have scheme like this (using Federation):

# type from another subgraph
type Product @key(fields: "id") {
	id: String!
	name: String!
	slug: String!
}

# type from another subgraph
type Service @key(fields: "id") {
	id: String!
	name: String!
}

type BasketItemProduct {
	id: String!
	price: String!
	count: Int!
	product: Product
	services: [Service!]!
}

type BasketItemPresent {
	id: String!
	price: String!
	count: Int!
	product: Product
}

union BasketItem =
	| BasketItemProduct
	| BasketItemPresent

type Basket {
	items: [BasketItem!]!
}

I want to fetch query like this:

query {
	basket {
		items {
			...  on BasketItemProduct {
				price
				count
				product {
					name
					slug
				}
			}
			...  on BasketItemPresent {
				price
				count
				product {
					name
					slug
				}
			}
		}
	}
}

My code (simplified) looks like this:

function mapService(service: Service | null) {
	return castNotSkeleton({
		name: service,
	});
}

function mapProduct(product: Product | null) {
	return castNotSkeleton({
		name: product.name,
		slug: product.slug,
		services: product.services.map(mapService),
	});
}

function mapBasketItemProduct(itemProduct: BasketItemProduct | null) {
	if (!itemProduct) {
		return null;
	}
	
	return castNotSkeleton({
		id: itemProduct,
		price: itemProduct.price,
		count: itemProduct.count,
		product: mapProduct(itemProduct.product ?? null),
	});
}

function mapBasketItemPresent(itemPresent: BasketItemPresent | null) {
	if (! itemPresent) {
		return null;
	}

	return castNotSkeleton({
		id: itemPresent,
		price: itemPresent.price,
		count: itemPresent.count,
		product: mapProduct(itemPresent.product ?? null),
	});
}

export function api() {
	return resolved(() => {
		return query.basket.items.map((it) => ({
			product: mapBasketItemProduct(it.$on.BasketItemProduct ?? null),
			present: mapBasketItemProduct(it.$BasketItemPresent ?? null),
		}));
	});
}

and with this piece of code I've got query like this (simplified):

query {
	basket {
		items {
			...  on BasketItemProduct {
				price
				count
				product {
					name
					slug
				}
			}
			...  on BasketItemPresent {
				price
				count
			}
		}
	}
}

so second usage of mapproduct is ignored.

After some investigation I found that problem is in this piece of code:

https://github.com/gqty-dev/gqty/blob/f39bb45023613e0606bedb0b25dd3468da146b67/packages/gqty/src/Selection/SelectionManager.ts#L142-L158

because cacheKey for Product.slug (for example) looks like this: basket.items.0.product.slug and at the second pass it returns entry for BasketItemProduct and not creating seperate one for BasketItemPresent

My ugly fix for testing looks like this:

if (prevSelection?.unions?.length > 0) {
  cacheKey += ";" + prevSelection.unions.join(";");
}
let selection = selectionCache.get(cacheKey);
if (selection == null || selection.prevSelection !== prevSelection) ...

I hope you can forward me to the right point for this issue! ❤️

Ty3uK avatar Dec 16 '22 09:12 Ty3uK