3215 Fix 1n issue for products->designer and printProduct->interiors query (#140)

This commit is contained in:
Anders Gustafsson
2023-01-26 09:58:18 +01:00
committed by GitHub
parent 675f366025
commit 1720280840
6 changed files with 163 additions and 94 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
Apollo graphql api server for Photowall
This servers runs in two instances on AWS. `graphql.photowall.com` is used for photowall admin. `data.photowall.com` is used by the website (client).
This servers runs in two instances on AWS. `graphql.photowall.com` is used for photowall admin. `data.photowall.com` is used by the website (client).
When clients want to use the public `data.photowall.com` they use the `/login` REST endpoint to retreive a BearerToken with restricted cognito scopes. They can then access certain product and designer information with the token.
Third party systems like Unbox (Synergy) will use the `graphql.photowall.com` to read their data like they are doing now. This is to make sure the website wont be affected by larger downloads of data like unbox does. Unbox has their own cognito app client with limited scopes.
@@ -56,4 +56,4 @@ npm test
### Force update the staging
update 4
update 5
+105 -71
View File
@@ -20,6 +20,7 @@
"axios": "^1.2.0",
"body-parser": "^1.20.1",
"cors": "^2.8.5",
"dataloader": "^2.1.0",
"datasource-sql": "^2.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
@@ -206,43 +207,6 @@
"node": ">=14"
}
},
"node_modules/@apollo/server/node_modules/@graphql-tools/merge": {
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.11.tgz",
"integrity": "sha512-IpZh8r8e8FycXaUv04xe5HQH9siD1tkS8MvaO8Wb2FaPXv15XSYP+Wsb2MUStpIqGfQxa6xY/+eEuxv+VqwXyg==",
"dependencies": {
"@graphql-tools/utils": "9.1.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@apollo/server/node_modules/@graphql-tools/schema": {
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.9.tgz",
"integrity": "sha512-hwg8trUytO5ayQ8bzL3+sAyXcu2rhKt5pLXpLO0/TMTN2nXd3DBO4mqx+Ra4Er2mE/msInGQ5EmZbxVBPv+hSg==",
"dependencies": {
"@graphql-tools/merge": "8.3.11",
"@graphql-tools/utils": "9.1.0",
"tslib": "^2.4.0",
"value-or-promise": "1.0.11"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@apollo/server/node_modules/@graphql-tools/utils": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.1.0.tgz",
"integrity": "sha512-4Ketxo98IwKA/56LP6cI6PgQBwUCujszQcTNkzjq7liJPa2mLjKnmVOJ0bauMwKcEazeYuZagceljb0POmEGvQ==",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@apollo/server/node_modules/lru-cache": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
@@ -2464,6 +2428,54 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@graphql-tools/merge": {
"version": "8.3.15",
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.15.tgz",
"integrity": "sha512-hYYOlsqkUlL6oOo7zzuk6hIv7xQzy+x21sgK84d5FWaiWYkLYh9As8myuDd9SD5xovWWQ9m/iRhIOVDEMSyEKA==",
"dependencies": {
"@graphql-tools/utils": "9.1.4",
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": {
"version": "9.1.4",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.1.4.tgz",
"integrity": "sha512-hgIeLt95h9nQgQuzbbdhuZmh+8WV7RZ/6GbTj6t3IU4Zd2zs9yYJ2jgW/krO587GMOY8zCwrjNOMzD40u3l7Vg==",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@graphql-tools/schema": {
"version": "9.0.13",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.13.tgz",
"integrity": "sha512-guRA3fwAtv+M1Kh930P4ydH9aKJTWscIkhVFcWpj/cnjYYxj88jkEJ15ZNiJX/2breNY+sbVgmlgLKb6aXi/Jg==",
"dependencies": {
"@graphql-tools/merge": "8.3.15",
"@graphql-tools/utils": "9.1.4",
"tslib": "^2.4.0",
"value-or-promise": "1.0.12"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": {
"version": "9.1.4",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.1.4.tgz",
"integrity": "sha512-hgIeLt95h9nQgQuzbbdhuZmh+8WV7RZ/6GbTj6t3IU4Zd2zs9yYJ2jgW/krO587GMOY8zCwrjNOMzD40u3l7Vg==",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/@grpc/grpc-js": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz",
@@ -4368,6 +4380,11 @@
"node": ">= 8"
}
},
"node_modules/dataloader": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz",
"integrity": "sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ=="
},
"node_modules/datasource-sql": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/datasource-sql/-/datasource-sql-2.0.1.tgz",
@@ -8900,9 +8917,9 @@
}
},
"node_modules/value-or-promise": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz",
"integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz",
"integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==",
"engines": {
"node": ">=12"
}
@@ -9215,34 +9232,6 @@
"resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-2.0.0.tgz",
"integrity": "sha512-RC0twEwwBKbhk/y4B2X4YEciRG1xoKMgiPy5xQqNMd3pG78sR+ybctG/m7c/8+NaaQOS22UPUCBd6yS6WihBIg=="
},
"@graphql-tools/merge": {
"version": "8.3.11",
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.11.tgz",
"integrity": "sha512-IpZh8r8e8FycXaUv04xe5HQH9siD1tkS8MvaO8Wb2FaPXv15XSYP+Wsb2MUStpIqGfQxa6xY/+eEuxv+VqwXyg==",
"requires": {
"@graphql-tools/utils": "9.1.0",
"tslib": "^2.4.0"
}
},
"@graphql-tools/schema": {
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.9.tgz",
"integrity": "sha512-hwg8trUytO5ayQ8bzL3+sAyXcu2rhKt5pLXpLO0/TMTN2nXd3DBO4mqx+Ra4Er2mE/msInGQ5EmZbxVBPv+hSg==",
"requires": {
"@graphql-tools/merge": "8.3.11",
"@graphql-tools/utils": "9.1.0",
"tslib": "^2.4.0",
"value-or-promise": "1.0.11"
}
},
"@graphql-tools/utils": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.1.0.tgz",
"integrity": "sha512-4Ketxo98IwKA/56LP6cI6PgQBwUCujszQcTNkzjq7liJPa2mLjKnmVOJ0bauMwKcEazeYuZagceljb0POmEGvQ==",
"requires": {
"tslib": "^2.4.0"
}
},
"lru-cache": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
@@ -11063,6 +11052,46 @@
"strip-json-comments": "^3.1.1"
}
},
"@graphql-tools/merge": {
"version": "8.3.15",
"resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.15.tgz",
"integrity": "sha512-hYYOlsqkUlL6oOo7zzuk6hIv7xQzy+x21sgK84d5FWaiWYkLYh9As8myuDd9SD5xovWWQ9m/iRhIOVDEMSyEKA==",
"requires": {
"@graphql-tools/utils": "9.1.4",
"tslib": "^2.4.0"
},
"dependencies": {
"@graphql-tools/utils": {
"version": "9.1.4",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.1.4.tgz",
"integrity": "sha512-hgIeLt95h9nQgQuzbbdhuZmh+8WV7RZ/6GbTj6t3IU4Zd2zs9yYJ2jgW/krO587GMOY8zCwrjNOMzD40u3l7Vg==",
"requires": {
"tslib": "^2.4.0"
}
}
}
},
"@graphql-tools/schema": {
"version": "9.0.13",
"resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.13.tgz",
"integrity": "sha512-guRA3fwAtv+M1Kh930P4ydH9aKJTWscIkhVFcWpj/cnjYYxj88jkEJ15ZNiJX/2breNY+sbVgmlgLKb6aXi/Jg==",
"requires": {
"@graphql-tools/merge": "8.3.15",
"@graphql-tools/utils": "9.1.4",
"tslib": "^2.4.0",
"value-or-promise": "1.0.12"
},
"dependencies": {
"@graphql-tools/utils": {
"version": "9.1.4",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.1.4.tgz",
"integrity": "sha512-hgIeLt95h9nQgQuzbbdhuZmh+8WV7RZ/6GbTj6t3IU4Zd2zs9yYJ2jgW/krO587GMOY8zCwrjNOMzD40u3l7Vg==",
"requires": {
"tslib": "^2.4.0"
}
}
}
},
"@grpc/grpc-js": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz",
@@ -12560,6 +12589,11 @@
}
}
},
"dataloader": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz",
"integrity": "sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ=="
},
"datasource-sql": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/datasource-sql/-/datasource-sql-2.0.1.tgz",
@@ -15887,9 +15921,9 @@
}
},
"value-or-promise": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz",
"integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg=="
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz",
"integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q=="
},
"vary": {
"version": "1.1.2",
+1
View File
@@ -25,6 +25,7 @@
"axios": "^1.2.0",
"body-parser": "^1.20.1",
"cors": "^2.8.5",
"dataloader": "^2.1.0",
"datasource-sql": "^2.0.1",
"dotenv": "^16.0.3",
"express": "^4.18.2",
+23 -19
View File
@@ -3,10 +3,16 @@ import { Designer } from '../types/designer-types';
import { DataSourceOptions, Maybe } from '../types/types';
import { MINUTE } from './utils';
import { ScopeAccess, Scopes } from '../cognito/access-control';
import DataLoader from 'dataloader';
export class DesignerAPI extends BaseSQLDataSource {
private loader: DataLoader<number, Designer>;
constructor(options: DataSourceOptions, config) {
super(options, config);
this.loader = new DataLoader((keys: number[]) =>
this.getDesigners(undefined, keys),
);
}
async getDesignerById(id: number): Promise<Designer> {
@@ -14,22 +20,7 @@ export class DesignerAPI extends BaseSQLDataSource {
Scopes.DESIGNERS_READ,
Scopes.DESIGNERS_PUBLIC_READ,
]);
return (
this.knex
.select('*')
.from('designers')
.where('designerid', id)
.first()
// @ts-ignore
.cache(MINUTE)
.then((row) => {
return {
...row,
id: row.designerid,
excludeFromSearch: !row.visibleSearch || !row.visibleDesignerpage,
};
})
);
return this.loader.load(id);
}
async searchDesigners(name: string): Promise<Array<Designer>> {
@@ -49,15 +40,28 @@ export class DesignerAPI extends BaseSQLDataSource {
});
}
async getDesigners(limit: Maybe<number>): Promise<Array<Designer>> {
async getDesigners(
limit: number = 5000,
ids?: number[],
): Promise<Array<Designer>> {
ScopeAccess.validate(this.user).all([Scopes.DESIGNERS_READ]);
limit = limit ?? 5000;
return (
this.knex
.select('*')
.select(
'designerid',
'name',
'path',
'visible_search',
'visible_designerpage',
)
.from('designers')
.limit(limit)
.orderBy('name')
.modify((queryBuilder) => {
if (ids) {
queryBuilder.whereIn('designerid', ids);
}
})
// @ts-ignore
.cache(MINUTE)
.then((rows) => {
+26 -2
View File
@@ -12,8 +12,13 @@ import { moveS3File } from '../aws/s3';
import { BaseSQLDataSource } from './BaseSQLDataSource';
import { GraphQLError } from 'graphql';
import { ScopeAccess, Scopes } from '../cognito/access-control';
import DataLoader from 'dataloader';
interface InteriorWithPrintId extends Interior {
printId: number;
}
export class InteriorAPI extends BaseSQLDataSource {
private printProductInteriorsLoader: DataLoader<number, Interior[]>;
imageServerApi: ImageServerApi;
constructor(
@@ -23,6 +28,19 @@ export class InteriorAPI extends BaseSQLDataSource {
) {
super(options, config);
this.imageServerApi = imageServerApi;
this.printProductInteriorsLoader = new DataLoader(async (ids: number[]) => {
const results = await this.getPrintProductInteriorsBatch(ids);
const indexed = {} as Record<string, InteriorWithPrintId[]>;
results.forEach((interiorRow) => {
if (!indexed[interiorRow.printId]) {
indexed[interiorRow.printId] = [interiorRow];
} else {
indexed[interiorRow.printId].push(interiorRow);
}
});
const output = ids.map((id) => indexed[id] ?? []);
return output;
});
}
// -------------------------------
@@ -136,7 +154,13 @@ export class InteriorAPI extends BaseSQLDataSource {
return allInteriors;
}
async getPrintProductInteriors(printId: number): Promise<Array<Interior>> {
async getPrintProductInteriors(printId: number): Promise<Interior[]> {
return this.printProductInteriorsLoader.load(printId);
}
async getPrintProductInteriorsBatch(
printIds: number[],
): Promise<InteriorWithPrintId[]> {
ScopeAccess.validate(this.user).all([
Scopes.INTERIORS_READ,
Scopes.PRODUCTS_READ,
@@ -145,7 +169,7 @@ export class InteriorAPI extends BaseSQLDataSource {
.select('*')
.from('v_interior_image_urls')
.orderBy('position')
.where('print_id', printId)
.whereIn('print_id', printIds)
.then((rows) => {
return rows.map((row) => {
const res = {
+6
View File
@@ -19,6 +19,7 @@ import { moveS3File } from '../aws/s3';
import { DesignerAPI } from '../datasources/designer-api';
import { PrintProductDefaultsAPI } from '../datasources/printproduct-defaults-api';
import { PrintProductDefaults } from '../types/printproduct-defaults-types';
import { GraphQLError } from 'graphql';
const ProductBlacklist = {
async market(parent, _args, { dataSources }) {
@@ -68,6 +69,11 @@ async function getProductsResult(
input: ProductsFilterInput,
{ dataSources },
): Promise<ProductListResult> {
if (input.pagination.limit > 5000) {
throw new GraphQLError('Pagination limit cannot exceed 5000', {
extensions: { code: 'BAD_USER_INPUT' },
});
}
const total = (<ProductAPI>dataSources.productApi).getProductsTotal(input);
const items = (<ProductAPI>dataSources.productApi).getProducts(input);
const offset = input.pagination?.offset ?? 0;