populate.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { INestApplicationContext } from '@nestjs/common';
  2. import fs from 'fs-extra';
  3. import path from 'path';
  4. import { lastValueFrom } from 'rxjs';
  5. const loggerCtx = 'Populate';
  6. /* eslint-disable no-console */
  7. /**
  8. * @description
  9. * Populates the Vendure server with some initial data and (optionally) product data from
  10. * a supplied CSV file. The format of the CSV file is described in the section
  11. * [Importing Product Data](/guides/developer-guide/importing-data/).
  12. *
  13. * If the `channelOrToken` argument is provided, all ChannelAware entities (Products, ProductVariants,
  14. * Assets, ShippingMethods, PaymentMethods etc.) will be assigned to the specified Channel.
  15. * The argument can be either a Channel object or a valid channel `token`.
  16. *
  17. * Internally the `populate()` function does the following:
  18. *
  19. * 1. Uses the {@link Populator} to populate the {@link InitialData}.
  20. * 2. If `productsCsvPath` is provided, uses {@link Importer} to populate Product data.
  21. * 3. Uses {@link Populator} to populate collections specified in the {@link InitialData}.
  22. *
  23. * @example
  24. * ```ts
  25. * import { bootstrap } from '\@vendure/core';
  26. * import { populate } from '\@vendure/core/cli';
  27. * import { config } from './vendure-config.ts'
  28. * import { initialData } from './my-initial-data.ts';
  29. *
  30. * const productsCsvFile = path.join(__dirname, 'path/to/products.csv')
  31. *
  32. * populate(
  33. * () => bootstrap(config),
  34. * initialData,
  35. * productsCsvFile,
  36. * )
  37. * .then(app => app.close())
  38. * .then(
  39. * () => process.exit(0),
  40. * err => {
  41. * console.log(err);
  42. * process.exit(1);
  43. * },
  44. * );
  45. * ```
  46. *
  47. * @docsCategory import-export
  48. */
  49. export async function populate<T extends INestApplicationContext>(
  50. bootstrapFn: () => Promise<T | undefined>,
  51. initialDataPathOrObject: string | object,
  52. productsCsvPath?: string,
  53. channelOrToken?: string | import('@vendure/core').Channel,
  54. ): Promise<T> {
  55. const app = await bootstrapFn();
  56. if (!app) {
  57. throw new Error('Could not bootstrap the Vendure app');
  58. }
  59. let channel: import('@vendure/core').Channel | undefined;
  60. const { ChannelService, Channel, Logger } = await import('@vendure/core');
  61. if (typeof channelOrToken === 'string') {
  62. channel = await app.get(ChannelService).getChannelFromToken(channelOrToken);
  63. if (!channel) {
  64. Logger.warn(
  65. `Warning: channel with token "${channelOrToken}" was not found. Using default Channel instead.`,
  66. loggerCtx,
  67. );
  68. }
  69. } else if (channelOrToken instanceof Channel) {
  70. channel = channelOrToken;
  71. }
  72. const initialData: import('@vendure/core').InitialData =
  73. typeof initialDataPathOrObject === 'string'
  74. ? require(initialDataPathOrObject)
  75. : initialDataPathOrObject;
  76. await populateInitialData(app, initialData, channel);
  77. if (productsCsvPath) {
  78. const importResult = await importProductsFromCsv(
  79. app,
  80. productsCsvPath,
  81. initialData.defaultLanguage,
  82. channel,
  83. );
  84. if (importResult.errors && importResult.errors.length) {
  85. const errorFile = path.join(process.cwd(), 'vendure-import-error.log');
  86. Logger.error(
  87. `${importResult.errors.length} errors encountered when importing product data. See: ${errorFile}`,
  88. loggerCtx,
  89. );
  90. await fs.writeFile(errorFile, importResult.errors.join('\n'));
  91. }
  92. Logger.info(`Imported ${importResult.imported} products`, loggerCtx);
  93. await populateCollections(app, initialData, channel);
  94. }
  95. Logger.info('Done!', loggerCtx);
  96. return app;
  97. }
  98. export async function populateInitialData(
  99. app: INestApplicationContext,
  100. initialData: import('@vendure/core').InitialData,
  101. channel?: import('@vendure/core').Channel,
  102. ) {
  103. const { Populator, Logger } = await import('@vendure/core');
  104. const populator = app.get(Populator);
  105. try {
  106. await populator.populateInitialData(initialData, channel);
  107. Logger.info('Populated initial data', loggerCtx);
  108. } catch (err: any) {
  109. Logger.error(err.message, loggerCtx);
  110. }
  111. }
  112. export async function populateCollections(
  113. app: INestApplicationContext,
  114. initialData: import('@vendure/core').InitialData,
  115. channel?: import('@vendure/core').Channel,
  116. ) {
  117. const { Populator, Logger } = await import('@vendure/core');
  118. const populator = app.get(Populator);
  119. try {
  120. if (initialData.collections.length) {
  121. await populator.populateCollections(initialData, channel);
  122. Logger.info(`Created ${initialData.collections.length} Collections`, loggerCtx);
  123. }
  124. } catch (err: any) {
  125. Logger.info(err.message, loggerCtx);
  126. }
  127. }
  128. export async function importProductsFromCsv(
  129. app: INestApplicationContext,
  130. productsCsvPath: string,
  131. languageCode: import('@vendure/core').LanguageCode,
  132. channel?: import('@vendure/core').Channel,
  133. ): Promise<import('@vendure/core').ImportProgress> {
  134. const { Importer, RequestContextService } = await import('@vendure/core');
  135. const importer = app.get(Importer);
  136. const requestContextService = app.get(RequestContextService);
  137. const productData = await fs.readFile(productsCsvPath, 'utf-8');
  138. const ctx = await requestContextService.create({
  139. apiType: 'admin',
  140. languageCode,
  141. channelOrToken: channel,
  142. });
  143. return lastValueFrom(importer.parseAndImport(productData, ctx, true));
  144. }