|
|
@@ -3,13 +3,48 @@ import fs from 'fs-extra';
|
|
|
import path from 'path';
|
|
|
import { lastValueFrom } from 'rxjs';
|
|
|
|
|
|
-import { logColored } from './cli-utils';
|
|
|
+const loggerCtx = 'Populate';
|
|
|
|
|
|
// tslint:disable:no-console
|
|
|
/**
|
|
|
* @description
|
|
|
* Populates the Vendure server with some initial data and (optionally) product data from
|
|
|
- * a supplied CSV file.
|
|
|
+ * a supplied CSV file. The format of the CSV file is described in the section
|
|
|
+ * [Importing Product Data](/docs/developer-guide/importing-product-data).
|
|
|
+ *
|
|
|
+ * If the `channelOrToken` argument is provided, all ChannelAware entities (Products, ProductVariants,
|
|
|
+ * Assets, ShippingMethods, PaymentMethods etc.) will be assigned to the specified Channel.
|
|
|
+ * The argument can be either a Channel object or a valid channel `token`.
|
|
|
+ *
|
|
|
+ * Internally the `populate()` function does the following:
|
|
|
+ *
|
|
|
+ * 1. Uses the {@link Populator} to populate the {@link InitialData}.
|
|
|
+ * 2. If `productsCsvPath` is provided, uses {@link Importer} to populate Product data.
|
|
|
+ * 3. Uses {@Populator} to populate collections specified in the {@link InitialData}.
|
|
|
+ *
|
|
|
+ * @example
|
|
|
+ * ```TypeScript
|
|
|
+ * import { bootstrap } from '\@vendure/core';
|
|
|
+ * import { populate } from '\@vendure/core/cli';
|
|
|
+ * import { config } from './vendure-config.ts'
|
|
|
+ * import { initialData } from './my-initial-data.ts';
|
|
|
+ *
|
|
|
+ * const productsCsvFile = path.join(__dirname, 'path/to/products.csv')
|
|
|
+ *
|
|
|
+ * populate(
|
|
|
+ * () => bootstrap(config),
|
|
|
+ * initialData,
|
|
|
+ * productsCsvFile,
|
|
|
+ * )
|
|
|
+ * .then(app => app.close())
|
|
|
+ * .then(
|
|
|
+ * () => process.exit(0),
|
|
|
+ * err => {
|
|
|
+ * console.log(err);
|
|
|
+ * process.exit(1);
|
|
|
+ * },
|
|
|
+ * );
|
|
|
+ * ```
|
|
|
*
|
|
|
* @docsCategory import-export
|
|
|
*/
|
|
|
@@ -17,20 +52,39 @@ export async function populate<T extends INestApplicationContext>(
|
|
|
bootstrapFn: () => Promise<T | undefined>,
|
|
|
initialDataPathOrObject: string | object,
|
|
|
productsCsvPath?: string,
|
|
|
+ channelOrToken?: string | import('@vendure/core').Channel,
|
|
|
): Promise<T> {
|
|
|
const app = await bootstrapFn();
|
|
|
if (!app) {
|
|
|
throw new Error('Could not bootstrap the Vendure app');
|
|
|
}
|
|
|
+ let channel: import('@vendure/core').Channel | undefined;
|
|
|
+ const { ChannelService, Channel, Logger } = await import('@vendure/core');
|
|
|
+ if (typeof channelOrToken === 'string') {
|
|
|
+ channel = await app.get(ChannelService).getChannelFromToken(channelOrToken);
|
|
|
+ if (!channel) {
|
|
|
+ Logger.warn(
|
|
|
+ `Warning: channel with token "${channelOrToken}" was not found. Using default Channel instead.`,
|
|
|
+ loggerCtx,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } else if (channelOrToken instanceof Channel) {
|
|
|
+ channel = channelOrToken;
|
|
|
+ }
|
|
|
const initialData: import('@vendure/core').InitialData =
|
|
|
typeof initialDataPathOrObject === 'string'
|
|
|
? require(initialDataPathOrObject)
|
|
|
: initialDataPathOrObject;
|
|
|
|
|
|
- await populateInitialData(app, initialData, logColored);
|
|
|
+ await populateInitialData(app, initialData, channel);
|
|
|
|
|
|
if (productsCsvPath) {
|
|
|
- const importResult = await importProductsFromCsv(app, productsCsvPath, initialData.defaultLanguage);
|
|
|
+ const importResult = await importProductsFromCsv(
|
|
|
+ app,
|
|
|
+ productsCsvPath,
|
|
|
+ initialData.defaultLanguage,
|
|
|
+ channel,
|
|
|
+ );
|
|
|
if (importResult.errors && importResult.errors.length) {
|
|
|
const errorFile = path.join(process.cwd(), 'vendure-import-error.log');
|
|
|
console.log(
|
|
|
@@ -39,48 +93,44 @@ export async function populate<T extends INestApplicationContext>(
|
|
|
await fs.writeFile(errorFile, importResult.errors.join('\n'));
|
|
|
}
|
|
|
|
|
|
- logColored(`\nImported ${importResult.imported} products`);
|
|
|
+ Logger.info(`Imported ${importResult.imported} products`, loggerCtx);
|
|
|
|
|
|
- await populateCollections(app, initialData, logColored);
|
|
|
+ await populateCollections(app, initialData);
|
|
|
}
|
|
|
|
|
|
- logColored('\nDone!');
|
|
|
+ Logger.info('Done!', loggerCtx);
|
|
|
return app;
|
|
|
}
|
|
|
|
|
|
export async function populateInitialData(
|
|
|
app: INestApplicationContext,
|
|
|
initialData: import('@vendure/core').InitialData,
|
|
|
- loggingFn?: (message: string) => void,
|
|
|
+ channel?: import('@vendure/core').Channel,
|
|
|
) {
|
|
|
- const { Populator } = await import('@vendure/core');
|
|
|
+ const { Populator, Logger } = await import('@vendure/core');
|
|
|
const populator = app.get(Populator);
|
|
|
try {
|
|
|
- await populator.populateInitialData(initialData);
|
|
|
- if (typeof loggingFn === 'function') {
|
|
|
- loggingFn(`Populated initial data`);
|
|
|
- }
|
|
|
+ await populator.populateInitialData(initialData, channel);
|
|
|
+ Logger.info(`Populated initial data`, loggerCtx);
|
|
|
} catch (err: any) {
|
|
|
- console.log(err.message);
|
|
|
+ Logger.error(err.message, loggerCtx);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
export async function populateCollections(
|
|
|
app: INestApplicationContext,
|
|
|
initialData: import('@vendure/core').InitialData,
|
|
|
- loggingFn?: (message: string) => void,
|
|
|
+ channel?: import('@vendure/core').Channel,
|
|
|
) {
|
|
|
- const { Populator } = await import('@vendure/core');
|
|
|
+ const { Populator, Logger } = await import('@vendure/core');
|
|
|
const populator = app.get(Populator);
|
|
|
try {
|
|
|
if (initialData.collections.length) {
|
|
|
- await populator.populateCollections(initialData);
|
|
|
- if (typeof loggingFn === 'function') {
|
|
|
- loggingFn(`Created ${initialData.collections.length} Collections`);
|
|
|
- }
|
|
|
+ await populator.populateCollections(initialData, channel);
|
|
|
+ Logger.info(`Created ${initialData.collections.length} Collections`, loggerCtx);
|
|
|
}
|
|
|
} catch (err: any) {
|
|
|
- console.log(err.message);
|
|
|
+ Logger.info(err.message, loggerCtx);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -88,10 +138,16 @@ export async function importProductsFromCsv(
|
|
|
app: INestApplicationContext,
|
|
|
productsCsvPath: string,
|
|
|
languageCode: import('@vendure/core').LanguageCode,
|
|
|
+ channel?: import('@vendure/core').Channel,
|
|
|
): Promise<import('@vendure/core').ImportProgress> {
|
|
|
- const { Importer } = await import('@vendure/core');
|
|
|
+ const { Importer, RequestContextService } = await import('@vendure/core');
|
|
|
const importer = app.get(Importer);
|
|
|
+ const requestContextService = app.get(RequestContextService);
|
|
|
const productData = await fs.readFile(productsCsvPath, 'utf-8');
|
|
|
-
|
|
|
- return lastValueFrom(importer.parseAndImport(productData, languageCode, true));
|
|
|
+ const ctx = await requestContextService.create({
|
|
|
+ apiType: 'admin',
|
|
|
+ languageCode,
|
|
|
+ channelOrToken: channel,
|
|
|
+ });
|
|
|
+ return lastValueFrom(importer.parseAndImport(productData, ctx, true));
|
|
|
}
|