Compare commits

...
10 Commits
Author SHA1 Message Date
Anders GustafssonandGitHub 8608b28300 8425 - stop using v2 category tables and views (#190)
Add new relic deployment marker / New Relic (push) Has been cancelled
Node.js CI with tests / build (16.x) (push) Has been cancelled
Sync staging from master / build (push) Has been cancelled
2026-06-16 14:11:02 +02:00
Anders GustafssonandGitHub eecc0f4f1b 9675 - Fix material insert for new material (#195) 2026-06-08 09:58:35 +02:00
Arwid ThornströmandGitHub dcf41787d2 Enhance ECS CloudFormation template by adding EnvironmentName paramet… (#192)
* Enhance ECS CloudFormation template by adding EnvironmentName parameter with allowed values and tagging resources with Project and Environment details for better organization and management.

* fixed

* Update CloudFormation templates to include Project tag in create and update stack commands, while removing redundant Project tags from resources in ecs-service.yaml for improved consistency and organization.

* Add COST_ENVIRONMENT variable to CloudFormation Makefile for production and testing environments, and include it in stack creation and update commands for improved tagging consistency.

* revert ecs service
2026-05-20 13:48:30 +02:00
Anders GustafssonandGitHub e82457e45a 8942 - clear product-category cache on mutation (#194) 2026-05-12 14:01:06 +02:00
Erik TiekstraandGitHub c996cf53e0 9101: Added new filters for motifs (#193) 2026-05-04 09:55:51 +02:00
Erik TiekstraandGitHub bbb750e5f4 9028: added pattern and extra wide option to facets (#191) 2026-04-02 07:14:50 +02:00
Anders GustafssonandGitHub d25a8878f0 8832 - Add style to facets (#188) 2026-03-03 09:57:11 +01:00
Anders GustafssonandGitHub f9e670e1c3 7960 Add categories v2 to product endpoint (#187) 2026-02-09 08:46:06 +01:00
Anders GustafssonandGitHub 2f86495a88 Revert "7960 - Add categories_v2 field on Product (#180)" (#186)
This reverts commit 3bef054684.
2026-02-05 14:54:37 +01:00
Anders GustafssonandGitHub 3bef054684 7960 - Add categories_v2 field on Product (#180) 2026-02-05 14:53:14 +01:00
8 changed files with 194 additions and 56 deletions
+5 -3
View File
@@ -28,6 +28,7 @@ DOCKER_IMAGE=$(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/$(STACK_NAME)
ifeq ($(STACK_ENVIRONMENT_NAME),production) ifeq ($(STACK_ENVIRONMENT_NAME),production)
SUBDOMAIN=$(STACK_NAME) SUBDOMAIN=$(STACK_NAME)
ENVIRONMENT_NAME=production ENVIRONMENT_NAME=production
COST_ENVIRONMENT=production
BERNARD_QUEUE_ARN=arn:aws:sqs:eu-west-1:954747537408:photowall-bernard-production BERNARD_QUEUE_ARN=arn:aws:sqs:eu-west-1:954747537408:photowall-bernard-production
SAMPLE_RIPPER_QUEUE_ARN=$(SAMPLE_RIPPER_QUEUE_ARN_PRODUCTION) SAMPLE_RIPPER_QUEUE_ARN=$(SAMPLE_RIPPER_QUEUE_ARN_PRODUCTION)
GITHUB_BRANCH=master GITHUB_BRANCH=master
@@ -39,6 +40,7 @@ ifeq ($(STACK_ENVIRONMENT_NAME),production)
else else
SUBDOMAIN=$(STACK_NAME)-$(STACK_ENVIRONMENT_NAME) SUBDOMAIN=$(STACK_NAME)-$(STACK_ENVIRONMENT_NAME)
ENVIRONMENT_NAME=$(STACK_ENVIRONMENT_NAME) ENVIRONMENT_NAME=$(STACK_ENVIRONMENT_NAME)
COST_ENVIRONMENT=testing
BERNARD_QUEUE_ARN=arn:aws:sqs:eu-west-1:954747537408:photowall-bernard-stage BERNARD_QUEUE_ARN=arn:aws:sqs:eu-west-1:954747537408:photowall-bernard-stage
SAMPLE_RIPPER_QUEUE_ARN=$(SAMPLE_RIPPER_QUEUE_ARN_STAGING) SAMPLE_RIPPER_QUEUE_ARN=$(SAMPLE_RIPPER_QUEUE_ARN_STAGING)
GITHUB_BRANCH=$(STACK_ENVIRONMENT_NAME) GITHUB_BRANCH=$(STACK_ENVIRONMENT_NAME)
@@ -97,7 +99,7 @@ delete-stack:
create-stack: check-create-variables-set check-create-initial-ecr-image create-stack: check-create-variables-set check-create-initial-ecr-image
aws cloudformation create-stack --stack-name $(STACK_NAME)-$(STACK_ENVIRONMENT_NAME) --template-body file://ecs-service.yaml \ aws cloudformation create-stack --stack-name $(STACK_NAME)-$(STACK_ENVIRONMENT_NAME) --template-body file://ecs-service.yaml \
--capabilities CAPABILITY_NAMED_IAM \ --capabilities CAPABILITY_NAMED_IAM \
--tags Key=EnvironmentName,Value=$(STACK_ENVIRONMENT_NAME) \ --tags Key=EnvironmentName,Value=$(STACK_ENVIRONMENT_NAME) Key=Project,Value=GraphQLServer Key=CostEnvironment,Value=$(COST_ENVIRONMENT) \
--parameters ParameterKey="EnvironmentName",ParameterValue="$(ENVIRONMENT_NAME)" \ --parameters ParameterKey="EnvironmentName",ParameterValue="$(ENVIRONMENT_NAME)" \
ParameterKey="BernardQueueArn",ParameterValue="${BERNARD_QUEUE_ARN}" \ ParameterKey="BernardQueueArn",ParameterValue="${BERNARD_QUEUE_ARN}" \
ParameterKey="SampleRipperQueueArn",ParameterValue="${SAMPLE_RIPPER_QUEUE_ARN}" \ ParameterKey="SampleRipperQueueArn",ParameterValue="${SAMPLE_RIPPER_QUEUE_ARN}" \
@@ -117,14 +119,14 @@ update-stack: set-repository-lifecycle-policy
$(call checkdef,ENVIRONMENT_NAME) $(call checkdef,ENVIRONMENT_NAME)
aws cloudformation update-stack --stack-name $(STACK_NAME)-$(STACK_ENVIRONMENT_NAME) --template-body file://ecs-service.yaml \ aws cloudformation update-stack --stack-name $(STACK_NAME)-$(STACK_ENVIRONMENT_NAME) --template-body file://ecs-service.yaml \
--capabilities CAPABILITY_NAMED_IAM \ --capabilities CAPABILITY_NAMED_IAM \
--tags Key=EnvironmentName,Value=$(STACK_ENVIRONMENT_NAME) \ --tags Key=EnvironmentName,Value=$(STACK_ENVIRONMENT_NAME) Key=Project,Value=GraphQLServer Key=CostEnvironment,Value=$(COST_ENVIRONMENT) \
--parameters ParameterKey="EnvironmentName",UsePreviousValue=true \ --parameters ParameterKey="EnvironmentName",UsePreviousValue=true \
ParameterKey="BernardQueueArn",ParameterValue="${BERNARD_QUEUE_ARN}" \ ParameterKey="BernardQueueArn",ParameterValue="${BERNARD_QUEUE_ARN}" \
ParameterKey="SampleRipperQueueArn",ParameterValue="${SAMPLE_RIPPER_QUEUE_ARN}" \ ParameterKey="SampleRipperQueueArn",ParameterValue="${SAMPLE_RIPPER_QUEUE_ARN}" \
ParameterKey="Image",UsePreviousValue=true \ ParameterKey="Image",UsePreviousValue=true \
ParameterKey="Subdomain",UsePreviousValue=true \ ParameterKey="Subdomain",UsePreviousValue=true \
ParameterKey="Certificate",UsePreviousValue=true \ ParameterKey="Certificate",UsePreviousValue=true \
ParameterKey="DatabaseSecurityGroup",,UsePreviousValue=true \ ParameterKey="DatabaseSecurityGroup",UsePreviousValue=true \
ParameterKey="GitHubRepo",ParameterValue="$(GITHUB_REPO)" \ ParameterKey="GitHubRepo",ParameterValue="$(GITHUB_REPO)" \
ParameterKey="GitHubBranch",UsePreviousValue=true \ ParameterKey="GitHubBranch",UsePreviousValue=true \
ParameterKey="CpuSize",ParameterValue="$(CPU_SIZE)" \ ParameterKey="CpuSize",ParameterValue="$(CPU_SIZE)" \
+117 -37
View File
@@ -3,7 +3,18 @@ import { ScopeAccess, Scopes } from '../cognito/access-control';
import { Category, CategoryLocaleData } from '../types/category-types'; import { Category, CategoryLocaleData } from '../types/category-types';
import { DataSourceOptions } from '../types/types'; import { DataSourceOptions } from '../types/types';
import { BaseSQLDataSource } from './BaseSQLDataSource'; import { BaseSQLDataSource } from './BaseSQLDataSource';
import { createQuestionMarksFromList, MINUTE } from './utils'; import { createQuestionMarksFromList, HOUR, MINUTE } from './utils';
const ROOMS_PARENT_ID = 270;
const COLORS_PARENT_ID = 1665;
const STYLES_PARENT_ID = 1814;
const PATTERNS_PARENT_ID = 1414;
const KNOWN_FACET_PARENT_IDS = [
ROOMS_PARENT_ID,
COLORS_PARENT_ID,
STYLES_PARENT_ID,
PATTERNS_PARENT_ID,
];
export class CategoryAPI extends BaseSQLDataSource { export class CategoryAPI extends BaseSQLDataSource {
private categoryLoader: DataLoader<number, Category>; private categoryLoader: DataLoader<number, Category>;
@@ -65,65 +76,114 @@ WHERE product_category.product_id = ?
return res; return res;
} }
async getCategoryBasedFacets( async getProductCategoriesV2(productId: number): Promise<Array<Category>> {
productId: number, ScopeAccess.validate(this.user).all([Scopes.CATEGORIES_READ]);
): Promise<{ room: string[]; color: string[] }> { const query = /* sql */ `
SELECT product_category.category_id as id,
categories.*
FROM product_category
JOIN v_categorytree categories ON product_category.category_id = categories.id
WHERE product_category.product_id = ?
`;
const res = await this.knex.raw(query, productId).then((data) =>
data.rows.map((row) => {
return {
...row,
pathNumeric: row.path_numeric,
};
}),
);
return res;
}
async getMotifRootIds(): Promise<number[]> {
const { rows } = await this.cachedRaw(
/* sql */ `
SELECT id
FROM v_categorytree
WHERE depth = 1
AND id NOT IN (${createQuestionMarksFromList(KNOWN_FACET_PARENT_IDS)})
`,
KNOWN_FACET_PARENT_IDS,
)
// @ts-ignore
.cache(HOUR);
return rows.map((row) => row.id);
}
async getCategoryBasedFacets(productId: number): Promise<{
room: string[];
color: string[];
style: string[];
pattern: string[];
motif: string[];
}> {
ScopeAccess.validate(this.user).some([ ScopeAccess.validate(this.user).some([
Scopes.CATEGORIES_READ, Scopes.CATEGORIES_READ,
Scopes.CATEGORIES_PUBLIC_READ, Scopes.CATEGORIES_PUBLIC_READ,
]); ]);
const ROOMS_PARENT_ID = 270; const motifRootIds = await this.getMotifRootIds();
const COLORS_PARENT_ID = 1665; const allFacetParentIds = [...KNOWN_FACET_PARENT_IDS, ...motifRootIds];
const { rows } = await this.cachedRaw( const { rows } = await this.cachedRaw(
` /* sql */ `
WITH rooms AS ( WITH facet_parents AS (
SELECT lft, rgt FROM categories WHERE id = ? SELECT id, lft, rgt, name,
), CASE
colors AS ( WHEN id = ? THEN 'room'
SELECT lft, rgt FROM categories WHERE id = ? WHEN id = ? THEN 'color'
WHEN id = ? THEN 'style'
WHEN id = ? THEN 'pattern'
ELSE 'motif'
END AS facet
FROM categories
WHERE id IN (${createQuestionMarksFromList(allFacetParentIds)})
) )
SELECT SELECT
array_remove( array_agg(DISTINCT c.name) FILTER (WHERE fp.facet = 'room') AS room,
array_agg( array_agg(DISTINCT c.name) FILTER (WHERE fp.facet = 'color') AS color,
DISTINCT CASE array_agg(DISTINCT c.name) FILTER (WHERE fp.facet = 'style') AS style,
WHEN c.lft > rooms.lft AND c.rgt < rooms.rgt THEN c.name array_agg(DISTINCT c.name) FILTER (WHERE fp.facet = 'pattern') AS pattern,
END array_agg(DISTINCT fp.name) FILTER (WHERE fp.facet = 'motif') AS motif
),
NULL
) AS room,
array_remove(
array_agg(
DISTINCT CASE
WHEN c.lft > colors.lft AND c.rgt < colors.rgt THEN c.name
END
),
NULL
) AS color
FROM product_category pc FROM product_category pc
JOIN categories c ON c.id = pc.category_id JOIN categories c ON c.id = pc.category_id
CROSS JOIN rooms JOIN facet_parents fp ON c.lft > fp.lft AND c.rgt < fp.rgt
CROSS JOIN colors
WHERE pc.product_id = ? WHERE pc.product_id = ?
AND (
(c.lft > rooms.lft AND c.rgt < rooms.rgt)
OR
(c.lft > colors.lft AND c.rgt < colors.rgt)
)
`, `,
[ROOMS_PARENT_ID, COLORS_PARENT_ID, productId], [
ROOMS_PARENT_ID,
COLORS_PARENT_ID,
STYLES_PARENT_ID,
PATTERNS_PARENT_ID,
...allFacetParentIds,
productId,
],
) )
// @ts-ignore // @ts-ignore
.cache(MINUTE * 5); .cache(MINUTE * 5);
const facetRow = rows[0] ?? { room: [], color: [] }; const facetRow = rows[0] ?? {
room: [],
color: [],
style: [],
pattern: [],
motif: [],
};
const roomCategories: string[] = facetRow.room ?? []; const roomCategories: string[] = facetRow.room ?? [];
const colorCategories: string[] = facetRow.color ?? []; const colorCategories: string[] = facetRow.color ?? [];
const styleCategories: string[] = facetRow.style ?? [];
const patternCategories: string[] = facetRow.pattern ?? [];
const motifCategories: string[] = facetRow.motif ?? [];
return { return {
room: roomCategories, room: roomCategories,
color: colorCategories, color: colorCategories,
style: styleCategories,
pattern: patternCategories,
motif: motifCategories,
}; };
} }
@@ -153,6 +213,26 @@ WHERE product_category.product_id = ?
); );
} }
async getCategoriesV2(ids: number[]): Promise<Array<Category>> {
ScopeAccess.validate(this.user).some([
Scopes.CATEGORIES_READ,
Scopes.CATEGORIES_PUBLIC_READ,
]);
return (
this.knex
.select('*')
.modify((queryBuilder) => {
if (ids) {
queryBuilder.whereIn('id', ids);
}
})
.from('v_categorytree')
// @ts-ignore
.cache(MINUTE)
);
}
async searchCategories(name: string): Promise<Array<Category>> { async searchCategories(name: string): Promise<Array<Category>> {
ScopeAccess.validate(this.user).all([Scopes.CATEGORIES_READ]); ScopeAccess.validate(this.user).all([Scopes.CATEGORIES_READ]);
return this.knex return this.knex
+6
View File
@@ -851,6 +851,12 @@ export class ProductAPI extends BaseSQLDataSource {
ProductMaterials.PREMIUM_WALLPAPER, ProductMaterials.PREMIUM_WALLPAPER,
]), ]),
); );
insertMaterials.push(
this.knex.raw(insertMaterialQuery, [
printid,
ProductMaterials.MATTE_WALLPAPER,
]),
);
break; break;
case ProductGroup.CANVAS: case ProductGroup.CANVAS:
insertMaterials.push( insertMaterials.push(
+4
View File
@@ -8,6 +8,7 @@ import {
type InputType = string | undefined | null; type InputType = string | undefined | null;
export const MINUTE = 60; export const MINUTE = 60;
export const HOUR = 3600;
function isNumeric(n: InputType) { function isNumeric(n: InputType) {
return !isNaN(parseFloat(n)); return !isNaN(parseFloat(n));
@@ -82,6 +83,9 @@ export const createQuestionMarksFromList = (arr: unknown[]): string => {
return '?,'.repeat(arr.length).slice(0, -1); return '?,'.repeat(arr.length).slice(0, -1);
}; };
export const sanitizeFacetValues = (values: string[]): string[] =>
values.map((v) => v.toLowerCase().replace(/,/g, ''));
export const isRepeatingPattern = (product: Product): boolean => { export const isRepeatingPattern = (product: Product): boolean => {
const found = product.printProducts.find( const found = product.printProducts.find(
(pr) => pr.groupId === ProductGroup.WALLPAPER, (pr) => pr.groupId === ProductGroup.WALLPAPER,
+5
View File
@@ -26,6 +26,10 @@ async function getCategories(_, { ids }, { dataSources }) {
return (<CategoryAPI>dataSources.categoryApi).getCategories(ids); return (<CategoryAPI>dataSources.categoryApi).getCategories(ids);
} }
async function getCategoriesV2(_, { ids }, { dataSources }) {
return (<CategoryAPI>dataSources.categoryApi).getCategoriesV2(ids);
}
async function getCategory(_, { id }, { dataSources }) { async function getCategory(_, { id }, { dataSources }) {
return (<CategoryAPI>dataSources.categoryApi).getCategoryById(id); return (<CategoryAPI>dataSources.categoryApi).getCategoryById(id);
} }
@@ -34,5 +38,6 @@ export const categoryTypeDefs = { Category };
export const categoryQueryTypeDefs = { export const categoryQueryTypeDefs = {
categories: getCategories, categories: getCategories,
categories_v2: getCategoriesV2,
category: getCategory, category: getCategory,
}; };
+54 -16
View File
@@ -1,9 +1,8 @@
import { ProductAPI } from '../datasources/product-api'; import { ProductAPI } from '../datasources/product-api';
import { import { Orientation, ProductGroup } from '../types/product-types';
Orientation, import type {
PrintProduct, PrintProduct as PrintProductType,
Product, Product as ProductType,
ProductGroup,
ProductListResult, ProductListResult,
ProductsFilterInput, ProductsFilterInput,
ProductsSearchResult, ProductsSearchResult,
@@ -16,16 +15,22 @@ import { Keyword } from '../types/keyword-types';
import { MarketLocaleAPI } from '../datasources/market-locale-api'; import { MarketLocaleAPI } from '../datasources/market-locale-api';
import { InteriorAPI } from '../datasources/interior-api'; import { InteriorAPI } from '../datasources/interior-api';
import { InteriorsLambdaAPI } from '../datasources/interiors-lambda-api'; import { InteriorsLambdaAPI } from '../datasources/interiors-lambda-api';
import { IdNumberResult, MarketInput } from '../types/types'; import type { IdNumberResult } from '../types/types';
import { moveS3File } from '../aws/s3'; import { moveS3File } from '../aws/s3';
import { DesignerAPI } from '../datasources/designer-api'; import { DesignerAPI } from '../datasources/designer-api';
import { PrintProductDefaultsAPI } from '../datasources/printproduct-defaults-api'; import { PrintProductDefaultsAPI } from '../datasources/printproduct-defaults-api';
import { PrintProductDefaults } from '../types/printproduct-defaults-types'; import { PrintProductDefaults } from '../types/printproduct-defaults-types';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { getOrientationString, isRepeatingPattern } from '../datasources/utils'; import {
getOrientationString,
isRepeatingPattern,
sanitizeFacetValues,
} from '../datasources/utils';
import { sendMessage } from '../aws/sqs'; import { sendMessage } from '../aws/sqs';
import { sendProductUpdatedEvent } from '../bernard/client'; import { sendProductUpdatedEvent } from '../bernard/client';
const EXTRA_WIDE_RATIO_LIMIT = 2.4;
const ProductBlacklist = { const ProductBlacklist = {
async market(parent, _args, { dataSources }) { async market(parent, _args, { dataSources }) {
return (<MarketLocaleAPI>dataSources.marketLocaleApi).getMarketById( return (<MarketLocaleAPI>dataSources.marketLocaleApi).getMarketById(
@@ -36,7 +41,7 @@ const ProductBlacklist = {
const Product = { const Product = {
async printProducts( async printProducts(
product: { printProducts: PrintProduct[] }, product: { printProducts: PrintProductType[] },
input: { groups: ProductGroup[] }, input: { groups: ProductGroup[] },
) { ) {
if (!input.groups) { if (!input.groups) {
@@ -49,6 +54,13 @@ const Product = {
async categories({ id }, _args, { dataSources }): Promise<Array<Category>> { async categories({ id }, _args, { dataSources }): Promise<Array<Category>> {
return (<CategoryAPI>dataSources.categoryApi).getProductCategories(id); return (<CategoryAPI>dataSources.categoryApi).getProductCategories(id);
}, },
async categories_v2(
{ id },
_args,
{ dataSources },
): Promise<Array<Category>> {
return (<CategoryAPI>dataSources.categoryApi).getProductCategoriesV2(id);
},
async collections({ id }, _args, { dataSources }) { async collections({ id }, _args, { dataSources }) {
return dataSources.collectionApi.getProductCollections(id); return dataSources.collectionApi.getProductCollections(id);
}, },
@@ -64,7 +76,7 @@ const Product = {
async related({ id }, _args, { dataSources }) { async related({ id }, _args, { dataSources }) {
return (<ProductAPI>dataSources.productApi).getRelatedProducts(id); return (<ProductAPI>dataSources.productApi).getRelatedProducts(id);
}, },
async facets(product: Product, _args, { dataSources }) { async facets(product: ProductType, _args, { dataSources }) {
const keywords = await (<KeywordAPI>( const keywords = await (<KeywordAPI>(
dataSources.keywordApi dataSources.keywordApi
)).getProductKeywords(product.id); )).getProductKeywords(product.id);
@@ -109,17 +121,41 @@ const Product = {
}), }),
]); ]);
const patterns = isRepeating ? categoryBasedFacets.pattern : [];
const motifs = isRepeating ? [] : categoryBasedFacets.motif;
return [ return [
{ {
attribute: 'color', attribute: 'color',
values: [...colors], values: sanitizeFacetValues([...colors]),
}, },
{ {
attribute: 'orientation', attribute: 'orientation',
values: orientations.map((value) => value.toLowerCase()), values: orientations.map((value) => {
if (
product.fields.width / product.fields.height >
EXTRA_WIDE_RATIO_LIMIT
) {
return 'extra-wide';
}
return value.toLowerCase();
}),
},
{ attribute: 'type', values: sanitizeFacetValues(types) },
{
attribute: 'room',
values: sanitizeFacetValues(categoryBasedFacets.room),
},
{
attribute: 'style',
values: sanitizeFacetValues(categoryBasedFacets.style),
},
{ attribute: 'pattern', values: sanitizeFacetValues(patterns) },
{
attribute: 'motif',
values: sanitizeFacetValues(motifs),
}, },
{ attribute: 'type', values: types },
{ attribute: 'room', values: categoryBasedFacets.room },
]; ];
}, },
}; };
@@ -177,7 +213,7 @@ async function getProductsSearchResult(
]).then((d) => { ]).then((d) => {
// flatten, remove falsy (each searchBy... can return undefined above) // flatten, remove falsy (each searchBy... can return undefined above)
// and finally remove duplicates. // and finally remove duplicates.
const products = {} as { [key: string]: Product }; const products = {} as { [key: string]: ProductType };
d.flat().forEach((product) => { d.flat().forEach((product) => {
if (product && !products[product.id]) { if (product && !products[product.id]) {
products[product.id] = product; products[product.id] = product;
@@ -208,7 +244,7 @@ async function getProductsSearchResult(
}; };
} }
async function getProduct(_, { id }, { dataSources }): Promise<Product> { async function getProduct(_, { id }, { dataSources }): Promise<ProductType> {
return (<ProductAPI>dataSources.productApi).getProduct(id); return (<ProductAPI>dataSources.productApi).getProduct(id);
} }
@@ -216,7 +252,7 @@ async function getProductByPath(
_, _,
{ path }, { path },
{ dataSources }, { dataSources },
): Promise<Product> { ): Promise<ProductType> {
return (<ProductAPI>dataSources.productApi).getProductByPath(path); return (<ProductAPI>dataSources.productApi).getProductByPath(path);
} }
@@ -547,6 +583,7 @@ export const productMutationTypeDefs = {
productId, productId,
categoryIds, categoryIds,
); );
await sendProductUpdatedEvent([productId], ['categories']);
return (<ProductAPI>dataSources.productApi).getProduct(productId); return (<ProductAPI>dataSources.productApi).getProduct(productId);
}, },
@@ -563,6 +600,7 @@ export const productMutationTypeDefs = {
productId, productId,
categoryIds, categoryIds,
); );
await sendProductUpdatedEvent([productId], ['categories']);
return (<ProductAPI>dataSources.productApi).getProduct(productId); return (<ProductAPI>dataSources.productApi).getProduct(productId);
}, },
+2
View File
@@ -28,6 +28,7 @@ type Query {
productByPath(path: String!): Product productByPath(path: String!): Product
productsSearch(q: String!): ProductsSearchResult productsSearch(q: String!): ProductsSearchResult
categories(ids: [Int]): [Category] categories(ids: [Int]): [Category]
categories_v2(ids: [Int]): [Category]
category(id: Int!): Category category(id: Int!): Category
keywords: [Keyword] keywords: [Keyword]
keyword(id: Int!): Keyword keyword(id: Int!): Keyword
@@ -262,6 +263,7 @@ type Product {
printProducts(groups: [ProductGroup]): [PrintProduct] printProducts(groups: [ProductGroup]): [PrintProduct]
blacklisting: [ProductBlacklist] blacklisting: [ProductBlacklist]
categories: [Category] categories: [Category]
categories_v2: [Category]
collections: [Collection] collections: [Collection]
designerId: Int designerId: Int
designer: Designer designer: Designer
+1
View File
@@ -84,6 +84,7 @@ export enum ProductMaterials {
PREMIUM_WALLPAPER = 4, PREMIUM_WALLPAPER = 4,
PREMIUM_POSTER = 5, PREMIUM_POSTER = 5,
PREMIUM_FRAMED_PRINT = 6, PREMIUM_FRAMED_PRINT = 6,
MATTE_WALLPAPER = 7,
} }
export enum ProductType { export enum ProductType {