From 584fe39d8031fcd1c8b0dfcf514c65a1566667b5 Mon Sep 17 00:00:00 2001 From: Nico Hauser Date: Tue, 26 Oct 2021 16:59:58 +0200 Subject: [PATCH] Use GraphQL schema to generate typescript types --- .gitignore | 3 +- codegen.config.yml | 6 + data-utils.ts | 68 +- data-utils/attributes.ts | 66 +- graphql-utils.ts | 1173 +++++---- import.ts | 89 +- package.json | 7 +- schema.graphql | 4791 ++++++++++++++++++++++++++++++++++ schema.ts | 5216 +++++++++++++++++++++++++++++++++++++ scripts/convert-schema.js | 12 + types.ts | 80 +- yarn.lock | 4382 ++++++++++++++++++++++++++++++- 12 files changed, 15073 insertions(+), 820 deletions(-) create mode 100644 codegen.config.yml create mode 100644 schema.graphql create mode 100644 schema.ts create mode 100644 scripts/convert-schema.js diff --git a/.gitignore b/.gitignore index f832ddf..ca702f4 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,5 @@ dist out*.json -excel*.json \ No newline at end of file +excel*.json +data/ \ No newline at end of file diff --git a/codegen.config.yml b/codegen.config.yml new file mode 100644 index 0000000..208a0c2 --- /dev/null +++ b/codegen.config.yml @@ -0,0 +1,6 @@ +schema: schema.graphql +generates: + ./schema.ts: + plugins: + - typescript + - typescript-operations diff --git a/data-utils.ts b/data-utils.ts index 7b0026e..de06e14 100644 --- a/data-utils.ts +++ b/data-utils.ts @@ -3,13 +3,12 @@ import { Record, ProductPrototype, ProductVariantPrototype, - AttributeFacet, - Facet, BulkDiscount, ID, - LanguageCode, - OptionGroup, - FacetValue, + Unpacked, + FacetPrototype, + FacetValuePrototype, + OptionGroupPrototype, } from "./types"; import { IMPORT_OPTION_GROUPS, @@ -20,6 +19,16 @@ import { CATEGORY_FACET_CODE, RESELLER_DISCOUNT_FACET_CODE, } from "./data-utils/facets"; +import { + CreateFacetInput, + CreateFacetValueInput, + CreateProductOptionGroupInput, + Facet, + FacetValue, + LanguageCode, + Maybe, + ProductOptionGroup, +} from "./schema"; export const SLUGIFY_OPTIONS = { lower: true, strict: true }; export const SEPERATOR = "|"; @@ -60,9 +69,12 @@ const getFloatingPointValue = ( }; const findItemByUnknownLocaleString = async < - ObjectTranslation extends { languageCode: LanguageCode; name: string }, + ObjectTranslation extends { + languageCode: LanguageCode; + name?: Maybe; + }, Obj extends { code: string; translations: ObjectTranslation[] }, - ItemTranslation extends { languageCode: LanguageCode; name: string }, + ItemTranslation extends { languageCode: LanguageCode; name?: Maybe }, Item extends { code: string; translations: ItemTranslation[] } >( object: Obj, @@ -77,11 +89,11 @@ const findItemByUnknownLocaleString = async < suggestions = suggestions.filter( (s) => !s.translations.find((t) => t.languageCode === languageCode) || - s.translations.find((t) => t.name.trim().toLowerCase() === v) + s.translations.find((t) => (t?.name || "").trim().toLowerCase() === v) ); const betterSuggestions = suggestions.filter((s) => - s.translations.find((t) => t.name.trim().toLowerCase() === v) + s.translations.find((t) => (t?.name || "").trim().toLowerCase() === v) ); const matches = @@ -90,7 +102,9 @@ const findItemByUnknownLocaleString = async < ? betterSuggestions : suggestions : items.filter((item) => - item.translations.find((t) => v === t.name.trim().toLowerCase()) + item.translations.find( + (t) => v === (t?.name || "").trim().toLowerCase() + ) ); const untranslated = items.filter( @@ -123,7 +137,10 @@ Wählen Sie die entsprechende Option aus.`, } }; -export const tableToProducts = async (records: Record[], facets: Facet[]) => { +export const tableToProducts = async ( + records: Record[], + facets: FacetPrototype[] +) => { const products: (ProductPrototype & { translationId?: ID; initialVariantSku?: ID; @@ -143,7 +160,7 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { )}` ); } - const id: ID = record[column]; + const id: ID = record[column].toString(); column = IMPORT_ATTRIBUTE_COLUMNS.parentId.find(inRecord); if (!column) { @@ -181,15 +198,15 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { let languageCode: LanguageCode; switch (languageField) { case "fr": - languageCode = "fr"; + languageCode = LanguageCode.Fr; break; case "de": default: - languageCode = "de"; + languageCode = LanguageCode.De; } column = IMPORT_ATTRIBUTE_COLUMNS.translationId.find(inRecord); - const translationId = column && record[column]; + const translationId = column && record[column].toString(); column = IMPORT_ATTRIBUTE_COLUMNS.name.find(inRecord); const nameField = column && record[column].toString().trim(); @@ -243,6 +260,8 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { ); } + price = Math.round(price * 100) / 100; + column = IMPORT_ATTRIBUTE_COLUMNS.minimumOrderQuantity.find(inRecord); const minimumOrderQuantity: number = getIntegerValue( column && record[column], @@ -341,9 +360,8 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { //modify some option group columns before processing them alltogether const unitColumn = IMPORT_ATTRIBUTE_COLUMNS.unit.find(inRecord); - const quantityPerUnitColumn = IMPORT_ATTRIBUTE_COLUMNS.quantityPerUnit.find( - inRecord - ); + const quantityPerUnitColumn = + IMPORT_ATTRIBUTE_COLUMNS.quantityPerUnit.find(inRecord); if (quantityPerUnitColumn && unitColumn) { const unit = record[unitColumn]; @@ -386,7 +404,7 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { bulkDiscounts = JSON.parse(bulkDiscountsField).map( ({ qty, ppu }: { qty: string | number; ppu: string | number }) => ({ quantity: parseInt(qty.toString()), - price: Math.floor(parseFloat(ppu.toString()) * 100), + price: Math.round(parseFloat(ppu.toString()) * 100), }) ); } catch (e) { @@ -409,7 +427,7 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { if (pricePerUnit > 0 && quantity > 0) { bulkDiscounts.push({ - price: pricePerUnit * 100, + price: Math.round(pricePerUnit * 100), quantity: quantity, }); } @@ -441,7 +459,7 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { products.push({ previousIds: [id], - translationId, + translationId: translationId?.toString(), sku, translations: [ { @@ -469,7 +487,7 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { } //import option groups - const groups: OptionGroup[] = []; + const groups: OptionGroupPrototype[] = []; IMPORT_OPTION_GROUPS.forEach((attribute) => { const columnKey = attribute.columnKeys.find(inRecord); @@ -496,12 +514,12 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { const facetValueCodes: string[] = []; - //add category facets + //add category facets values for (const c of categories) { const f = facets.find((f) => f.code === CATEGORY_FACET_CODE); if (f) { - let suggestions: FacetValue[] = []; + let suggestions: FacetValuePrototype[] = []; if (translationId) { const existingVariant = variants.find( (v) => v.translationId === translationId @@ -546,7 +564,7 @@ export const tableToProducts = async (records: Record[], facets: Facet[]) => { const f = facets.find((f) => f.code === RESELLER_DISCOUNT_FACET_CODE); if (f) { - let suggestions: FacetValue[] = []; + let suggestions: FacetValuePrototype[] = []; if (translationId) { const existingVariant = variants.find( (v) => v.translationId === translationId diff --git a/data-utils/attributes.ts b/data-utils/attributes.ts index e6b7f92..35eace0 100644 --- a/data-utils/attributes.ts +++ b/data-utils/attributes.ts @@ -1,4 +1,4 @@ -import { LanguageCode } from "types"; +import { LanguageCode } from "../schema"; interface ImportFacet { translations: { @@ -12,88 +12,88 @@ interface ImportFacet { export const IMPORT_OPTION_GROUPS: ImportFacet[] = [ { translations: [ - { languageCode: "de", name: "Ausführung" }, - { languageCode: "fr", name: "Modèle" }, + { languageCode: LanguageCode.De, name: "Ausführung" }, + { languageCode: LanguageCode.Fr, name: "Modèle" }, ], code: "model", columnKeys: ["Ausführung", "Ausfuehrung", "Modèle", "Modele"], }, { translations: [ - { languageCode: "de", name: "Pfeilrichtung" }, - { languageCode: "fr", name: "Sens de la flèche" }, + { languageCode: LanguageCode.De, name: "Pfeilrichtung" }, + { languageCode: LanguageCode.Fr, name: "Sens de la flèche" }, ], code: "arrow-direction", columnKeys: ["Pfeilrichtung", "Sens de la flèche", "Sens de la fleche"], }, { translations: [ - { languageCode: "de", name: "Grösse" }, - { languageCode: "fr", name: "Taille" }, + { languageCode: LanguageCode.De, name: "Grösse" }, + { languageCode: LanguageCode.Fr, name: "Taille" }, ], code: "size", columnKeys: ["Grösse", "Produkt Grösse", "Taille"], }, { translations: [ - { languageCode: "de", name: "Jahr" }, - { languageCode: "fr", name: "An" }, + { languageCode: LanguageCode.De, name: "Jahr" }, + { languageCode: LanguageCode.Fr, name: "An" }, ], code: "year", columnKeys: ["Jahr", "An"], }, { translations: [ - { languageCode: "de", name: "Farbe" }, - { languageCode: "fr", name: "Couleur" }, + { languageCode: LanguageCode.De, name: "Farbe" }, + { languageCode: LanguageCode.Fr, name: "Couleur" }, ], code: "color", columnKeys: ["Farbe", "Couleur"], }, { translations: [ - { languageCode: "de", name: "Format" }, - { languageCode: "fr", name: "Format" }, + { languageCode: LanguageCode.De, name: "Format" }, + { languageCode: LanguageCode.Fr, name: "Format" }, ], code: "format", columnKeys: ["Format"], }, { translations: [ - { languageCode: "de", name: "Leuchtdichte" }, - { languageCode: "fr", name: "Luminance" }, + { languageCode: LanguageCode.De, name: "Leuchtdichte" }, + { languageCode: LanguageCode.Fr, name: "Luminance" }, ], code: "luminance", columnKeys: ["Leuchtdichte_mcd", "Leuchtdichte", "Luminance"], }, { translations: [ - { languageCode: "de", name: "Material" }, - { languageCode: "fr", name: "Matériau" }, + { languageCode: LanguageCode.De, name: "Material" }, + { languageCode: LanguageCode.Fr, name: "Matériau" }, ], code: "material", columnKeys: ["Material", "Produkt Material", "Matériau", "Materiau"], }, { translations: [ - { languageCode: "de", name: "Norm" }, - { languageCode: "fr", name: "Norme" }, + { languageCode: LanguageCode.De, name: "Norm" }, + { languageCode: LanguageCode.Fr, name: "Norme" }, ], code: "norm", columnKeys: ["Norm", "Norme"], }, { translations: [ - { languageCode: "de", name: "PSPA Klasse" }, - { languageCode: "fr", name: "PSPA Classe" }, + { languageCode: LanguageCode.De, name: "PSPA Klasse" }, + { languageCode: LanguageCode.Fr, name: "PSPA Classe" }, ], code: "pspa-class", columnKeys: ["PSPA_Class", "Pspa-klasse"], }, { translations: [ - { languageCode: "de", name: "Ursprungsland" }, - { languageCode: "fr", name: "Pays d'origine" }, + { languageCode: LanguageCode.De, name: "Ursprungsland" }, + { languageCode: LanguageCode.Fr, name: "Pays d'origine" }, ], code: "country", columnKeys: [ @@ -105,8 +105,8 @@ export const IMPORT_OPTION_GROUPS: ImportFacet[] = [ }, { translations: [ - { languageCode: "de", name: "Druckeigenschaft(-en)" }, - { languageCode: "fr", name: "Propriétés d'impression" }, + { languageCode: LanguageCode.De, name: "Druckeigenschaft(-en)" }, + { languageCode: LanguageCode.Fr, name: "Propriétés d'impression" }, ], code: "print-property", columnKeys: [ @@ -117,32 +117,32 @@ export const IMPORT_OPTION_GROUPS: ImportFacet[] = [ }, { translations: [ - { languageCode: "de", name: "Einheit" }, - { languageCode: "fr", name: "Unité" }, + { languageCode: LanguageCode.De, name: "Einheit" }, + { languageCode: LanguageCode.Fr, name: "Unité" }, ], code: "unit", columnKeys: ["Einheit", "Produkt Einheit", "Unité"], }, { translations: [ - { languageCode: "de", name: "Symbolnummer" }, - { languageCode: "fr", name: "Numéro de symbole" }, + { languageCode: LanguageCode.De, name: "Symbolnummer" }, + { languageCode: LanguageCode.Fr, name: "Numéro de symbole" }, ], code: "symbol-number", columnKeys: ["Symbolnummer", "Numéro de symbole"], }, { translations: [ - { languageCode: "de", name: "Inhalt" }, - { languageCode: "fr", name: "Contenu" }, + { languageCode: LanguageCode.De, name: "Inhalt" }, + { languageCode: LanguageCode.Fr, name: "Contenu" }, ], code: "content", columnKeys: ["Inhalt", "Contenu"], }, { translations: [ - { languageCode: "de", name: "Variante" }, - { languageCode: "fr", name: "Variante" }, + { languageCode: LanguageCode.De, name: "Variante" }, + { languageCode: LanguageCode.Fr, name: "Variante" }, ], code: "variant", columnKeys: ["Variante", "Variante"], diff --git a/graphql-utils.ts b/graphql-utils.ts index 36cca24..b189ea7 100644 --- a/graphql-utils.ts +++ b/graphql-utils.ts @@ -3,40 +3,56 @@ import fetch from "node-fetch"; import FormData from "form-data"; import slugify from "slugify"; import { GraphQLClient, rawRequest } from "graphql-request"; -import { DeepRequired } from "ts-essentials"; import { SLUGIFY_OPTIONS } from "./data-utils"; -import { - ProductPrototype, - OptionGroup, - Facet, - ProductVariantCreation, - ProductVariantUpdate, - AttributeFacet, - LanguageCode, - ID, - Collection, - FacetValue, - Option, -} from "./types"; import { downloadFiles, cleanDownloads, getFilenameFromUrl, isValidUrl, } from "./utils"; +import { + Asset, + BulkDiscountUpdate, + Collection, + CollectionList, + CreateAssetResult, + CreateCollectionInput, + CreateFacetInput, + CreateFacetValueInput, + CreateProductOptionGroupInput, + CreateProductOptionInput, + CreateProductVariantInput, + Facet, + FacetList, + FacetValue, + LanguageCode, + Maybe, + Mutation, + Product, + ProductOption, + ProductOptionGroup, + ProductVariant, + Query, + UpdateFacetInput, + UpdateProductInput, + UpdateProductOptionGroupInput, + UpdateProductOptionInput, + UpdateProductVariantInput, +} from "./schema"; +import { + FacetPrototype, + FacetValuePrototype, + ID, + OptionGroupPrototype, + OptionPrototype, + ProductPrototype, +} from "./types"; export const uploadFilesToGraphql = async ( endpoint: string, authenticationToken: string, filepaths: string[] -): Promise<{ - data: { - createAssets: { - id: string; - name: string; - }[]; - }; -}> => { +): Promise => { const body = new FormData(); body.append( @@ -45,8 +61,14 @@ export const uploadFilesToGraphql = async ( query: /* GraphQL */ ` mutation CreateAssets($input: [CreateAssetInput!]!) { createAssets(input: $input) { - id - name + ... on Asset { + id + name + } + ... on MimeTypeError { + errorCode + message + } } } `, @@ -70,11 +92,27 @@ export const uploadFilesToGraphql = async ( body.append(index.toString(), fs.createReadStream(filepath)); }); - return await fetch(endpoint, { + const results: { + data: { createAssets: Mutation["createAssets"] }; + } = await fetch(endpoint, { method: "POST", body, headers: { Authorization: "Bearer " + authenticationToken }, }).then((r) => r.json()); + + const assets: Asset[] = []; + + results.data.createAssets.forEach((result) => { + if ("id" in result) { + assets.push(result); + } else { + throw new Error( + `Error Code: ${result.errorCode}. Message: ${result.message}` + ); + } + }); + + return assets; }; export const assertAuthentication = async ( @@ -84,13 +122,23 @@ export const assertAuthentication = async ( ) => { const login = await rawRequest( endpoint, - `mutation Login($username: String!, $password: String!){ - login(username: $username, password: $password){ - user{ - identifier + /* GraphQL */ ` + mutation Login($username: String!, $password: String!) { + login(username: $username, password: $password) { + ... on CurrentUser { + identifier + } + ... on InvalidCredentialsError { + errorCode + message + } + ... on NativeAuthStrategyError { + errorCode + message + } } } - }`, + `, { username, password, @@ -99,9 +147,18 @@ export const assertAuthentication = async ( const token = login.headers.get("vendure-auth-token"); - if ((login.errors && login.errors.length > 0) || token === null) { + if ( + (login.errors && login.errors.length > 0) || + //@ts-ignore + login?.data?.login?.errorCode || + token === null + ) { console.log("Authentifikation fehlgeschlagen!"); console.error(login.errors); + //@ts-ignore + console.error(login?.data?.login?.errorCode); + //@ts-ignore + console.error(login?.data?.login?.message); process.exit(0); } @@ -110,24 +167,13 @@ export const assertAuthentication = async ( export const getCollections = async ( graphQLClient: GraphQLClient -): Promise[]> => { +): Promise => { const response: { - collections: { - items: { - id: ID; - name: string; - translations: { - languageCode: LanguageCode; - name: string; - description: string; - slug: string; - }[]; - }[]; - }; - } = await graphQLClient.request( - `query { - collections{ - items{ + collections: Query["collections"]; + } = await graphQLClient.request(/* GraphQL */ ` + query { + collections { + items { id name translations { @@ -138,45 +184,28 @@ export const getCollections = async ( } } } - }` - ); + } + `); return response.collections.items; }; export const getFacets = async ( graphQLClient: GraphQLClient -): Promise[]> => { +): Promise => { const response: { - facets: { - items: { - id: ID; - code: string; - translations: { - languageCode: LanguageCode; - name: string; - }[]; - values: { - id: ID; - code: string; - translations: { - languageCode: LanguageCode; - name: string; - }[]; - }[]; - }[]; - }; - } = await graphQLClient.request( - `query { - facets{ - items{ + facets: Query["facets"]; + } = await graphQLClient.request(/* GraphQL */ ` + query { + facets { + items { id code translations { languageCode name } - values{ + values { id code translations { @@ -186,8 +215,8 @@ export const getFacets = async ( } } } - }` - ); + } + `); return response.facets.items; }; @@ -205,41 +234,40 @@ export const createCategoryCollection = async ( facetValueIds: ID[], isPrivate = false ): Promise => { - const response: { - createCollection: { - id: string; - name: string; - }; - } = await graphQLClient.request( - `mutation CreateCollection($input: CreateCollectionInput!){ - createCollection(input: $input){ - id - name - } - }`, - { - input: { - parentId: 1, - isPrivate, - translations: collection.translations, - filters: [ + const input: CreateCollectionInput = { + parentId: "1", + isPrivate, + translations: collection.translations, + filters: [ + { + code: "facet-value-filter", + arguments: [ + { + name: "facetValueIds", + value: JSON.stringify(facetValueIds), + }, { - code: "facet-value-filter", - arguments: [ - { - name: "facetValueIds", - type: "facetValueIds", - value: JSON.stringify(facetValueIds), - }, - { - name: "containsAny", - type: "boolean", - value: "false", - }, - ], + name: "containsAny", + value: "false", }, ], }, + ], + }; + + const response: { + createCollection: Mutation["createCollection"]; + } = await graphQLClient.request( + /* GraphQL */ ` + mutation CreateCollection($input: CreateCollectionInput!) { + createCollection(input: $input) { + id + name + } + } + `, + { + input, } ); @@ -251,28 +279,31 @@ export const getExistingProducts = async ( productGroupKeys: string[] ) => { const existing: { - getProductsByGroupKeys: { - id: string; - customFields: { groupKey: string }; - }[]; + getProductsByGroupKeys: Product[]; } = await graphQLClient.request( - `query GetProductsByGroupKeys($productGroupKeys: [String!]!){ - getProductsByGroupKeys(productGroupKeys: $productGroupKeys){ - id - customFields { - groupKey + /* GraphQL */ ` + query GetProductsByGroupKeys($productGroupKeys: [String!]!) { + getProductsByGroupKeys(productGroupKeys: $productGroupKeys) { + id + customFields { + groupKey + } } } - }`, + `, { productGroupKeys } ); const skuToProductId: { [sku: string]: ID } = {}; - existing.getProductsByGroupKeys.forEach( - (p: { id: string; customFields: { groupKey: string } }) => { + existing.getProductsByGroupKeys.forEach((p) => { + if (p.customFields && p.customFields.groupKey) { skuToProductId[p.customFields.groupKey] = p.id; + } else { + throw new Error( + `customFields.groupKey is undefined for product with the id ${p.id}` + ); } - ); + }); return skuToProductId; }; @@ -282,21 +313,20 @@ export const getFacetValues = async ( facetId: ID = "1" ) => { const response: { - facet: { - id: string; - values: { id: string; name: string; code: string }[]; - }; + facet: Facet; } = await graphQLClient.request( - `query Facet($id: ID!){ - facet(id: $id){ - id - values { + /* GraphQL */ ` + query Facet($id: ID!) { + facet(id: $id) { id - name - code + values { + id + name + code + } } } - }`, + `, { id: facetId } ); @@ -306,35 +336,35 @@ export const getFacetValues = async ( export const createFacetValues = async ( graphQLClient: GraphQLClient, facetId: ID = "1", - values: FacetValue[] -): Promise[]> => { + values: FacetValuePrototype[] +): Promise => { if (values.length === 0) { return []; } + const input: CreateFacetValueInput[] = values.map((v) => ({ + facetId, + code: v.code, + translations: v.translations, + })); + const response: { - createFacetValues: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }[]; + createFacetValues: Mutation["createFacetValues"]; } = await graphQLClient.request( - `mutation CreateFacetValues($input: [CreateFacetValueInput!]!){ - createFacetValues(input: $input){ - id - code - translations{ - languageCode - name + /* GraphQL */ ` + mutation CreateFacetValues($input: [CreateFacetValueInput!]!) { + createFacetValues(input: $input) { + id + code + translations { + languageCode + name + } } } - }`, + `, { - input: values.map((v) => ({ - facetId, - code: v.code, - translations: v.translations, - })), + input, } ); @@ -343,35 +373,35 @@ export const createFacetValues = async ( export const updateFacetValues = async ( graphQLClient: GraphQLClient, - values: DeepRequired[] -): Promise[]> => { + values: (FacetValuePrototype & { id: ID })[] +): Promise => { if (values.length === 0) { return []; } + const input: UpdateFacetInput[] = values.map((v) => ({ + id: v.id, + code: v.code, + translations: v.translations, + })); + const response: { - updateFacetValues: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }[]; + updateFacetValues: Mutation["updateFacetValues"]; } = await graphQLClient.request( - `mutation UpdateFacetValues($input: [UpdateFacetValueInput!]!){ - updateFacetValues(input: $input){ - id - code - translations{ - languageCode - name + /* GraphQL */ ` + mutation UpdateFacetValues($input: [UpdateFacetValueInput!]!) { + updateFacetValues(input: $input) { + id + code + translations { + languageCode + name + } } } - }`, + `, { - input: values.map((v) => ({ - id: v.id, - code: v.code, - translations: v.translations, - })), + input, } ); @@ -418,10 +448,7 @@ export const findOrCreateAssets = async ( cleanDownloads(); - return [ - ...Object.values(assetIdsByName), - ...uploadResponse.data.createAssets.map((a) => a.id), - ]; + return [...Object.values(assetIdsByName), ...uploadResponse.map((a) => a.id)]; }; export const getAssetsIdByName = async ( @@ -431,13 +458,15 @@ export const getAssetsIdByName = async ( const mappings = await Promise.all( names.map(async (name) => { const value: { - assetByName: { id: number }; + assetByName: Query["assetByName"]; } = await graphQLClient.request( - `query AssetByName($name: String!){ - assetByName(name: $name){ - id - } - }`, + /* GraphQL */ ` + query AssetByName($name: String!) { + assetByName(name: $name) { + id + } + } + `, { name } ); @@ -455,19 +484,19 @@ export const getAssetsIdByName = async ( export const createOrUpdateFacets = async ( graphQLClient: GraphQLClient, - facets: Facet[] -): Promise[]> => { + facets: FacetPrototype[] +): Promise => { return Promise.all( facets.map(async (f) => { - let facet: DeepRequired; + let facet: Facet; if (f.id) { facet = await updateFacet(graphQLClient, { ...f, id: f.id }); } else { facet = await createFacet(graphQLClient, f); } - const u: DeepRequired[] = []; - const c: FacetValue[] = []; + const u: (FacetValuePrototype & { id: ID })[] = []; + const c: FacetValuePrototype[] = []; f.values.forEach((v) => { if (v.id) { @@ -482,86 +511,70 @@ export const createOrUpdateFacets = async ( updateFacetValues(graphQLClient, u), ]); - return getFacet(graphQLClient, facet.id); + return assertGetFacet(graphQLClient, facet.id); }) ); }; -export const getFacet = async ( +export const assertGetFacet = async ( graphQLClient: GraphQLClient, id: ID -): Promise> => { - const response: { - facet: { - id: ID; - code: string; - translations: { - languageCode: LanguageCode; - name: string; - }[]; - values: { - id: ID; - code: string; - translations: { - languageCode: LanguageCode; - name: string; - }[]; - }[]; - }; - } = await graphQLClient.request( - `query Facet($id: ID!) { - facet(id: $id){ - id - code - translations { - languageCode - name - } - values{ +): Promise => { + const response: { facet: Query["facet"] } = await graphQLClient.request( + /* GraphQL */ ` + query Facet($id: ID!) { + facet(id: $id) { id code translations { languageCode name } + values { + id + code + translations { + languageCode + name + } + } } } - }`, + `, { id } ); + if (!response.facet) { + throw new Error(`The facet with id ${id} does not exist!`); + } + return response.facet; }; export const createFacet = async ( graphQLClient: GraphQLClient, - facet: { - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }, + facet: FacetPrototype, isPrivate = false -): Promise> => { +): Promise => { + const input: CreateFacetInput = { + code: facet.code, + isPrivate, + translations: facet.translations, + }; + const response: { - createFacet: { - id: ID; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - values: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }[]; - }; + createFacet: Mutation["createFacet"]; } = await graphQLClient.request( - `mutation CreateFacet($input: CreateFacetInput!) { - createFacet(input: $input) { + /* GraphQL */ ` + mutation CreateFacet($input: CreateFacetInput!) { + createFacet(input: $input) { id code translations { languageCode name } - values{ + values { id code translations { @@ -570,13 +583,10 @@ export const createFacet = async ( } } } - }`, + } + `, { - input: { - code: facet.code, - isPrivate, - translations: facet.translations, - }, + input, } ); @@ -585,34 +595,29 @@ export const createFacet = async ( export const updateFacet = async ( graphQLClient: GraphQLClient, - facet: { - id: ID; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }, + facet: FacetPrototype & { id: ID }, isPrivate = false -): Promise> => { +): Promise => { + const input: UpdateFacetInput = { + id: facet.id, + code: facet.code, + isPrivate, + translations: facet.translations, + }; + const response: { - updateFacet: { - id: ID; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - values: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }[]; - }; + updateFacet: Mutation["updateFacet"]; } = await graphQLClient.request( - `mutation UpdateFacet($input: UpdateFacetInput!) { - updateFacet(input: $input) { + /* GraphQL */ ` + mutation UpdateFacet($input: UpdateFacetInput!) { + updateFacet(input: $input) { id code translations { languageCode name } - values{ + values { id code translations { @@ -621,14 +626,10 @@ export const updateFacet = async ( } } } - }`, + } + `, { - input: { - id: facet.id, - code: facet.code, - isPrivate, - translations: facet.translations, - }, + input, } ); @@ -641,52 +642,52 @@ export const findOrCreateFacet = async ( isPrivate = false ) => { const searchResponse: { - facets: { - items: { id: string; code: string }[]; - }; - } = await graphQLClient.request( - `query { - facets { - items{ - id - code - } + facets: Query["facets"]; + } = await graphQLClient.request(/* GraphQL */ ` + query { + facets { + items { + id + code } - }` - ); + } + } + `); - const f = searchResponse.facets.items.find((f) => f.code === facet.code); + const f = searchResponse.facets.items.find( + (f: Facet) => f.code === facet.code + ); if (f) { return findOrCreateFacetValues(graphQLClient, f.id, facet.values); } else { + const input: CreateFacetInput = { + code: facet.code, + isPrivate, + translations: facet.translations, + values: facet.values.map((v) => ({ + code: v.code, + translations: v.translations, + })), + }; + const response: { - createFacet: { - id: string; - code: string; - values: { id: string; code: string }[]; - }; + createFacet: Mutation["createFacet"]; } = await graphQLClient.request( - `mutation CreateFacet($input: CreateFacetInput!) { - createFacet(input: $input) { + /* GraphQL */ ` + mutation CreateFacet($input: CreateFacetInput!) { + createFacet(input: $input) { id code - values{ + values { id code } } - }`, + } + `, { - input: { - code: facet.code, - isPrivate, - translations: facet.translations, - values: facet.values.map((v) => ({ - code: v.code, - translations: v.translations, - })), - }, + input, } ); @@ -747,11 +748,13 @@ export const createProduct = async ( const response: { createProduct: { id: string }; } = await graphQLClient.request( - `mutation CreateProduct($input: CreateProductInput!){ - createProduct(input: $input){ - id + /* GraphQL */ ` + mutation CreateProduct($input: CreateProductInput!) { + createProduct(input: $input) { + id + } } - }`, + `, { input: { featuredAssetId: assetIds[0], @@ -781,67 +784,66 @@ export const createProduct = async ( export const updateProduct = async ( graphQLClient: GraphQLClient, - product: Required, + product: ProductPrototype & { id: ID }, facetValueCodeToId: { [code: string]: ID } -) => { - return await graphQLClient.request( - `mutation UpdateProduct($input: UpdateProductInput!){ - updateProduct(input: $input){ - id +): Promise => { + const input: UpdateProductInput = { + id: product.id, + enabled: true, + //assets stay the same + /*featuredAssetId: null, + assetIds: [], + facetValueIds: product.facetValueCodes.map((code) => { + if (!(code in facetValueCodeToId)) { + throw new Error( + `Es wurde keine ID für ${code} in [${Object.keys( + facetValueCodeToId + ).join(", ")}] grefunden!` + ); } - }`, - { - input: { - id: product.id, - enabled: true, - //assets stay the same - /*featuredAssetId: null, - assetIds: [], - facetValueIds: product.facetValueCodes.map((code) => { - if (!(code in facetValueCodeToId)) { - throw new Error( - `Es wurde keine ID für ${code} in [${Object.keys( - facetValueCodeToId - ).join(", ")}] grefunden!` - ); - } - return facetValueCodeToId[code]; - }),*/ - // translations: product.translations, - customFields: { - productRecommendationsEnabled: false, - groupKey: product.sku, - }, - }, + return facetValueCodeToId[code]; + }),*/ + // translations: product.translations, + customFields: { + productRecommendationsEnabled: false, + groupKey: product.sku, + }, + }; + + const response: { + updateProduct: Mutation["updateProduct"]; + } = await graphQLClient.request( + /* GraphQL */ ` + mutation UpdateProduct($input: UpdateProductInput!) { + updateProduct(input: $input) { + id + } + } + `, + { + input, } ); + + return response.updateProduct; }; export const getOptionGroups = async ( graphQLClient: GraphQLClient -): Promise[]> => { +): Promise => { const optionGroupsResponse: { - productOptionGroups: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - options: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }[]; - }[]; - } = await graphQLClient.request( - `query { - productOptionGroups{ + productOptionGroups: Query["productOptionGroups"]; + } = await graphQLClient.request(/* GraphQL */ ` + query { + productOptionGroups { id code translations { languageCode name } - options{ + options { id code translations { @@ -850,8 +852,8 @@ export const getOptionGroups = async ( } } } - }` - ); + } + `); return optionGroupsResponse.productOptionGroups; }; @@ -859,65 +861,62 @@ export const getOptionGroups = async ( export const getOptionGroupsByProductId = async ( graphQLClient: GraphQLClient, productId: ID -): Promise[]> => { +): Promise => { const optionGroupsResponse: { - product: { - optionGroups: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - options: { - id: string; - code: string; - translations: { languageCode: LanguageCode; name: string }[]; - }[]; - }[]; - }; + product: Query["product"]; } = await graphQLClient.request( - `query optionGroups($productId: ID!){ - product(id: $productId){ - optionGroups{ - id - code - translations { - languageCode - name - } - options{ + /* GraphQL */ ` + query optionGroups($productId: ID!) { + product(id: $productId) { + optionGroups { id code translations { languageCode name } + options { + id + code + translations { + languageCode + name + } + } } } } - }`, + `, { productId, } ); + if (!optionGroupsResponse.product) { + throw new Error( + `Produkt mit der Id ${productId} konnte nicht gefunden werden!` + ); + } + return optionGroupsResponse.product.optionGroups; }; export const createOrUpdateOptionGroups = async ( graphQLClient: GraphQLClient, - optionGroups: OptionGroup[], + optionGroups: OptionGroupPrototype[], productId: ID -): Promise[]> => { +): Promise => { const existingOptionGroups = await getOptionGroupsByProductId( graphQLClient, productId ); - const response: DeepRequired[] = []; + const response: ProductOptionGroup[] = []; let deletedVariants = false; for (const g of optionGroups) { - let group: DeepRequired; + let group: ProductOptionGroup; const existingGroup = existingOptionGroups.find((gr) => gr.code === g.code); if (existingGroup) { @@ -941,8 +940,8 @@ export const createOrUpdateOptionGroups = async ( await assignOptionGroupToProduct(graphQLClient, productId, group.id); } - const u: DeepRequired