Add keywords and product types
This commit is contained in:
+46
-1
@@ -156,13 +156,17 @@
|
|||||||
{
|
{
|
||||||
product(id: 73803) {
|
product(id: 73803) {
|
||||||
id
|
id
|
||||||
|
keywords {
|
||||||
|
id
|
||||||
|
value
|
||||||
|
type
|
||||||
|
}
|
||||||
fields_json
|
fields_json
|
||||||
inserted
|
inserted
|
||||||
path
|
path
|
||||||
updated
|
updated
|
||||||
visible
|
visible
|
||||||
browsable
|
browsable
|
||||||
api1json
|
|
||||||
categories {
|
categories {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -191,3 +195,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Categories
|
||||||
|
{
|
||||||
|
category(id: 1653) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
path
|
||||||
|
isLeaf
|
||||||
|
depth
|
||||||
|
childCount
|
||||||
|
lft
|
||||||
|
rgt
|
||||||
|
products {
|
||||||
|
id
|
||||||
|
fields_json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keywords
|
||||||
|
{
|
||||||
|
keywords {
|
||||||
|
id
|
||||||
|
value
|
||||||
|
type
|
||||||
|
products {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
keyword(id: 618) {
|
||||||
|
id
|
||||||
|
value
|
||||||
|
type
|
||||||
|
products {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { SQLDataSource } from 'datasource-sql';
|
||||||
|
import { Keyword, KeywordType } from '../types/keyword-types';
|
||||||
|
|
||||||
|
const MINUTE = 60;
|
||||||
|
|
||||||
|
export class KeywordAPI extends SQLDataSource {
|
||||||
|
constructor(config) {
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
getType(data: any) {
|
||||||
|
// raw queries doesn't camelCase...
|
||||||
|
const typeObject = data.type_id ?? data.typeId;
|
||||||
|
const typeId = typeObject ?? 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
type: KeywordType[typeId],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProductKeywords(productId: number): Promise<Array<Keyword>> {
|
||||||
|
const query = /* sql */ `
|
||||||
|
SELECT keywords.*, keyword_type.type_id FROM keywords
|
||||||
|
LEFT JOIN keyword_type ON keyword_type.keyword_id = keywords.id
|
||||||
|
JOIN product_keyword ON product_keyword.keyword_id = keywords.id
|
||||||
|
WHERE product_keyword.product_id = ?
|
||||||
|
ORDER BY keywords.value
|
||||||
|
`;
|
||||||
|
|
||||||
|
const res = await this.knex
|
||||||
|
.raw(query, productId)
|
||||||
|
.then((data) => data.rows.map((row) => this.getType(row)));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKeywords(): Promise<Array<Keyword>> {
|
||||||
|
const query = /* sql */ `
|
||||||
|
SELECT keywords.*, keyword_type.type_id FROM keywords
|
||||||
|
LEFT JOIN keyword_type ON keyword_type.keyword_id = keywords.id
|
||||||
|
ORDER BY keywords.value
|
||||||
|
`;
|
||||||
|
const res = await this.knex
|
||||||
|
.raw(query)
|
||||||
|
.then((data) => data.rows.map((row) => this.getType(row)));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKeywordById(id: number): Promise<Keyword> {
|
||||||
|
const res = await this.knex
|
||||||
|
.select('*')
|
||||||
|
.from('keywords')
|
||||||
|
.leftJoin('keyword_type', 'keyword_type.keyword_id', 'keywords.id')
|
||||||
|
.where('id', id)
|
||||||
|
.first()
|
||||||
|
.cache(MINUTE)
|
||||||
|
.then((row) => this.getType(row));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* category keywords
|
||||||
|
*
|
||||||
|
SELECT category_keyword.category_id, keywords.value
|
||||||
|
FROM category_keyword
|
||||||
|
JOIN keywords ON category_keyword.keyword_id = keywords.id;
|
||||||
|
|
||||||
|
|
||||||
|
FROM categorydata cd
|
||||||
|
JOIN categorydatakeys cdk ON cd.datakey_id = cdk.id
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Product,
|
Product,
|
||||||
ProductGroup,
|
ProductGroup,
|
||||||
PrintProduct,
|
PrintProduct,
|
||||||
|
ProductType,
|
||||||
ProductBlacklist,
|
ProductBlacklist,
|
||||||
} from '../types/product-types';
|
} from '../types/product-types';
|
||||||
import { Maybe } from '../types/types';
|
import { Maybe } from '../types/types';
|
||||||
@@ -40,9 +41,25 @@ export class ProductAPI extends SQLDataSource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProductTypes(row: any): Array<ProductType> {
|
||||||
|
if (!row.types) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return row.types.map((type: any) => {
|
||||||
|
if (type === 'typeillustration') {
|
||||||
|
return ProductType[ProductType.ILLUSTRATION];
|
||||||
|
}
|
||||||
|
if (type === 'typephotography') {
|
||||||
|
return ProductType[ProductType.PHOTO];
|
||||||
|
}
|
||||||
|
return ProductType[ProductType.UNKNOWN];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
createProductFromRow(row: any) {
|
createProductFromRow(row: any) {
|
||||||
row.blacklisting = this.getBlacklisting(row);
|
row.blacklisting = this.getBlacklisting(row);
|
||||||
row.printProducts = this.getPrintProducts(row);
|
row.printProducts = this.getPrintProducts(row);
|
||||||
|
row.type = this.getProductTypes(row);
|
||||||
row.designerId = row.designerid;
|
row.designerId = row.designerid;
|
||||||
row.fields_json = row.fields;
|
row.fields_json = row.fields;
|
||||||
return row;
|
return row;
|
||||||
@@ -80,32 +97,12 @@ export class ProductAPI extends SQLDataSource {
|
|||||||
.raw(query)
|
.raw(query)
|
||||||
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getKeywordProducts(keywordId: number): Promise<Array<Product>> {
|
||||||
|
const query = sql.keywordProducts(keywordId);
|
||||||
|
|
||||||
|
return await this.knex
|
||||||
|
.raw(query)
|
||||||
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* SELECT product_keyword.product_id, keywords.value
|
|
||||||
FROM product_keyword
|
|
||||||
JOIN keywords ON product_keyword.keyword_id = keywords.id;
|
|
||||||
*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
keywords
|
|
||||||
id, value
|
|
||||||
|
|
||||||
|
|
||||||
keyword_type:
|
|
||||||
keyword_id, type_id
|
|
||||||
|
|
||||||
keywordstypes
|
|
||||||
id, name (color)
|
|
||||||
|
|
||||||
|
|
||||||
keywords_i18n NOT used it seems and misses some languages
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -48,7 +48,12 @@ SELECT products.productid as id,
|
|||||||
FROM "product-products_fields" pf
|
FROM "product-products_fields" pf
|
||||||
JOIN "product-fields" fields ON fields.fieldid = pf.fieldid
|
JOIN "product-fields" fields ON fields.fieldid = pf.fieldid
|
||||||
WHERE pf.productid = products.productid
|
WHERE pf.productid = products.productid
|
||||||
) AS fields
|
) AS fields,
|
||||||
|
( SELECT json_agg(keywords.value)
|
||||||
|
FROM product_keyword pk
|
||||||
|
JOIN keywords ON keywords.id = pk.keyword_id
|
||||||
|
WHERE keywords.id IN (1103,1104)AND pk.product_id = products.productid
|
||||||
|
) AS types
|
||||||
FROM "product-products" products
|
FROM "product-products" products
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -89,3 +94,12 @@ export function categoryProducts(categoryId: number) {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function keywordProducts(keywordId: number) {
|
||||||
|
return (
|
||||||
|
baseQuery +
|
||||||
|
/* sql */ `
|
||||||
|
WHERE products.productid IN (SELECT product_id FROM product_keyword WHERE keyword_id = ${keywordId});
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { Api1DataSource } from './datasources/api1-datasource';
|
|||||||
|
|
||||||
import { verifyToken } from './cognito/cognito-client';
|
import { verifyToken } from './cognito/cognito-client';
|
||||||
import { CategoryAPI } from './datasources/category-api';
|
import { CategoryAPI } from './datasources/category-api';
|
||||||
|
import { KeywordAPI } from './datasources/keyword-api';
|
||||||
|
|
||||||
console.log(`dbConfig`, dbConfig);
|
console.log(`dbConfig`, dbConfig);
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ const api2 = new Api2DataSource();
|
|||||||
const orderApi = new OrderAPI(knexConfig);
|
const orderApi = new OrderAPI(knexConfig);
|
||||||
const productApi = new ProductAPI(knexConfig);
|
const productApi = new ProductAPI(knexConfig);
|
||||||
const categoryApi = new CategoryAPI(knexConfig);
|
const categoryApi = new CategoryAPI(knexConfig);
|
||||||
|
const keywordApi = new KeywordAPI(knexConfig);
|
||||||
const designerApi = new DesignerAPI(knexConfig);
|
const designerApi = new DesignerAPI(knexConfig);
|
||||||
|
|
||||||
const dataSources = () => ({
|
const dataSources = () => ({
|
||||||
@@ -33,6 +35,7 @@ const dataSources = () => ({
|
|||||||
api2,
|
api2,
|
||||||
categoryApi,
|
categoryApi,
|
||||||
designerApi,
|
designerApi,
|
||||||
|
keywordApi,
|
||||||
orderApi,
|
orderApi,
|
||||||
productApi,
|
productApi,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,17 +3,20 @@ import { productQueryTypeDefs, productTypeDefs } from './products-resolver';
|
|||||||
import { designerQueryTypeDefs, designerTypeDefs } from './designers-resolver';
|
import { designerQueryTypeDefs, designerTypeDefs } from './designers-resolver';
|
||||||
import { categoryQueryTypeDefs, categoryTypeDefs } from './categories-resolver';
|
import { categoryQueryTypeDefs, categoryTypeDefs } from './categories-resolver';
|
||||||
import { DateTimeResolver, DateResolver, JSONResolver } from 'graphql-scalars';
|
import { DateTimeResolver, DateResolver, JSONResolver } from 'graphql-scalars';
|
||||||
|
import { keywordsQueryTypeDefs, keywordTypeDefs } from './keywords-resolver';
|
||||||
const resolvers = {
|
const resolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
...orderQueryTypeDefs,
|
...orderQueryTypeDefs,
|
||||||
...productQueryTypeDefs,
|
...productQueryTypeDefs,
|
||||||
...designerQueryTypeDefs,
|
...designerQueryTypeDefs,
|
||||||
...categoryQueryTypeDefs,
|
...categoryQueryTypeDefs,
|
||||||
|
...keywordsQueryTypeDefs,
|
||||||
},
|
},
|
||||||
...orderTypeDefs,
|
...orderTypeDefs,
|
||||||
...productTypeDefs,
|
...productTypeDefs,
|
||||||
...designerTypeDefs,
|
...designerTypeDefs,
|
||||||
...categoryTypeDefs,
|
...categoryTypeDefs,
|
||||||
|
...keywordTypeDefs,
|
||||||
DateTime: DateTimeResolver,
|
DateTime: DateTimeResolver,
|
||||||
Date: DateResolver,
|
Date: DateResolver,
|
||||||
JSON: JSONResolver,
|
JSON: JSONResolver,
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { IResolverObject } from 'graphql-tools';
|
||||||
|
import { KeywordAPI } from '../datasources/keyword-api';
|
||||||
|
import { ProductAPI } from '../datasources/product-api';
|
||||||
|
|
||||||
|
const Keyword: IResolverObject = {
|
||||||
|
async products({ id }, _args, { dataSources }) {
|
||||||
|
return (<ProductAPI>dataSources.productApi).getKeywordProducts(id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getKeywords(_, _args, { dataSources }) {
|
||||||
|
return (<KeywordAPI>dataSources.keywordApi).getKeywords();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getKeyword(_, { id }, { dataSources }) {
|
||||||
|
return (<KeywordAPI>dataSources.keywordApi).getKeywordById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const keywordTypeDefs = { Keyword };
|
||||||
|
|
||||||
|
export const keywordsQueryTypeDefs = {
|
||||||
|
keywords: getKeywords,
|
||||||
|
keyword: getKeyword,
|
||||||
|
};
|
||||||
@@ -5,6 +5,8 @@ import { ProductAPI } from '../datasources/product-api';
|
|||||||
import { Product } from '../types/product-types';
|
import { Product } from '../types/product-types';
|
||||||
import { CategoryAPI } from '../datasources/category-api';
|
import { CategoryAPI } from '../datasources/category-api';
|
||||||
import { Category } from '../types/category-types';
|
import { Category } from '../types/category-types';
|
||||||
|
import { KeywordAPI } from '../datasources/keyword-api';
|
||||||
|
import { Keyword } from '../types/keyword-types';
|
||||||
|
|
||||||
const ProductBlacklist: IResolverObject = {
|
const ProductBlacklist: IResolverObject = {
|
||||||
async market(parent, _args, { dataSources }) {
|
async market(parent, _args, { dataSources }) {
|
||||||
@@ -20,6 +22,9 @@ const Product: IResolverObject = {
|
|||||||
async designer({ designerId }, args, { dataSources }) {
|
async designer({ designerId }, args, { dataSources }) {
|
||||||
return dataSources.designerApi.getDesignerById(designerId);
|
return dataSources.designerApi.getDesignerById(designerId);
|
||||||
},
|
},
|
||||||
|
async keywords({ id }, _args, { dataSources }): Promise<Array<Keyword>> {
|
||||||
|
return (<KeywordAPI>dataSources.keywordApi).getProductKeywords(id);
|
||||||
|
},
|
||||||
async api1json({ id }, _args, { dataSources }): Promise<JSON> {
|
async api1json({ id }, _args, { dataSources }): Promise<JSON> {
|
||||||
return dataSources.api1.getProduct(id);
|
return dataSources.api1.getProduct(id);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ const typeDefs = gql`
|
|||||||
product(id: Int!): Product
|
product(id: Int!): Product
|
||||||
categories: [Category]
|
categories: [Category]
|
||||||
category(id: Int!): Category
|
category(id: Int!): Category
|
||||||
|
keywords: [Keyword]
|
||||||
|
keyword(id: Int!): Keyword
|
||||||
designers(limit: Int): [Designer]
|
designers(limit: Int): [Designer]
|
||||||
designer(id: Int!): Designer
|
designer(id: Int!): Designer
|
||||||
}
|
}
|
||||||
@@ -190,6 +192,12 @@ const typeDefs = gql`
|
|||||||
categories: [Category]
|
categories: [Category]
|
||||||
designerId: Int
|
designerId: Int
|
||||||
designer: Designer
|
designer: Designer
|
||||||
|
keywords: [Keyword]
|
||||||
|
"""
|
||||||
|
Will be single value return in later release
|
||||||
|
"""
|
||||||
|
type: [ProductType]
|
||||||
|
# Below are for debug
|
||||||
api1json: JSON
|
api1json: JSON
|
||||||
fields_json: JSON
|
fields_json: JSON
|
||||||
}
|
}
|
||||||
@@ -206,6 +214,12 @@ const typeDefs = gql`
|
|||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ProductType {
|
||||||
|
UNKNOWN
|
||||||
|
PHOTO
|
||||||
|
ILLUSTRATION
|
||||||
|
}
|
||||||
|
|
||||||
type ProductBlacklist {
|
type ProductBlacklist {
|
||||||
id: ID!
|
id: ID!
|
||||||
groupId: Int!
|
groupId: Int!
|
||||||
@@ -231,6 +245,18 @@ const typeDefs = gql`
|
|||||||
path: String
|
path: String
|
||||||
orderRows(filter: FilterInput): [OrderRow]
|
orderRows(filter: FilterInput): [OrderRow]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum KeywordType {
|
||||||
|
NOT_SET
|
||||||
|
COLOR
|
||||||
|
}
|
||||||
|
|
||||||
|
type Keyword {
|
||||||
|
id: Int!
|
||||||
|
value: String!
|
||||||
|
type: KeywordType
|
||||||
|
products: [Product]
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export { typeDefs };
|
export { typeDefs };
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export enum KeywordType {
|
||||||
|
NOT_SET = 0,
|
||||||
|
COLOR = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Keyword {
|
||||||
|
id: number;
|
||||||
|
value: string;
|
||||||
|
type: KeywordType;
|
||||||
|
}
|
||||||
+30
-23
@@ -10,29 +10,10 @@ export enum ProductGroup {
|
|||||||
UNKNOWN = 9,
|
UNKNOWN = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getProductGroupStringFromString(group: string): string {
|
export enum ProductType {
|
||||||
return ProductGroup[getProductGroupFromString(group)];
|
UNKNOWN = 0,
|
||||||
}
|
PHOTO = 1,
|
||||||
|
ILLUSTRATION = 2,
|
||||||
export function getProductGroupFromString(group: string): ProductGroup {
|
|
||||||
switch (group) {
|
|
||||||
case 'canvas':
|
|
||||||
return ProductGroup.CANVAS;
|
|
||||||
case 'framed-print':
|
|
||||||
return ProductGroup.FRAMED_PRINT;
|
|
||||||
case 'photo-wallpaper':
|
|
||||||
return ProductGroup.PHOTO_WALLPAPER;
|
|
||||||
case 'poster':
|
|
||||||
return ProductGroup.POSTER;
|
|
||||||
case 'wallpaper':
|
|
||||||
return ProductGroup.WALLPAPER;
|
|
||||||
case 'design-wallpaper':
|
|
||||||
return ProductGroup.DESIGNER_WALLPAPER;
|
|
||||||
case 'doityourselfframe':
|
|
||||||
return ProductGroup.DO_IT_YOURSELF_FRAME;
|
|
||||||
default:
|
|
||||||
return ProductGroup.UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export interface ProductBlacklist {
|
export interface ProductBlacklist {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -60,6 +41,32 @@ export interface Product {
|
|||||||
updated?: Date;
|
updated?: Date;
|
||||||
printProducts: [PrintProduct];
|
printProducts: [PrintProduct];
|
||||||
blacklisting: [ProductBlacklist];
|
blacklisting: [ProductBlacklist];
|
||||||
|
type: [ProductType];
|
||||||
api1json: JSON;
|
api1json: JSON;
|
||||||
fields_json: JSON;
|
fields_json: JSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProductGroupStringFromString(group: string): string {
|
||||||
|
return ProductGroup[getProductGroupFromString(group)];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProductGroupFromString(group: string): ProductGroup {
|
||||||
|
switch (group) {
|
||||||
|
case 'canvas':
|
||||||
|
return ProductGroup.CANVAS;
|
||||||
|
case 'framed-print':
|
||||||
|
return ProductGroup.FRAMED_PRINT;
|
||||||
|
case 'photo-wallpaper':
|
||||||
|
return ProductGroup.PHOTO_WALLPAPER;
|
||||||
|
case 'poster':
|
||||||
|
return ProductGroup.POSTER;
|
||||||
|
case 'wallpaper':
|
||||||
|
return ProductGroup.WALLPAPER;
|
||||||
|
case 'design-wallpaper':
|
||||||
|
return ProductGroup.DESIGNER_WALLPAPER;
|
||||||
|
case 'doityourselfframe':
|
||||||
|
return ProductGroup.DO_IT_YOURSELF_FRAME;
|
||||||
|
default:
|
||||||
|
return ProductGroup.UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user