From f60a51a94b603733b9827545e3238d7c49e3cdd4 Mon Sep 17 00:00:00 2001 From: Niklas Fondberg Date: Thu, 8 Apr 2021 15:09:31 +0200 Subject: [PATCH] Add product --- queries.graphql | 10 +++ src/datasources/order-api.ts | 28 ++++++- src/datasources/order-types.ts | 10 ++- src/datasources/product-api.ts | 18 ++-- src/datasources/sql/products-sql.ts | 103 +++++++++++++---------- src/index.ts | 1 - src/resolvers/datetime-type.ts | 1 + src/resolvers/index.ts | 10 +-- src/resolvers/orders-resolver.ts | 23 +++-- src/resolvers/products-resolver.ts | 26 +++++- src/schema.ts | 125 +++++----------------------- 11 files changed, 184 insertions(+), 171 deletions(-) diff --git a/queries.graphql b/queries.graphql index 0360b9b..9c51176 100644 --- a/queries.graphql +++ b/queries.graphql @@ -65,8 +65,18 @@ { order(id: 588274) { id + marketObject { + id + name + vat + currency + priceAdjustment + } rows { id + order { + id + } insertedDate orderId productId diff --git a/src/datasources/order-api.ts b/src/datasources/order-api.ts index 71935ad..09e39bf 100644 --- a/src/datasources/order-api.ts +++ b/src/datasources/order-api.ts @@ -1,6 +1,12 @@ import { SQLDataSource } from 'datasource-sql'; import { camelcase } from 'stringcase'; -import { Address, ContactInformation, Order, OrderRow } from './order-types'; +import { + Address, + ContactInformation, + Market, + Order, + OrderRow, +} from './order-types'; import { Maybe } from './types'; const MINUTE = 60; @@ -9,6 +15,24 @@ export class OrderAPI extends SQLDataSource { super(config); } + async getMarketById(id: number): Promise { + return await this.knex + .select('*') + .from('markets') + .where('id', id) + .first() + .cache(MINUTE); + } + + async getMarketByName(name: string): Promise { + return await this.knex + .select('*') + .from('markets') + .where('name', name) + .first() + .cache(MINUTE); + } + async getAddress(addressId: number): Promise> { const row = await this.knex .select('*') @@ -103,7 +127,6 @@ export class OrderAPI extends SQLDataSource { } const orderRow = details.reduce((obj, item) => { obj[camelcase(fields[item.fieldid])] = item.value; - obj['insertedDate'] = item.inserted; return obj; }, {}); @@ -120,6 +143,7 @@ export class OrderAPI extends SQLDataSource { } /** + * row { printId: '73963', inserted: 2021-02-04T10:11:41.093Z, diff --git a/src/datasources/order-types.ts b/src/datasources/order-types.ts index 5a94ec5..23d6fce 100644 --- a/src/datasources/order-types.ts +++ b/src/datasources/order-types.ts @@ -1,3 +1,11 @@ +export interface Market { + id: number; + name: string; + vat: number; + currency: string; + priceAdjustment: number; +} + export interface Address { id: number; firstname: string; @@ -71,7 +79,7 @@ export interface Order { export interface OrderRow { id: number; - insertedDate: Date; + inserted: Date; orderId: number; productId: number; productGroup: string; diff --git a/src/datasources/product-api.ts b/src/datasources/product-api.ts index 0dbd41b..988c23a 100644 --- a/src/datasources/product-api.ts +++ b/src/datasources/product-api.ts @@ -39,21 +39,29 @@ export class ProductAPI extends SQLDataSource { limit: Maybe, visible: Maybe, browsable: Maybe, - ): Promise { - // console.log(limit, browsable, visible); + ): Promise> { limit = limit ?? 100; const query = sql.products(limit, visible, browsable); - const data = await this.knex.raw(query).then((data) => + return await this.knex.raw(query).then((data) => data.rows.map((row) => { row.blacklisting = this.getBlacklisting(row); row.printProducts = this.getPrintProducts(row); return row; }), ); + } - // console.log(JSON.stringify(data, null, 2)); + async getProduct(id: number): Promise> { + const query = sql.product(id); - return data; + const res = await this.knex.raw(query).then((data) => + data.rows.map((row) => { + row.blacklisting = this.getBlacklisting(row); + row.printProducts = this.getPrintProducts(row); + return row; + }), + ); + return res.find(Boolean); } } diff --git a/src/datasources/sql/products-sql.ts b/src/datasources/sql/products-sql.ts index f3cffc0..3fd2be6 100644 --- a/src/datasources/sql/products-sql.ts +++ b/src/datasources/sql/products-sql.ts @@ -2,54 +2,59 @@ import { Maybe } from '../types'; // Get syntax highlighting with vscode by installing "Comment tagged templates2 +const baseQuery = /* sql */ ` + +SELECT products.productid as id, + products.path, + products.visible, + products.browsable, + products.designerid, + products.inserted, + products.updated, + ( + SELECT json_agg( + json_build_object( + 'printId', + "product-printproducts".printid, + 'productId', + "product-printproducts".productid, + 'groupId', + "product-printproducts".groupid, + 'inserted', + "product-printproducts".inserted, + 'updated', + "product-printproducts".updated + ) + ) FROM "product-printproducts" WHERE "product-printproducts".productid = products.productid + ) AS printProducts, + ( SELECT json_agg( + json_build_object( + 'id', + product_blacklist.id, + 'productId', + product_blacklist.product_id, + 'marketId', + product_blacklist.market_id, + 'groupId', + product_blacklist.group_id, + 'inserted', + product_blacklist.created_at, + 'updated', + product_blacklist.updated_at + ) + ) FROM product_blacklist WHERE product_blacklist.product_id = products.productid + ) AS blacklisting + FROM "product-products" products + `; + export function products( limit: number = 100, visible: Maybe, browsable: Maybe, ) { - const query = /* sql */ ` - - SELECT products.productid as id, - products.path, - products.visible, - products.browsable, - products.designerid, - products.inserted, - products.updated, - ( - SELECT json_agg( - json_build_object( - 'printId', - "product-printproducts".printid, - 'productId', - "product-printproducts".productid, - 'groupId', - "product-printproducts".groupid, - 'inserted', - "product-printproducts".inserted, - 'updated', - "product-printproducts".updated - ) - ) FROM "product-printproducts" WHERE "product-printproducts".productid = products.productid - ) AS printProducts, - ( SELECT json_agg( - json_build_object( - 'id', - product_blacklist.id, - 'productId', - product_blacklist.product_id, - 'marketId', - product_blacklist.market_id, - 'groupId', - product_blacklist.group_id, - 'inserted', - product_blacklist.created_at, - 'updated', - product_blacklist.updated_at - ) - ) FROM product_blacklist WHERE product_blacklist.product_id = products.productid - ) AS blacklisting - FROM "product-products" products + return ( + baseQuery + + /* sql */ ` ${ visible != null ? /* sql */ ` @@ -58,7 +63,15 @@ export function products( : `` } LIMIT ${limit} -`; +` + ); +} - return query; +export function product(id: number) { + return ( + baseQuery + + /* sql */ ` + WHERE products.productid = ${id} +` + ); } diff --git a/src/index.ts b/src/index.ts index b8d5870..26ea246 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,6 @@ import knexStringcase from 'knex-stringcase'; import { dbConfig } from './config'; import { typeDefs } from './schema'; import resolvers from './resolvers'; - import { OrderAPI } from './datasources/order-api'; import { ProductAPI } from './datasources/product-api'; diff --git a/src/resolvers/datetime-type.ts b/src/resolvers/datetime-type.ts index 2cebe54..fb87b29 100644 --- a/src/resolvers/datetime-type.ts +++ b/src/resolvers/datetime-type.ts @@ -1,6 +1,7 @@ import { GraphQLScalarType } from 'graphql'; import { Kind } from 'graphql/language'; +// possible switch to https://github.com/Urigo/graphql-scalars export const DateTimeScalar = new GraphQLScalarType({ name: 'DateTime', description: 'Date custom scalar type', diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts index 91143bd..2215810 100644 --- a/src/resolvers/index.ts +++ b/src/resolvers/index.ts @@ -1,12 +1,12 @@ -import orders, { ContactInformation, Order } from './orders-resolver'; -import products from './products-resolver'; +import { orderQueryTypeDefs, orderTypeDefs } from './orders-resolver'; +import { productQueryTypeDefs, productTypeDefs } from './products-resolver'; import { DateTimeScalar } from './datetime-type'; const resolvers = { - Query: { ...orders, ...products }, + Query: { ...orderQueryTypeDefs, ...productQueryTypeDefs }, DateTime: DateTimeScalar, - ContactInformation, - Order, + ...orderTypeDefs, + ...productTypeDefs, }; export default resolvers; diff --git a/src/resolvers/orders-resolver.ts b/src/resolvers/orders-resolver.ts index 3e76c26..a9f8c91 100644 --- a/src/resolvers/orders-resolver.ts +++ b/src/resolvers/orders-resolver.ts @@ -1,16 +1,25 @@ import { IResolverObject } from 'graphql-tools'; -export const ContactInformation: IResolverObject = { +const ContactInformation: IResolverObject = { // (parent, args, ctx, info) async address({ addressId }, args, { dataSources }) { return dataSources.orderApi.getAddress(addressId); }, }; -export const Order: IResolverObject = { +const Order: IResolverObject = { async rows({ id }, args, { dataSources }) { return dataSources.orderApi.getOrderRows(id); }, + async marketObject({ market }, args, { dataSources }) { + return dataSources.orderApi.getMarketByName(market); + }, +}; + +const OrderRow: IResolverObject = { + async order(parent, args, { dataSources }) { + return dataSources.orderApi.getOrder(parent.orderId); + }, }; async function getOrders(_, __, { dataSources }) { @@ -21,9 +30,13 @@ async function getOrder(_, { id }, { dataSources }) { return dataSources.orderApi.getOrder(id); } -const orders = { +export const orderTypeDefs = { + ContactInformation, + Order, + OrderRow, +}; + +export const orderQueryTypeDefs = { orders: getOrders, order: getOrder, }; - -export default orders; diff --git a/src/resolvers/products-resolver.ts b/src/resolvers/products-resolver.ts index 7425c56..acd54e9 100644 --- a/src/resolvers/products-resolver.ts +++ b/src/resolvers/products-resolver.ts @@ -1,7 +1,25 @@ // (parent, args, ctx, info) -const products = { - products: (_, { limit, visible, browsable }, { dataSources }) => - dataSources.productApi.getProducts(limit, visible, browsable), +import { IResolverObject } from 'graphql-tools'; + +const ProductBlacklist: IResolverObject = { + async market(parent, args, { dataSources }) { + return dataSources.orderApi.getMarketById(parent.marketId); + }, }; -export default products; +async function getProducts(_, { limit, visible, browsable }, { dataSources }) { + return dataSources.productApi.getProducts(limit, visible, browsable); +} + +async function getProduct(_, { id }, { dataSources }) { + return dataSources.productApi.getProduct(id); +} + +export const productTypeDefs = { + ProductBlacklist, +}; + +export const productQueryTypeDefs = { + products: getProducts, + product: getProduct, +}; diff --git a/src/schema.ts b/src/schema.ts index 218f1ce..b59f69c 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -7,6 +7,15 @@ const typeDefs = gql` orders: [Order] order(id: ID!): Order products(limit: Int, visible: Boolean, browsable: Boolean): [Product] + product(id: Int!): Product + } + + type Market { + id: ID! + name: String + vat: Float + currency: String + priceAdjustment: Float } type Address { @@ -79,12 +88,13 @@ const typeDefs = gql` billingAddressId: Int deliveryAddressId: Int rows: [OrderRow] + marketObject: Market } type OrderRow { id: ID! order: Order - insertedDate: DateTime + inserted: DateTime orderId: Int productId: Int productGroup: String @@ -100,6 +110,11 @@ const typeDefs = gql` pwintySku: String } + type Category { + id: ID! + name: String + } + type Product { id: ID! path: String! @@ -126,6 +141,7 @@ const typeDefs = gql` groupId: Int! group: ProductGroup! marketId: Int! + market: Market! inserted: DateTime! updated: DateTime! } @@ -141,38 +157,13 @@ const typeDefs = gql` export { typeDefs }; /** -schema { - query: Query -} -type Address { - id: ID! - firstname: String - lastname: String - recipientName: String - companyname: String - address1: String - address2: String - countryCode: String - city: String - zipcode: String - stateCountyOrRegion: String -} type Category { id: ID! name: String } -type ContactInformation { - email: String - phone: String - address: Address -} - -scalar Date - -scalar DateTime type Designer { id: ID! @@ -180,98 +171,26 @@ type Designer { orderRows(fromDate: Date, toDate: Date): [OrderRow] } -enum Group { - PHOTO_WALLPAPER - CANVAS - WALLPAPER - DO_IT_YOURSELF_FRAME - DESIGNER_WALLPAPER - POSTER - FRAMED_PRINT -} - -scalar JSONString - -type Market { - id: ID! - name: String -} - -type Order { - id: ID! - insertedDate: DateTime! - paid: Boolean! - confirmed: Boolean! - delivered: Boolean! - canceled: Boolean! - countryCode: String! - locale: String! - market: String! - currency: String - contractCustomerId: Int - exchangeRateEUR: Float - language: String - pwintyId: Int - email: String - phone: String - rows: [OrderRow] - billingInformation: ContactInformation - deliveryInformation: ContactInformation -} - -type OrderRow { - id: ID! - order: Order - insertedDate: DateTime - orderId: Int - productId: Int - productGroup: String - artNo: String - path: String - name: String - price: Float - designerId: String - commissionAmount: String - status: String - pwintyImageId: Int - frameColor: String - pwintySku: String - json: JSONString -} - type PrintProduct { - id: ID! - group: Group! - insertedDate: DateTime! + insertedDate: DateTime! updatedDate: DateTime } type Product { - id: ID! - path: String! - visible: Boolean - browsable: Boolean - insertedDate: DateTime! - updatedDate: DateTime! categories: [Category] designer: Designer - printProducts: [PrintProduct] - blacklisting: [ProductBlacklist] + insertedDate: DateTime! + updatedDate: DateTime } type ProductBlacklist { - id: ID! - group: Group! - marketId: String! - insertedDate: DateTime! - updatedDate: DateTime! market: Market + insertedDate: DateTime! + updatedDate: DateTime } type Query { - products(limit: Int, visible: Boolean, browsable: Boolean): [Product] product(id: Int!): Product - order(id: Int!): Order designer(id: Int!): Designer designers: [Designer] }