Compare commits
10
Commits
83155577ac
...
8608b28300
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8608b28300 | ||
|
|
eecc0f4f1b | ||
|
|
dcf41787d2 | ||
|
|
e82457e45a | ||
|
|
c996cf53e0 | ||
|
|
bbb750e5f4 | ||
|
|
d25a8878f0 | ||
|
|
f9e670e1c3 | ||
|
|
2f86495a88 | ||
|
|
3bef054684 |
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user