Merge branch 'master' into 2369-rework-stock-product-handling

This commit is contained in:
Rikard Bartholf
2022-09-14 15:41:24 +02:00
10 changed files with 5142 additions and 6755 deletions
+4993 -6713
View File
File diff suppressed because it is too large Load Diff
+26 -26
View File
@@ -17,46 +17,46 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.49.0", "@aws-sdk/client-s3": "^3.95.0",
"@aws-sdk/client-sqs": "^3.49.0", "@aws-sdk/client-sqs": "^3.95.0",
"@aws-sdk/client-translate": "^3.49.0", "@aws-sdk/client-translate": "^3.95.0",
"@aws-sdk/s3-request-presigner": "^3.49.0", "@aws-sdk/s3-request-presigner": "^3.95.0",
"@newrelic/apollo-server-plugin": "^1.2.0", "@newrelic/apollo-server-plugin": "^1.3.0",
"apollo-datasource": "^3.3.1", "apollo-datasource": "^3.3.1",
"apollo-datasource-rest": "^3.5.1", "apollo-datasource-rest": "^3.5.3",
"apollo-server": "^3.6.2", "apollo-server": "^3.7.0",
"async-redis": "^2.0.0", "async-redis": "^2.0.0",
"axios": "^0.25.0", "axios": "^0.27.2",
"datasource-sql": "^1.6.0", "datasource-sql": "^1.6.0",
"dotenv": "^16.0.0", "dotenv": "^16.0.1",
"graphql": "^16.3.0", "graphql": "^16.5.0",
"graphql-scalars": "^1.14.1", "graphql-scalars": "^1.17.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"jwk-to-pem": "^2.0.5", "jwk-to-pem": "^2.0.5",
"knex": "^1.0.2", "knex": "^2.0.0",
"knex-stringcase": "^1.4.6", "knex-stringcase": "^1.4.6",
"newrelic": "^8.7.1", "newrelic": "^8.11.1",
"nodemon": "^2.0.15", "nodemon": "^2.0.16",
"pg": "^8.7.3", "pg": "^8.7.3",
"pg-hstore": "^2.3.4", "pg-hstore": "^2.3.4",
"prettier": "^2.5.1", "prettier": "^2.6.2",
"slug": "^5.2.0", "slug": "^5.3.0",
"stringcase": "^4.3.1", "stringcase": "^4.3.1",
"ts-node": "^10.4.0", "ts-node": "^10.7.0",
"typescript": "^4.5.5", "typescript": "^4.6.4",
"util": "^0.12.4", "util": "^0.12.4",
"validator": "^13.7.0" "validator": "^13.7.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.4.0", "@types/jest": "^27.5.1",
"@typescript-eslint/eslint-plugin": "^5.10.2", "@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.10.2", "@typescript-eslint/parser": "^5.25.0",
"eslint": "^8.8.0", "eslint": "^8.15.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"isomorphic-fetch": "^3.0.0", "isomorphic-fetch": "^3.0.0",
"jest": "^27.5.0", "jest": "^28.1.0",
"testcontainers": "^8.2.0", "testcontainers": "^8.10.1",
"ts-jest": "^27.1.3" "ts-jest": "^28.0.2"
} }
} }
+5 -12
View File
@@ -1,11 +1,14 @@
import { SQLDataSource } from 'datasource-sql'; import { SQLDataSource } from 'datasource-sql';
import { Keyword, KeywordType } from '../types/keyword-types'; import { Keyword, KeywordType } from '../types/keyword-types';
import { TextsAPI } from './texts-api';
const MINUTE = 60; const MINUTE = 60;
export class KeywordAPI extends SQLDataSource { export class KeywordAPI extends SQLDataSource {
constructor(config) { textsApi: TextsAPI;
constructor(config, textsApi) {
super(config); super(config);
this.textsApi = textsApi;
} }
getType(data: any) { getType(data: any) {
@@ -98,18 +101,8 @@ ORDER BY keywords.value
}); });
} }
} }
await this.textsApi.translateKeyword(keywordId, name);
return keywordId; return keywordId;
} }
} }
/*
category keywords:
SELECT category_keyword.category_id, keywords.value
FROM category_keyword
JOIN keywords ON category_keyword.keyword_id = keywords.id;
texts:
FROM categorydata cd
JOIN categorydatakeys cdk ON cd.datakey_id = cdk.id
*/
+14 -3
View File
@@ -194,6 +194,18 @@ export class ProductAPI extends BaseSQLDataSource {
return res.find(Boolean); return res.find(Boolean);
} }
async getProductByPath(path: string): Promise<Maybe<Product>> {
const query = sql.productByPath(path);
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 getDesignerProducts(designerId: number): Promise<Array<Product>> { async getDesignerProducts(designerId: number): Promise<Array<Product>> {
const query = sql.designerProducts(); const query = sql.designerProducts();
@@ -381,7 +393,7 @@ export class ProductAPI extends BaseSQLDataSource {
/** /**
* @function addProduct * @function addProduct
* @description Adds a new product to system, will use batchname and name to * @description Adds a new product to system, will use name to
* set a unique path, artNo is the productId with e infront. * set a unique path, artNo is the productId with e infront.
*/ */
async addProduct( async addProduct(
@@ -390,8 +402,7 @@ export class ProductAPI extends BaseSQLDataSource {
designerId: number, designerId: number,
): Promise<number> { ): Promise<number> {
const fixedBatch = !!batch ? batch : ''; const fixedBatch = !!batch ? batch : '';
const path = batch ? fixedBatch + ' ' + name : name; const productPath = slug(name);
const productPath = slug(path);
const safePath = await this.getUniqueProductPath(productPath, null); const safePath = await this.getUniqueProductPath(productPath, null);
const idres = await this.knex const idres = await this.knex
+9
View File
@@ -119,6 +119,15 @@ export function product(id: number) {
); );
} }
export function productByPath(path: string) {
return (
baseQuery +
/* sql */ `
WHERE products.path = '${path}'
`
);
}
export function categoryProducts(categoryId: number) { export function categoryProducts(categoryId: number) {
return ( return (
baseQuery + baseQuery +
+25
View File
@@ -14,6 +14,31 @@ export class TextsAPI extends SQLDataSource {
this.marketLocaleApi = marketLocaleApi; this.marketLocaleApi = marketLocaleApi;
} }
async translateKeyword(keywordId: number, keyword: string): Promise<void> {
const locales = await this.marketLocaleApi.getLocales();
for (let i = 0; i < locales.length; i++) {
let langCode = this.getLanguageCode(locales[i]);
// If we have a fallback locale we skip translating
if (locales[i].fallbackId) {
continue;
}
const input: TranslateInput = {
text: keyword,
targetLanguageCode: langCode,
sourceLanguageCode: 'en',
};
const translated = await translateText(input);
const res = await this.knex('keywords_i18n')
.insert({
value: translated,
keyword_id: keywordId,
locale_id: locales[i].id,
})
.returning('*');
}
}
async translateProductName( async translateProductName(
name: string, name: string,
oldPath: string, oldPath: string,
+1 -1
View File
@@ -40,7 +40,7 @@ const imageServerApi = new ImageServerApi();
const interiorsLambdaApi = new InteriorsLambdaAPI(); const interiorsLambdaApi = new InteriorsLambdaAPI();
const interiorApi = new InteriorAPI(knexConfig, imageServerApi); const interiorApi = new InteriorAPI(knexConfig, imageServerApi);
const printProductDefaultsApi = new PrintProductDefaultsAPI(knexConfig); const printProductDefaultsApi = new PrintProductDefaultsAPI(knexConfig);
const keywordApi = new KeywordAPI(knexConfig); const keywordApi = new KeywordAPI(knexConfig, textsApi);
const orderApi = new OrderAPI(knexConfig); const orderApi = new OrderAPI(knexConfig);
const productApi = new ProductAPI( const productApi = new ProductAPI(
knexConfig, knexConfig,
+9
View File
@@ -121,6 +121,14 @@ async function getProduct(_, { id }, { dataSources, auth }): Promise<Product> {
return (<ProductAPI>dataSources.productApi).getProduct(id); return (<ProductAPI>dataSources.productApi).getProduct(id);
} }
async function getProductByPath(
_,
{ path },
{ dataSources },
): Promise<Product> {
return (<ProductAPI>dataSources.productApi).getProductByPath(path);
}
export const productTypeDefs = { export const productTypeDefs = {
ProductBlacklist, ProductBlacklist,
Product, Product,
@@ -130,6 +138,7 @@ export const productTypeDefs = {
export const productQueryTypeDefs = { export const productQueryTypeDefs = {
products: getProductsResult, products: getProductsResult,
product: getProduct, product: getProduct,
productByPath: getProductByPath,
productsSearch: getProductsSearchResult, productsSearch: getProductsSearchResult,
}; };
+1
View File
@@ -25,6 +25,7 @@ type Query {
filter: ProductsFilterInput filter: ProductsFilterInput
): ProductsResult ): ProductsResult
product(id: Int!): Product product(id: Int!): Product
productByPath(path: String!): Product
productsSearch(q: String!): ProductsSearchResult productsSearch(q: String!): ProductsSearchResult
categories: [Category] categories: [Category]
category(id: Int!): Category category(id: Int!): Category
+59
View File
@@ -0,0 +1,59 @@
const {
TranslateClient,
TranslateTextCommand,
} = require('@aws-sdk/client-translate');
const db = require('knex')({
client: 'pg',
connection: 'postgresql://fondberg:@docker.for.mac.localhost/photowall',
searchPath: ['public'],
});
async function translateText(input) {
const translateClient = new TranslateClient({
region: 'eu-west-1',
});
const translateTextCommand = new TranslateTextCommand({
SourceLanguageCode: input.sourceLanguageCode ?? 'auto',
TargetLanguageCode: input.targetLanguageCode,
Text: input.text,
});
return translateClient
.send(translateTextCommand)
.then((resp) => resp.TranslatedText);
}
function getLanguageCode(locale) {
let langCode = locale.value.substr(0, 2);
return langCode == 'nn' ? 'no' : langCode;
}
async function translate(locales, keyword) {
for (let i = 0; i < locales.length; i++) {
const langCode = getLanguageCode(locales[i]);
const input = {
text: keyword.value,
targetLanguageCode: langCode,
};
const translated = await translateText(input);
console.log(keyword.value + ' : ' + translated);
const res = await db('keywords_i18n')
.insert({
value: translated,
keyword_id: keyword.id,
locale_id: locales[i].id,
})
.returning('*');
}
}
async function main() {
const locales = await db.select().table('locales').whereNull('fallback_id');
const keywords = await db.select().table('keywords');
for (let i = 0; i < keywords.length; i++) {
await translate(locales, keywords[i]);
}
}
main();