mock-data.service.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. import * as faker from 'faker/locale/en_GB';
  2. import * as fs from 'fs-extra';
  3. import gql from 'graphql-tag';
  4. import * as path from 'path';
  5. import {
  6. AddOptionGroupToProduct,
  7. Asset,
  8. CreateAddressInput,
  9. CreateCustomerInput,
  10. CreateFacet,
  11. CreateFacetValueWithFacetInput,
  12. CreateProduct,
  13. CreateProductOptionGroup,
  14. GenerateProductVariants,
  15. LanguageCode,
  16. ProductTranslationInput,
  17. UpdateProductVariants,
  18. } from 'shared/generated-types';
  19. import { CREATE_FACET } from '../../admin-ui/src/app/data/definitions/facet-definitions';
  20. import {
  21. ADD_OPTION_GROUP_TO_PRODUCT,
  22. CREATE_PRODUCT,
  23. CREATE_PRODUCT_OPTION_GROUP,
  24. GENERATE_PRODUCT_VARIANTS,
  25. UPDATE_PRODUCT_VARIANTS,
  26. } from '../../admin-ui/src/app/data/definitions/product-definitions';
  27. import { Channel } from '../src/entity/channel/channel.entity';
  28. import { Customer } from '../src/entity/customer/customer.entity';
  29. import { SimpleGraphQLClient } from './simple-graphql-client';
  30. // tslint:disable:no-console
  31. /**
  32. * A service for creating mock data via the GraphQL API.
  33. */
  34. export class MockDataService {
  35. apiUrl: string;
  36. constructor(private client: SimpleGraphQLClient, private logging = true) {
  37. // make the generated results deterministic
  38. faker.seed(1);
  39. }
  40. async populateChannels(channelCodes: string[]): Promise<Channel[]> {
  41. const channels: Channel[] = [];
  42. for (const code of channelCodes) {
  43. const channel = await this.client.query<any>(gql`
  44. mutation {
  45. createChannel(code: "${code}") {
  46. id
  47. code
  48. token
  49. }
  50. }
  51. `);
  52. channels.push(channel.createChannel);
  53. this.log(`Created Channel: ${channel.createChannel.code}`);
  54. }
  55. return channels;
  56. }
  57. async populateOptions(): Promise<string> {
  58. return this.client
  59. .query<CreateProductOptionGroup.Mutation, CreateProductOptionGroup.Variables>(
  60. CREATE_PRODUCT_OPTION_GROUP,
  61. {
  62. input: {
  63. code: 'size',
  64. translations: [
  65. { languageCode: LanguageCode.en, name: 'Size' },
  66. { languageCode: LanguageCode.de, name: 'Größe' },
  67. ],
  68. options: [
  69. {
  70. code: 'small',
  71. translations: [
  72. { languageCode: LanguageCode.en, name: 'Small' },
  73. { languageCode: LanguageCode.de, name: 'Klein' },
  74. ],
  75. },
  76. {
  77. code: 'large',
  78. translations: [
  79. { languageCode: LanguageCode.en, name: 'Large' },
  80. { languageCode: LanguageCode.de, name: 'Groß' },
  81. ],
  82. },
  83. ],
  84. },
  85. },
  86. )
  87. .then(data => {
  88. this.log('Created option group:', data.createProductOptionGroup.name);
  89. return data.createProductOptionGroup.id;
  90. });
  91. }
  92. async populateCustomers(count: number = 5): Promise<any> {
  93. for (let i = 0; i < count; i++) {
  94. const firstName = faker.name.firstName();
  95. const lastName = faker.name.lastName();
  96. const query1 = gql`
  97. mutation CreateCustomer($input: CreateCustomerInput!, $password: String) {
  98. createCustomer(input: $input, password: $password) {
  99. id
  100. emailAddress
  101. }
  102. }
  103. `;
  104. const variables1 = {
  105. input: {
  106. firstName,
  107. lastName,
  108. emailAddress: faker.internet.email(firstName, lastName),
  109. phoneNumber: faker.phone.phoneNumber(),
  110. } as CreateCustomerInput,
  111. password: 'test',
  112. };
  113. const customer: Customer | void = await this.client
  114. .query(query1, variables1)
  115. .then((data: any) => data.createCustomer as Customer, err => this.log(err));
  116. if (customer) {
  117. const query2 = gql`
  118. mutation($customerId: ID!, $input: CreateAddressInput!) {
  119. createCustomerAddress(customerId: $customerId, input: $input) {
  120. id
  121. streetLine1
  122. }
  123. }
  124. `;
  125. const variables2 = {
  126. input: {
  127. fullName: `${firstName} ${lastName}`,
  128. streetLine1: faker.address.streetAddress(),
  129. city: faker.address.city(),
  130. province: faker.address.county(),
  131. postalCode: faker.address.zipCode(),
  132. country: faker.address.country(),
  133. } as CreateAddressInput,
  134. customerId: customer.id,
  135. };
  136. await this.client.query(query2, variables2).then(
  137. data => {
  138. this.log(`Created Customer ${i + 1}:`, data);
  139. return data as Customer;
  140. },
  141. err => this.log(err),
  142. );
  143. }
  144. }
  145. }
  146. async populateAssets(): Promise<Asset[]> {
  147. const fileNames = await fs.readdir(path.join(__dirname, 'assets'));
  148. const filePaths = fileNames.map(fileName => path.join(__dirname, 'assets', fileName));
  149. return this.client.uploadAssets(filePaths).then(response => {
  150. console.log(`Created ${response.createAssets.length} Assets`);
  151. return response.createAssets;
  152. });
  153. }
  154. async populateProducts(count: number = 5, optionGroupId: string, assets: Asset[]): Promise<any> {
  155. for (let i = 0; i < count; i++) {
  156. const query = CREATE_PRODUCT;
  157. const name = faker.commerce.productName();
  158. const slug = name.toLowerCase().replace(/\s+/g, '-');
  159. const description = faker.lorem.sentence();
  160. const languageCodes = [LanguageCode.en, LanguageCode.de];
  161. // get 2 (pseudo) random asset ids
  162. const randomAssets = this.shuffleArray(assets).slice(0, 2);
  163. const variables: CreateProduct.Variables = {
  164. input: {
  165. translations: languageCodes.map(code =>
  166. this.makeProductTranslation(code, name, slug, description),
  167. ),
  168. assetIds: randomAssets.map(a => a.id),
  169. featuredAssetId: randomAssets[0].id,
  170. },
  171. };
  172. const product = await this.client
  173. .query<CreateProduct.Mutation, CreateProduct.Variables>(query, variables)
  174. .then(
  175. data => {
  176. this.log(`Created Product ${i + 1}:`, data.createProduct.name);
  177. return data;
  178. },
  179. err => this.log(err),
  180. );
  181. if (product) {
  182. await this.client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
  183. ADD_OPTION_GROUP_TO_PRODUCT,
  184. {
  185. productId: product.createProduct.id,
  186. optionGroupId,
  187. },
  188. );
  189. const prodWithVariants = await this.makeProductVariant(product.createProduct.id);
  190. const variants = prodWithVariants.generateVariantsForProduct.variants;
  191. for (const variant of variants) {
  192. const variantEN = variant.translations[0];
  193. const variantDE = { ...variantEN };
  194. variantDE.languageCode = LanguageCode.de;
  195. variantDE.name = variantDE.name.replace(LanguageCode.en, LanguageCode.de);
  196. delete variantDE.id;
  197. variant.translations.push(variantDE);
  198. }
  199. await this.client.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
  200. UPDATE_PRODUCT_VARIANTS,
  201. {
  202. input: variants.map(({ id, translations, sku, price }) => ({
  203. id,
  204. translations,
  205. sku,
  206. price,
  207. })),
  208. },
  209. );
  210. }
  211. }
  212. }
  213. async populateFacets() {
  214. await this.client.query<CreateFacet.Mutation, CreateFacet.Variables>(CREATE_FACET, {
  215. input: {
  216. code: 'brand',
  217. translations: [
  218. {
  219. languageCode: LanguageCode.en,
  220. name: 'Brand',
  221. },
  222. {
  223. languageCode: LanguageCode.en,
  224. name: 'Marke',
  225. },
  226. ],
  227. values: this.makeFacetValues(10),
  228. },
  229. });
  230. this.log('Created "brand" Facet');
  231. }
  232. private makeFacetValues(count: number): CreateFacetValueWithFacetInput[] {
  233. return Array.from({ length: count }).map(() => {
  234. const brand = faker.company.companyName();
  235. return {
  236. code: brand.replace(/\s/g, '_'),
  237. translations: [
  238. {
  239. languageCode: LanguageCode.en,
  240. name: brand,
  241. },
  242. {
  243. languageCode: LanguageCode.de,
  244. name: brand,
  245. },
  246. ],
  247. };
  248. });
  249. }
  250. private makeProductTranslation(
  251. languageCode: LanguageCode,
  252. name: string,
  253. slug: string,
  254. description: string,
  255. ): ProductTranslationInput {
  256. return {
  257. languageCode,
  258. name: `${languageCode} ${name}`,
  259. slug: `${languageCode} ${slug}`,
  260. description: `${languageCode} ${description}`,
  261. };
  262. }
  263. private async makeProductVariant(productId: string): Promise<GenerateProductVariants.Mutation> {
  264. const query = GENERATE_PRODUCT_VARIANTS;
  265. return this.client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(query, {
  266. productId,
  267. defaultSku: faker.random.alphaNumeric(5),
  268. defaultPrice: faker.random.number({
  269. min: 100,
  270. max: 1000,
  271. }),
  272. });
  273. }
  274. private log(...args: any[]) {
  275. if (this.logging) {
  276. console.log(...args);
  277. }
  278. }
  279. /**
  280. * Deterministacally randomize array element order. Returns a new
  281. * shuffled array and leaves the input array intact.
  282. * Using Durstenfeld shuffle algorithm.
  283. *
  284. * Source: https://stackoverflow.com/a/12646864/772859
  285. */
  286. private shuffleArray<T>(array: T[]): T[] {
  287. const clone = array.slice(0);
  288. for (let i = clone.length - 1; i > 0; i--) {
  289. const j = Math.floor((faker.random.number(1000) / 1000) * (i + 1));
  290. const temp = clone[i];
  291. clone[i] = clone[j];
  292. clone[j] = temp;
  293. }
  294. return clone;
  295. }
  296. }