Major refactor
This commit is contained in:
@@ -151,3 +151,43 @@
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
# JULY 2021
|
||||
{
|
||||
product(id: 73803) {
|
||||
id
|
||||
fields_json
|
||||
inserted
|
||||
path
|
||||
updated
|
||||
visible
|
||||
browsable
|
||||
api1json
|
||||
categories {
|
||||
id
|
||||
name
|
||||
path
|
||||
isLeaf
|
||||
depth
|
||||
childCount
|
||||
lft
|
||||
rgt
|
||||
}
|
||||
designer {
|
||||
id
|
||||
name
|
||||
path
|
||||
}
|
||||
printProducts {
|
||||
group
|
||||
id
|
||||
updated
|
||||
}
|
||||
blacklisting {
|
||||
group
|
||||
id
|
||||
marketId
|
||||
updated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,12 @@ export const createAllTestContainers =
|
||||
)
|
||||
.start();
|
||||
|
||||
const stream = await apiContainer.logs();
|
||||
stream
|
||||
.on('data', (line) => console.log(line))
|
||||
.on('err', (line) => console.error(line))
|
||||
.on('end', () => console.log('Stream closed'));
|
||||
|
||||
Object.assign(allContainers, {
|
||||
version: Math.round(Math.random() * 999999),
|
||||
dbContainer,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { promisify } from 'util';
|
||||
import * as Axios from 'axios';
|
||||
import * as jsonwebtoken from 'jsonwebtoken';
|
||||
import { Maybe } from '../datasources/types';
|
||||
import { Maybe } from '../types/types';
|
||||
const jwkToPem = require('jwk-to-pem');
|
||||
|
||||
export interface ClaimVerifyRequest {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { RESTDataSource } from 'apollo-datasource-rest';
|
||||
|
||||
export class Api1DataSource extends RESTDataSource {
|
||||
authHeader: string;
|
||||
constructor() {
|
||||
super();
|
||||
this.baseURL = 'http://docker.for.mac.localhost:8082';
|
||||
this.authHeader = 'Basic ZGV2OmRldg==';
|
||||
}
|
||||
|
||||
willSendRequest(request) {
|
||||
request.headers.set('Authorization', this.authHeader);
|
||||
}
|
||||
|
||||
async getProduct(id) {
|
||||
return await this.get(`/products/${id}?` + new URLSearchParams({}), null, {
|
||||
cacheOptions: { ttl: 5 },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { SQLDataSource } from 'datasource-sql';
|
||||
import { Category } from '../types/category-types';
|
||||
|
||||
const MINUTE = 60;
|
||||
|
||||
export class CategoryAPI extends SQLDataSource {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
async getProductCategories(productId: number): Promise<Array<Category>> {
|
||||
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,
|
||||
path: row.path.replace(/^root/, ''),
|
||||
};
|
||||
}),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
async getCategories(): Promise<Array<Category>> {
|
||||
return await this.knex
|
||||
.select('*')
|
||||
.from('v_categorytree')
|
||||
.cache(MINUTE)
|
||||
.then((rows) => {
|
||||
return rows.map((row) => {
|
||||
return {
|
||||
...row,
|
||||
path: row.path.replace(/^root/, ''),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getCategoryById(id: number): Promise<Category> {
|
||||
return await this.knex
|
||||
.select('*')
|
||||
.from('v_categorytree')
|
||||
.where('id', id)
|
||||
.first()
|
||||
.cache(MINUTE)
|
||||
.then((row) => {
|
||||
return {
|
||||
...row,
|
||||
path: row.path.replace(/^root/, ''),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseSQLDataSource } from './BaseSQLDataSource';
|
||||
import { Designer } from './designer-types';
|
||||
import { Maybe } from './types';
|
||||
import { Designer } from '../types/designer-types';
|
||||
import { Maybe } from '../types/types';
|
||||
|
||||
const MINUTE = 60;
|
||||
export class DesignerAPI extends BaseSQLDataSource {
|
||||
|
||||
@@ -6,10 +6,10 @@ import {
|
||||
Market,
|
||||
Order,
|
||||
OrderRow,
|
||||
} from './order-types';
|
||||
import { GeneralInput, Maybe } from './types';
|
||||
} from '../types/order-types';
|
||||
import { GeneralInput, Maybe } from '../types/types';
|
||||
import { convertToPossibleType } from './utils';
|
||||
import { getProductGroupStringFromString } from './product-types';
|
||||
import { getProductGroupStringFromString } from '../types/product-types';
|
||||
|
||||
const MINUTE = 60;
|
||||
export class OrderAPI extends BaseSQLDataSource {
|
||||
@@ -80,9 +80,8 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
return row;
|
||||
}
|
||||
|
||||
async getOrders(input: Maybe<GeneralInput>): Promise<Array<Order>> {
|
||||
let query = this.knex.select('*').from('orders');
|
||||
// .where('inserted', '>=', '2021-03-01T00:00:00Z')
|
||||
async getOrdersTotal(input: Maybe<GeneralInput>): Promise<Number> {
|
||||
let query = this.knex.table('orders');
|
||||
|
||||
if (input?.dates?.from) {
|
||||
query = query.where('inserted', '>=', input.dates.from);
|
||||
@@ -91,17 +90,37 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
if (input?.dates?.to) {
|
||||
query = query.where('inserted', '<=', input.dates.to);
|
||||
}
|
||||
query = query.orderBy('inserted', 'ASC');
|
||||
|
||||
const res = await query.clone().count();
|
||||
console.log(res);
|
||||
return 57777;
|
||||
}
|
||||
|
||||
async getOrders(input: Maybe<GeneralInput>): Promise<Array<Order>> {
|
||||
let query = this.getOrdersQuery(input);
|
||||
|
||||
if (input?.limit) {
|
||||
query = query.limit(input?.limit);
|
||||
}
|
||||
|
||||
query = query.orderBy('inserted', 'ASC');
|
||||
return await query
|
||||
.cache(MINUTE)
|
||||
.then((rows) => rows.map((row) => this.createOrderFromRow(row)));
|
||||
}
|
||||
|
||||
private getOrdersQuery(input: GeneralInput) {
|
||||
let query = this.knex.table('orders');
|
||||
if (input?.dates?.from) {
|
||||
query = query.where('inserted', '>=', input.dates.from);
|
||||
}
|
||||
|
||||
if (input?.dates?.to) {
|
||||
query = query.where('inserted', '<=', input.dates.to);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
async getOrderById(id: number): Promise<Order> {
|
||||
return await this.knex
|
||||
.select('*')
|
||||
@@ -115,7 +134,7 @@ export class OrderAPI extends BaseSQLDataSource {
|
||||
async getOrderRowFieldMapping(): Promise<any> {
|
||||
// const fields = await this.knex.select('*').from('order-row_fields');
|
||||
const fieldsRes = await this.cacheQuery(
|
||||
60,
|
||||
MINUTE * 10,
|
||||
this.knex
|
||||
.raw('SELECT * from "order-row_fields"')
|
||||
.then((data) => data.rows),
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
ProductGroup,
|
||||
PrintProduct,
|
||||
ProductBlacklist,
|
||||
Category,
|
||||
} from './product-types';
|
||||
import { Maybe } from './types';
|
||||
} from '../types/product-types';
|
||||
import { Maybe } from '../types/types';
|
||||
|
||||
const MINUTE = 60;
|
||||
|
||||
@@ -16,35 +15,10 @@ export class ProductAPI extends SQLDataSource {
|
||||
super(config);
|
||||
}
|
||||
|
||||
async getCategories(productId: number): Promise<Array<Category>> {
|
||||
const query = /* sql */ `
|
||||
SELECT product_category.category_id, categories.name
|
||||
FROM product_category JOIN 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((o) => {
|
||||
return {
|
||||
...o,
|
||||
id: o.category_id,
|
||||
};
|
||||
}),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
async getMaterial(printId: number): Promise<any> {
|
||||
// SELECT materialid, material, (price / 100::float) as price FROM "product-materials"
|
||||
}
|
||||
|
||||
// TODO: get product details
|
||||
async getProductDetails(id: number): Promise<any> {
|
||||
// SELECT * FROM "product-fields";
|
||||
// SELECT * FROM "product-products_fields" WHERE productid = 58941;
|
||||
return null;
|
||||
}
|
||||
|
||||
getBlacklisting(row: any): Array<ProductBlacklist> {
|
||||
if (!row.blacklisting) {
|
||||
return [];
|
||||
@@ -70,6 +44,7 @@ export class ProductAPI extends SQLDataSource {
|
||||
row.blacklisting = this.getBlacklisting(row);
|
||||
row.printProducts = this.getPrintProducts(row);
|
||||
row.designerId = row.designerid;
|
||||
row.fields_json = row.fields;
|
||||
return row;
|
||||
}
|
||||
|
||||
@@ -89,9 +64,20 @@ export class ProductAPI extends SQLDataSource {
|
||||
async getProduct(id: number): Promise<Maybe<Product>> {
|
||||
const query = sql.product(id);
|
||||
|
||||
const res = await this.knex
|
||||
.raw(query)
|
||||
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
||||
const res = await this.knex.raw(query).then((data) =>
|
||||
data.rows.map((row) => {
|
||||
const prod = this.createProductFromRow(row);
|
||||
return prod;
|
||||
}),
|
||||
);
|
||||
return res.find(Boolean);
|
||||
}
|
||||
|
||||
async getCategoryProducts(categoryId: number): Promise<Array<Product>> {
|
||||
const query = sql.categoryProducts(categoryId);
|
||||
|
||||
return await this.knex
|
||||
.raw(query)
|
||||
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Maybe } from '../types';
|
||||
import { Maybe } from '../../types/types';
|
||||
|
||||
// Get syntax highlighting with vscode by installing "Comment tagged templates2
|
||||
|
||||
@@ -43,7 +43,12 @@ SELECT products.productid as id,
|
||||
product_blacklist.updated_at
|
||||
)
|
||||
) FROM product_blacklist WHERE product_blacklist.product_id = products.productid
|
||||
) AS blacklisting
|
||||
) AS blacklisting,
|
||||
( SELECT json_object_agg(fields.field, pf.value)
|
||||
FROM "product-products_fields" pf
|
||||
JOIN "product-fields" fields ON fields.fieldid = pf.fieldid
|
||||
WHERE pf.productid = products.productid
|
||||
) AS fields
|
||||
FROM "product-products" products
|
||||
`;
|
||||
|
||||
@@ -75,3 +80,12 @@ export function product(id: number) {
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
export function categoryProducts(categoryId: number) {
|
||||
return (
|
||||
baseQuery +
|
||||
/* sql */ `
|
||||
WHERE products.productid IN (SELECT product_id FROM product_category WHERE category_id = ${categoryId});
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
+17
-7
@@ -10,21 +10,31 @@ import { OrderAPI } from './datasources/order-api';
|
||||
import { ProductAPI } from './datasources/product-api';
|
||||
import { DesignerAPI } from './datasources/designer-api';
|
||||
import { Api2DataSource } from './datasources/api2-datasource';
|
||||
import { Api1DataSource } from './datasources/api1-datasource';
|
||||
|
||||
import { verifyToken } from './cognito/cognito-client';
|
||||
import { CategoryAPI } from './datasources/category-api';
|
||||
|
||||
console.log(`dbConfig`, dbConfig);
|
||||
|
||||
// Should we convert columns?
|
||||
const knexConfig = knexStringcase(dbConfig);
|
||||
|
||||
// TODO: Try with this to see if - tables don't get converted
|
||||
// delete knexConfig.appWrapIdentifier;
|
||||
|
||||
// set up any dataSources our resolvers need
|
||||
const api1 = new Api1DataSource();
|
||||
const api2 = new Api2DataSource();
|
||||
const orderApi = new OrderAPI(knexConfig);
|
||||
const productApi = new ProductAPI(knexConfig);
|
||||
const categoryApi = new CategoryAPI(knexConfig);
|
||||
const designerApi = new DesignerAPI(knexConfig);
|
||||
|
||||
const dataSources = () => ({
|
||||
orderApi: new OrderAPI(knexConfig),
|
||||
productApi: new ProductAPI(knexConfig),
|
||||
designerApi: new DesignerAPI(knexConfig),
|
||||
api2: new Api2DataSource(),
|
||||
api1,
|
||||
api2,
|
||||
categoryApi,
|
||||
designerApi,
|
||||
orderApi,
|
||||
productApi,
|
||||
});
|
||||
|
||||
const context = async ({ req }) => {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { CategoryAPI } from '../datasources/category-api';
|
||||
import { ProductAPI } from '../datasources/product-api';
|
||||
|
||||
const Category: IResolverObject = {
|
||||
async products({ id }, _args, { dataSources }) {
|
||||
return (<ProductAPI>dataSources.productApi).getCategoryProducts(id);
|
||||
},
|
||||
};
|
||||
|
||||
async function getCategories(_, _args, { dataSources }) {
|
||||
return (<CategoryAPI>dataSources.categoryApi).getCategories();
|
||||
}
|
||||
|
||||
async function getCategory(_, { id }, { dataSources }) {
|
||||
return (<CategoryAPI>dataSources.categoryApi).getCategoryById(id);
|
||||
}
|
||||
|
||||
export const categoryTypeDefs = { Category };
|
||||
|
||||
export const categoryQueryTypeDefs = {
|
||||
categories: getCategories,
|
||||
category: getCategory,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { DesignerAPI } from '../datasources/designer-api';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { FilterInput } from '../datasources/types';
|
||||
import { FilterInput } from '../types/types';
|
||||
|
||||
const Designer: IResolverObject = {
|
||||
async orderRows({ id }, args: FilterInput, { dataSources }) {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { orderQueryTypeDefs, orderTypeDefs } from './orders-resolver';
|
||||
import { productQueryTypeDefs, productTypeDefs } from './products-resolver';
|
||||
import { designerQueryTypeDefs, designerTypeDefs } from './designers-resolver';
|
||||
import { categoryQueryTypeDefs, categoryTypeDefs } from './categories-resolver';
|
||||
import { DateTimeResolver, DateResolver, JSONResolver } from 'graphql-scalars';
|
||||
const resolvers = {
|
||||
Query: {
|
||||
...orderQueryTypeDefs,
|
||||
...productQueryTypeDefs,
|
||||
...designerQueryTypeDefs,
|
||||
...categoryQueryTypeDefs,
|
||||
},
|
||||
...orderTypeDefs,
|
||||
...productTypeDefs,
|
||||
...designerTypeDefs,
|
||||
...categoryTypeDefs,
|
||||
DateTime: DateTimeResolver,
|
||||
Date: DateResolver,
|
||||
JSON: JSONResolver,
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { FilterInput } from '../datasources/types';
|
||||
import { FilterInput } from '../types/types';
|
||||
|
||||
const ContactInformation: IResolverObject = {
|
||||
// (parent, args, ctx, info)
|
||||
async address({ addressId }, args, { dataSources }) {
|
||||
async address({ addressId }, _args, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getAddress(addressId);
|
||||
},
|
||||
};
|
||||
|
||||
const Order: IResolverObject = {
|
||||
async rows({ id }, args, { dataSources }) {
|
||||
async rows({ id }, _args, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderRowsByOrderId(id);
|
||||
},
|
||||
async market({ market }, args, { dataSources }) {
|
||||
async market({ market }, _args, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getMarketByName(market);
|
||||
},
|
||||
};
|
||||
|
||||
const OrderRow: IResolverObject = {
|
||||
async order(parent, args, { dataSources }) {
|
||||
async order(parent, _args, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getOrderById(parent.orderId);
|
||||
},
|
||||
};
|
||||
|
||||
async function getOrders(_, args: FilterInput, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getOrders(args.filter);
|
||||
async function getOrdersResult(_, args: FilterInput, { dataSources }) {
|
||||
const total = (<OrderAPI>dataSources.orderApi).getOrdersTotal(args.filter);
|
||||
const items = (<OrderAPI>dataSources.orderApi).getOrders(args.filter);
|
||||
return {
|
||||
total: total,
|
||||
items: items,
|
||||
};
|
||||
}
|
||||
|
||||
async function getOrder(_, { id }, { dataSources }) {
|
||||
@@ -39,6 +44,6 @@ export const orderTypeDefs = {
|
||||
};
|
||||
|
||||
export const orderQueryTypeDefs = {
|
||||
orders: getOrders,
|
||||
orders: getOrdersResult,
|
||||
order: getOrder,
|
||||
};
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
// (parent, args, ctx, info)
|
||||
import { IResolverObject } from 'graphql-tools';
|
||||
import { Api2DataSource } from '../datasources/api2-datasource';
|
||||
import { OrderAPI } from '../datasources/order-api';
|
||||
import { ProductAPI } from '../datasources/product-api';
|
||||
import { Product } from '../types/product-types';
|
||||
import { CategoryAPI } from '../datasources/category-api';
|
||||
import { Category } from '../types/category-types';
|
||||
|
||||
const ProductBlacklist: IResolverObject = {
|
||||
async market(parent, args, { dataSources }) {
|
||||
async market(parent, _args, { dataSources }) {
|
||||
return (<OrderAPI>dataSources.orderApi).getMarketById(parent.marketId);
|
||||
},
|
||||
};
|
||||
|
||||
const Product: IResolverObject = {
|
||||
async categories({ id }, args, { dataSources }) {
|
||||
return (<ProductAPI>dataSources.productApi).getCategories(id);
|
||||
// (parent, args, ctx, info)
|
||||
async categories({ id }, _args, { dataSources }): Promise<Array<Category>> {
|
||||
return (<CategoryAPI>dataSources.categoryApi).getProductCategories(id);
|
||||
},
|
||||
async designer({ designerId }, args, { dataSources }) {
|
||||
return dataSources.designerApi.getDesignerById(designerId);
|
||||
},
|
||||
async api1json({ id }, _args, { dataSources }): Promise<JSON> {
|
||||
return dataSources.api1.getProduct(id);
|
||||
},
|
||||
};
|
||||
|
||||
const PrintProduct: IResolverObject = {
|
||||
@@ -30,7 +36,11 @@ const PrintProduct: IResolverObject = {
|
||||
},
|
||||
};
|
||||
|
||||
async function getProducts(_, { limit, visible, browsable }, { dataSources }) {
|
||||
async function getProducts(
|
||||
_,
|
||||
{ limit, visible, browsable },
|
||||
{ dataSources },
|
||||
): Promise<Array<Product>> {
|
||||
return (<ProductAPI>dataSources.productApi).getProducts(
|
||||
limit,
|
||||
visible,
|
||||
@@ -38,7 +48,7 @@ async function getProducts(_, { limit, visible, browsable }, { dataSources }) {
|
||||
);
|
||||
}
|
||||
|
||||
async function getProduct(_, { id }, { dataSources }) {
|
||||
async function getProduct(_, { id }, { dataSources }): Promise<Product> {
|
||||
return (<ProductAPI>dataSources.productApi).getProduct(id);
|
||||
}
|
||||
|
||||
|
||||
+22
-1
@@ -6,10 +6,12 @@ const typeDefs = gql`
|
||||
scalar JSON
|
||||
|
||||
type Query {
|
||||
orders(filter: FilterInput): [Order]
|
||||
orders(filter: FilterInput!): OrderResult
|
||||
order(id: ID!): Order
|
||||
products(limit: Int, visible: Boolean, browsable: Boolean): [Product]
|
||||
product(id: Int!): Product
|
||||
categories: [Category]
|
||||
category(id: Int!): Category
|
||||
designers(limit: Int): [Designer]
|
||||
designer(id: Int!): Designer
|
||||
}
|
||||
@@ -21,7 +23,17 @@ const typeDefs = gql`
|
||||
|
||||
input FilterInput {
|
||||
dates: DateInput
|
||||
limit: Int!
|
||||
offset: Int!
|
||||
}
|
||||
"""
|
||||
Later replace with implements ListResult
|
||||
"""
|
||||
type OrderResult {
|
||||
total: Int!
|
||||
offset: Int
|
||||
limit: Int
|
||||
items: [Order]
|
||||
}
|
||||
|
||||
type Market {
|
||||
@@ -157,6 +169,13 @@ const typeDefs = gql`
|
||||
type Category {
|
||||
id: ID!
|
||||
name: String
|
||||
path: String
|
||||
isLeaf: Int
|
||||
depth: Int
|
||||
childCount: Int
|
||||
lft: Int
|
||||
rgt: Int
|
||||
products: [Product]
|
||||
}
|
||||
|
||||
type Product {
|
||||
@@ -171,6 +190,8 @@ const typeDefs = gql`
|
||||
categories: [Category]
|
||||
designerId: Int
|
||||
designer: Designer
|
||||
api1json: JSON
|
||||
fields_json: JSON
|
||||
}
|
||||
|
||||
enum ProductGroup {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface Category {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
isLeaf: number;
|
||||
depth: number;
|
||||
childCount: number;
|
||||
lft: number;
|
||||
rgt: number;
|
||||
}
|
||||
@@ -34,11 +34,6 @@ export function getProductGroupFromString(group: string): ProductGroup {
|
||||
return ProductGroup.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Category {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
export interface ProductBlacklist {
|
||||
id: number;
|
||||
groupId: number;
|
||||
@@ -65,4 +60,6 @@ export interface Product {
|
||||
updated?: Date;
|
||||
printProducts: [PrintProduct];
|
||||
blacklisting: [ProductBlacklist];
|
||||
api1json: JSON;
|
||||
fields_json: JSON;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export interface DateInput {
|
||||
export interface GeneralInput {
|
||||
dates: DateInput;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export interface FilterInput {
|
||||
Reference in New Issue
Block a user