P5 7619 refactor products to return list (#11)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
|
||||
publish-to-staging:
|
||||
@current_branch=$$(git rev-parse --abbrev-ref HEAD); \
|
||||
git branch -D staging 2>/dev/null; \
|
||||
git checkout -b staging; \
|
||||
git merge $$current_branch && \
|
||||
git push -f origin staging && \
|
||||
git checkout $$current_branch; \
|
||||
echo Pushed $$current_branch to staging
|
||||
@@ -339,3 +339,48 @@
|
||||
priceAdjustment
|
||||
}
|
||||
}
|
||||
|
||||
# for pwinty
|
||||
{
|
||||
order(id: 562333) {
|
||||
id
|
||||
pwintyId
|
||||
locale {
|
||||
value
|
||||
}
|
||||
deliveryInformation {
|
||||
address {
|
||||
recipientName
|
||||
companyname
|
||||
address1
|
||||
address2
|
||||
city
|
||||
countryCode
|
||||
stateCountyOrRegion
|
||||
zipcode
|
||||
}
|
||||
email
|
||||
phone
|
||||
}
|
||||
billingInformation {
|
||||
address {
|
||||
recipientName
|
||||
companyname
|
||||
address1
|
||||
address2
|
||||
city
|
||||
countryCode
|
||||
stateCountyOrRegion
|
||||
zipcode
|
||||
}
|
||||
email
|
||||
phone
|
||||
}
|
||||
rows {
|
||||
id
|
||||
pwintySku
|
||||
pwintyImageId
|
||||
frameColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ describe('GraphQL Apollo tests', () => {
|
||||
id
|
||||
name
|
||||
path
|
||||
orderRows {
|
||||
orderRows(pagination: {limit: 1000}) {
|
||||
name
|
||||
artNo
|
||||
}
|
||||
@@ -156,7 +156,7 @@ describe('GraphQL Apollo tests', () => {
|
||||
id
|
||||
name
|
||||
path
|
||||
orderRows {
|
||||
orderRows(pagination: {limit: 1000}) {
|
||||
name,
|
||||
artNo
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ WHERE product_category.product_id = ?
|
||||
|
||||
/**
|
||||
* TODO: should we have these?
|
||||
* category keywords
|
||||
* category keywords
|
||||
*
|
||||
SELECT category_keyword.category_id, keywords.value
|
||||
FROM category_keyword
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Order,
|
||||
OrderRow,
|
||||
} from '../types/order-types';
|
||||
import { GeneralInput, Maybe } from '../types/types';
|
||||
import { DateFilterInput, GeneralInput, Maybe } from '../types/types';
|
||||
import { convertToPossibleType } from './utils';
|
||||
import { getProductGroupStringFromString } from '../types/product-types';
|
||||
|
||||
@@ -54,10 +54,9 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
}
|
||||
|
||||
createOrderFromRow(row: any): Order {
|
||||
row.__resolveType = 'Order';
|
||||
row.billingInformation = this.getBillingInformation(row);
|
||||
row.deliveryInformation = this.getDeliveryInformation(row);
|
||||
row.marketName = row.market;
|
||||
row.localeName = row.locale;
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -71,11 +70,9 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
}
|
||||
|
||||
async getOrders(input: Maybe<GeneralInput>): Promise<Array<Order>> {
|
||||
let query = this.getOrdersQuery(input);
|
||||
let query = this.getOrdersQuery(input).limit(input?.pagination.limit);
|
||||
|
||||
if (input?.limit) {
|
||||
query = query.limit(input?.limit);
|
||||
}
|
||||
// TODO: handle offset
|
||||
query = query.orderBy('inserted', 'ASC');
|
||||
return query
|
||||
.cache(MINUTE)
|
||||
@@ -84,12 +81,12 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
|
||||
private getOrdersQuery(input: GeneralInput) {
|
||||
let query = this.knex.table('orders');
|
||||
if (input?.dates?.from) {
|
||||
query = query.where('inserted', '>=', input.dates.from);
|
||||
if (input?.filter?.dates?.from) {
|
||||
query = query.where('inserted', '>=', input.filter.dates.from);
|
||||
}
|
||||
|
||||
if (input?.dates?.to) {
|
||||
query = query.where('inserted', '<=', input.dates.to);
|
||||
if (input?.filter?.dates?.to) {
|
||||
query = query.where('inserted', '<=', input.filter.dates.to);
|
||||
}
|
||||
|
||||
return query;
|
||||
@@ -190,14 +187,16 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
AND orderdetails.inserted >= ?
|
||||
AND orderdetails.inserted <= ?
|
||||
ORDER BY orderdetails.inserted ASC
|
||||
${input?.limit ? `LIMIT ${input.limit}` : ''}
|
||||
${input?.pagination.limit ? `LIMIT ${input.pagination.limit}` : ''}
|
||||
`;
|
||||
|
||||
const from = input?.dates?.from ? input.dates.from : new Date('2000-01-01');
|
||||
const from = input?.filter?.dates?.from
|
||||
? input.filter.dates.from
|
||||
: new Date('2000-01-01');
|
||||
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const to = input?.dates?.to ? input.dates.to : tomorrow; // Tomorrow
|
||||
const to = input?.filter?.dates?.to ? input.filter?.dates.to : tomorrow; // Tomorrow
|
||||
|
||||
const rows = await this.knex
|
||||
.raw(query, [designerId, this.getSQLDate(from), this.getSQLDate(to)])
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
ProductWallpaperType,
|
||||
ProductFields,
|
||||
Orientation,
|
||||
ProductsFilter,
|
||||
ProductsFilterInput,
|
||||
} from '../types/product-types';
|
||||
import { Maybe } from '../types/types';
|
||||
import { convertToPossibleType } from './utils';
|
||||
@@ -67,6 +69,9 @@ export class ProductAPI extends BaseSQLDataSource {
|
||||
}
|
||||
|
||||
getProductFields(row: any): ProductFields {
|
||||
if (!row.fields) {
|
||||
return null; // 53460 for example
|
||||
}
|
||||
const fields = Object.keys(row.fields).reduce((mem, key) => {
|
||||
mem[camelcase(key)] = convertToPossibleType(row.fields[key]);
|
||||
return mem;
|
||||
@@ -76,7 +81,7 @@ export class ProductAPI extends BaseSQLDataSource {
|
||||
}
|
||||
|
||||
getOrientation(fields: ProductFields): string {
|
||||
if (!fields.width || !fields.height) {
|
||||
if (!fields || !fields.width || !fields.height) {
|
||||
return Orientation[Orientation.UNKNOWN];
|
||||
}
|
||||
if (fields.width > fields.height) {
|
||||
@@ -100,13 +105,17 @@ export class ProductAPI extends BaseSQLDataSource {
|
||||
return row;
|
||||
}
|
||||
|
||||
async getProducts(
|
||||
limit: Maybe<number>,
|
||||
visible: Maybe<boolean>,
|
||||
browsable: Maybe<boolean>,
|
||||
): Promise<Array<Product>> {
|
||||
limit = limit ?? 100;
|
||||
const query = sql.products(limit, visible, browsable);
|
||||
async getProductsTotal(input: ProductsFilterInput): Promise<number> {
|
||||
const query = sql.productsTotal(input);
|
||||
const res = await this.cachedRaw(query)
|
||||
.cache(MINUTE * 60)
|
||||
.then((data) => data.rows);
|
||||
return convertToPossibleType(res[0]['count']); // Optimize later to return Promise
|
||||
}
|
||||
|
||||
async getProducts(input: ProductsFilterInput): Promise<Array<Product>> {
|
||||
// TODO: handle offset
|
||||
const query = sql.products(input);
|
||||
|
||||
return this.knex
|
||||
.raw(query)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ProductsFilter, ProductsFilterInput } from '../../types/product-types';
|
||||
import { Maybe } from '../../types/types';
|
||||
|
||||
// Get syntax highlighting with vscode by installing "Comment tagged templates2
|
||||
@@ -75,19 +76,17 @@ FROM "product-products" products
|
||||
LEFT JOIN "product-stockproducts" stock ON stock.productid = products.productid
|
||||
`;
|
||||
|
||||
export function products(
|
||||
limit: number = 100,
|
||||
visible: Maybe<boolean>,
|
||||
browsable: Maybe<boolean>,
|
||||
) {
|
||||
export function products(input: ProductsFilterInput) {
|
||||
const limit = input.pagination.limit ?? 100;
|
||||
const filter = input.filter;
|
||||
return (
|
||||
baseQuery +
|
||||
/* sql */ `
|
||||
${
|
||||
visible != null
|
||||
filter.visible != null
|
||||
? /* sql */ `
|
||||
WHERE products.visible = ${visible ? '1' : '0'}
|
||||
AND products.browsable = ${browsable ? '1' : '0'}`
|
||||
WHERE products.visible = ${filter?.visible ? '1' : '0'}
|
||||
AND products.browsable = ${filter?.browsable ? '1' : '0'}`
|
||||
: ``
|
||||
}
|
||||
LIMIT ${limit}
|
||||
@@ -95,6 +94,20 @@ LIMIT ${limit}
|
||||
);
|
||||
}
|
||||
|
||||
export function productsTotal(input: ProductsFilterInput) {
|
||||
const filter = input.filter;
|
||||
return /*sql */ `
|
||||
SELECT count(*) FROM "product-products" products
|
||||
${
|
||||
filter.visible != null
|
||||
? /* sql */ `
|
||||
WHERE products.visible = ${filter?.visible ? '1' : '0'}
|
||||
AND products.browsable = ${filter?.browsable ? '1' : '0'}`
|
||||
: ``
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export function product(id: number) {
|
||||
return (
|
||||
baseQuery +
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ async function main() {
|
||||
dataSources,
|
||||
context,
|
||||
introspection: true,
|
||||
playground: false,
|
||||
playground: true,
|
||||
});
|
||||
|
||||
await server.listen();
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { DesignerAPI } from '../datasources/designer-api';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { FilterInput } from '../types/types';
|
||||
import { GeneralInput } from '../types/types';
|
||||
|
||||
const Designer: IResolverObject = {
|
||||
async orderRows({ id }, args: FilterInput, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderRowsByDesignerId(
|
||||
id,
|
||||
args.filter,
|
||||
);
|
||||
async orderRows({ id }, input: GeneralInput, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderRowsByDesignerId(id, input);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { MarketLocaleAPI } from '../datasources/market-locale-api';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { FilterInput } from '../types/types';
|
||||
import { OrdersResult } from '../types/order-types';
|
||||
import { GeneralInput } from '../types/types';
|
||||
|
||||
const ContactInformation: IResolverObject = {
|
||||
// (parent, args, ctx, info)
|
||||
@@ -32,12 +33,20 @@ const OrderRow: IResolverObject = {
|
||||
},
|
||||
};
|
||||
|
||||
async function getOrdersResult(_, args: FilterInput, { dataSources }) {
|
||||
const total = (<OrderAPI>dataSources.orderApi).getOrdersTotal(args.filter);
|
||||
const items = (<OrderAPI>dataSources.orderApi).getOrders(args.filter);
|
||||
async function getOrdersResult(
|
||||
_,
|
||||
input: GeneralInput,
|
||||
{ dataSources },
|
||||
): Promise<OrdersResult> {
|
||||
const total = (<OrderAPI>dataSources.orderApi).getOrdersTotal(input);
|
||||
const items = (<OrderAPI>dataSources.orderApi).getOrders(input);
|
||||
return {
|
||||
total: total,
|
||||
items: items,
|
||||
pagination: {
|
||||
total: total,
|
||||
limit: input.pagination.limit,
|
||||
offset: 0, // TODO: this will be implemented later if the client app needs it
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { ProductAPI } from '../datasources/product-api';
|
||||
import { Product } from '../types/product-types';
|
||||
import {
|
||||
Product,
|
||||
ProductListResult,
|
||||
ProductsFilterInput,
|
||||
} from '../types/product-types';
|
||||
import { CategoryAPI } from '../datasources/category-api';
|
||||
import { Category } from '../types/category-types';
|
||||
import { KeywordAPI } from '../datasources/keyword-api';
|
||||
@@ -21,7 +25,7 @@ const Product: IResolverObject = {
|
||||
async categories({ id }, _args, { dataSources }): Promise<Array<Category>> {
|
||||
return (<CategoryAPI>dataSources.categoryApi).getProductCategories(id);
|
||||
},
|
||||
async designer({ designerId }, args, { dataSources }) {
|
||||
async designer({ designerId }, _args, { dataSources }) {
|
||||
if (!designerId) {
|
||||
return null;
|
||||
}
|
||||
@@ -41,16 +45,22 @@ const PrintProduct: IResolverObject = {
|
||||
},
|
||||
};
|
||||
|
||||
async function getProducts(
|
||||
async function getProductsResult(
|
||||
_,
|
||||
{ limit, visible, browsable },
|
||||
input: ProductsFilterInput,
|
||||
{ dataSources },
|
||||
): Promise<Array<Product>> {
|
||||
return (<ProductAPI>dataSources.productApi).getProducts(
|
||||
limit,
|
||||
visible,
|
||||
browsable,
|
||||
);
|
||||
): Promise<ProductListResult> {
|
||||
const total = (<ProductAPI>dataSources.productApi).getProductsTotal(input);
|
||||
const items = (<ProductAPI>dataSources.productApi).getProducts(input);
|
||||
|
||||
return {
|
||||
pagination: {
|
||||
limit: input.pagination.limit,
|
||||
offset: 0, // TODO: this will be implemented later if the client app needs it
|
||||
total: total,
|
||||
},
|
||||
items: items,
|
||||
};
|
||||
}
|
||||
|
||||
async function getProduct(_, { id }, { dataSources }): Promise<Product> {
|
||||
@@ -64,11 +74,11 @@ export const productTypeDefs = {
|
||||
};
|
||||
|
||||
export const productQueryTypeDefs = {
|
||||
products: getProducts,
|
||||
products: getProductsResult,
|
||||
product: getProduct,
|
||||
};
|
||||
|
||||
// Mutations below
|
||||
// Mutations below, embryo stage
|
||||
|
||||
export const productMutationTypeDefs = {
|
||||
addKeyword(_, { productId, keywordId }, { dataSources }) {
|
||||
|
||||
+49
-30
@@ -6,12 +6,12 @@ const typeDefs = gql`
|
||||
scalar JSON
|
||||
|
||||
type Query {
|
||||
orders(filter: FilterInput!): OrderResult
|
||||
orders(pagination: PaginationInput!, filter: DateFilterInput!): OrdersResult
|
||||
order(id: ID!): Order
|
||||
"""
|
||||
Will be replaced with a ProductsResult type similar to OrdersResult but we need to change in the similar-images code then
|
||||
"""
|
||||
products(limit: Int, visible: Boolean, browsable: Boolean): [Product]
|
||||
products(
|
||||
pagination: PaginationInput!
|
||||
filter: ProductsFilterInput!
|
||||
): ProductsResult
|
||||
product(id: Int!): Product
|
||||
categories: [Category]
|
||||
category(id: Int!): Category
|
||||
@@ -26,12 +26,42 @@ const typeDefs = gql`
|
||||
interiors(filter: InteriorsFilterInput): [Interior]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
addKeyword(productId: Int!, keywordId: Int!): MutationResult
|
||||
input PaginationInput {
|
||||
limit: Int!
|
||||
"""
|
||||
offset is not implemented yet and result will always return as if it was 0. Implement when needed.
|
||||
"""
|
||||
offset: Int
|
||||
}
|
||||
|
||||
type MutationResult {
|
||||
result: String!
|
||||
input DateInput {
|
||||
from: Date!
|
||||
to: Date!
|
||||
}
|
||||
|
||||
input DateFilterInput {
|
||||
dates: DateInput
|
||||
}
|
||||
|
||||
input ProductsFilterInput {
|
||||
visible: Boolean
|
||||
browsable: Boolean
|
||||
}
|
||||
|
||||
type PaginationResult {
|
||||
total: Int!
|
||||
offset: Int!
|
||||
limit: Int!
|
||||
}
|
||||
|
||||
type OrdersResult {
|
||||
pagination: PaginationResult!
|
||||
items: [Order]
|
||||
}
|
||||
|
||||
type ProductsResult {
|
||||
pagination: PaginationResult!
|
||||
items: [Product]
|
||||
}
|
||||
|
||||
"""
|
||||
@@ -43,26 +73,6 @@ const typeDefs = gql`
|
||||
orientation: Orientation!
|
||||
}
|
||||
|
||||
input DateInput {
|
||||
from: Date
|
||||
to: Date
|
||||
}
|
||||
|
||||
input FilterInput {
|
||||
dates: DateInput
|
||||
limit: Int!
|
||||
offset: Int!
|
||||
}
|
||||
"""
|
||||
Later replace with implements ListResult when products support it
|
||||
"""
|
||||
type OrderResult {
|
||||
total: Int!
|
||||
offset: Int
|
||||
limit: Int
|
||||
items: [Order]
|
||||
}
|
||||
|
||||
type Market {
|
||||
id: ID!
|
||||
name: String!
|
||||
@@ -342,7 +352,7 @@ const typeDefs = gql`
|
||||
id: ID!
|
||||
name: String
|
||||
path: String
|
||||
orderRows(filter: FilterInput): [OrderRow]
|
||||
orderRows(pagination: PaginationInput!, filter: DateFilterInput): [OrderRow]
|
||||
}
|
||||
|
||||
enum KeywordType {
|
||||
@@ -356,6 +366,15 @@ const typeDefs = gql`
|
||||
type: KeywordType
|
||||
products: [Product]
|
||||
}
|
||||
|
||||
# Mutations below
|
||||
type Mutation {
|
||||
addKeyword(productId: Int!, keywordId: Int!): MutationResult
|
||||
}
|
||||
|
||||
type MutationResult {
|
||||
result: String!
|
||||
}
|
||||
`;
|
||||
|
||||
export { typeDefs };
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { PaginationResult } from './types';
|
||||
|
||||
export interface OrdersResult {
|
||||
pagination: PaginationResult;
|
||||
items: Array<Order> | Promise<Array<Order>>;
|
||||
}
|
||||
|
||||
export interface Address {
|
||||
id: number;
|
||||
firstname: string;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
import { PaginationInput, PaginationResult } from './types';
|
||||
|
||||
export interface ProductsFilterInput {
|
||||
pagination: PaginationInput;
|
||||
filter?: ProductsFilter;
|
||||
}
|
||||
export interface ProductsFilter {
|
||||
visible?: boolean;
|
||||
browsable?: boolean;
|
||||
}
|
||||
|
||||
export interface ProductListResult {
|
||||
pagination: PaginationResult;
|
||||
items: Array<Product> | Promise<Array<Product>>;
|
||||
}
|
||||
|
||||
export enum ProductGroup {
|
||||
PHOTO_WALLPAPER = 1,
|
||||
CANVAS = 2,
|
||||
|
||||
+15
-5
@@ -1,16 +1,26 @@
|
||||
export type Maybe<T> = T | undefined;
|
||||
|
||||
export interface PaginationInput {
|
||||
limit: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface DateInput {
|
||||
from: Date;
|
||||
to: Date;
|
||||
}
|
||||
|
||||
export interface GeneralInput {
|
||||
export interface DateFilterInput {
|
||||
dates: DateInput;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export interface FilterInput {
|
||||
filter: GeneralInput;
|
||||
export interface GeneralInput {
|
||||
pagination: PaginationInput;
|
||||
filter?: DateFilterInput;
|
||||
}
|
||||
|
||||
export interface PaginationResult {
|
||||
total: Promise<number> | number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user