From b7712ff700a4b3f02fad44acc38d53e198d47c84 Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Thu, 3 Oct 2024 14:13:28 +0300 Subject: [PATCH] docs: add guide on how to seed data --- .../custom-cli-scripts/seed-data/page.mdx | 200 ++++++++++++++++++ .../book/app/more-resources/examples/page.mdx | 6 + www/apps/book/generated/edit-dates.mjs | 5 +- www/apps/book/sidebar.mjs | 7 + 4 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 www/apps/book/app/advanced-development/custom-cli-scripts/seed-data/page.mdx diff --git a/www/apps/book/app/advanced-development/custom-cli-scripts/seed-data/page.mdx b/www/apps/book/app/advanced-development/custom-cli-scripts/seed-data/page.mdx new file mode 100644 index 0000000000000..15d1a9f4321d4 --- /dev/null +++ b/www/apps/book/app/advanced-development/custom-cli-scripts/seed-data/page.mdx @@ -0,0 +1,200 @@ +export const metadata = { + title: `${pageNumber} Seed Data with Custom CLI Script`, +} + +# {metadata.title} + +In this chapter, you'll learn how to seed data using a custom CLI script. + +## How to Seed Data + +To seed dummy data for development or demo purposes, use a custom CLI script. + +In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](!resources!/medusa-workflows-reference), to seed data. + +### Example: Seed Dummy Products + +In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products. + +First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script: + +```bash npm2yarn +npm install --save-dev @faker-js/faker +``` + +Then, create the file `src/scripts/demo-products.ts` with the following content: + +export const highlights = [ + ["16", "salesChannelModuleService", "Resolve the Sales Chanel Module's main service"], + ["19", "logger", "Resolve the logger to log messages in the terminal."], + ["22", "query", "Resolve Query to retrieve data later."], + ["26", "defaultSalesChannel", "Retrieve the default sales channel to associate products with."], + ["31", "sizeOptions", "Declare the size options to be used in the products' variants."], + ["32", "colorOptions", "Declare the color options to be used in the products' variants."], + ["33", "currency_code", "Declare the currency code to use in products' prices."], + ["34", "productsNum", "The number of products to seed."] +] + +```ts title="src/scripts/demo-products.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" +import { ExecArgs } from "@medusajs/framework/types" +import { faker } from "@faker-js/faker" +import { + ContainerRegistrationKeys, + Modules, + ProductStatus +} from "@medusajs/framework/utils"; +import { + createInventoryLevelsWorkflow, + createProductsWorkflow +} from "@medusajs/medusa/core-flows"; + +export default async function seedDummyProducts ({ + container +}: ExecArgs) { + const salesChannelModuleService = container.resolve( + Modules.SALES_CHANNEL + ); + const logger = container.resolve( + ContainerRegistrationKeys.LOGGER + ) + const query = container.resolve( + ContainerRegistrationKeys.QUERY + ) + + let defaultSalesChannel = await salesChannelModuleService + .listSalesChannels({ + name: "Default Sales Channel", + }); + + const sizeOptions = ["S", "M", "L", "XL"]; + const colorOptions = ["Black", "White"]; + const currency_code = "eur" + const productsNum = 50 + + // TODO seed products +} +``` + +So far, in the script, you: + +- Resolve the Sales Channel Module's main service to retrieve the application's default sales channel. This is the sales channel the dummy products will be available in. +- Resolve the Logger to log messages in the terminal, and Query to later retrieve data useful for the seeded products. +- Initialize some default data to use when seeding the products next. + +Next, replace the `TODO` with the following: + +```ts title="src/scripts/demo-products.ts" +const productsData = new Array(productsNum).fill(0).map((_, index) => { + const title = faker.commerce.product() + '_' + index + return { + title, + is_giftcard: true, + description: faker.commerce.productDescription(), + status: ProductStatus.PUBLISHED, + options: [ + { + title: "Size", + values: sizeOptions, + }, + { + title: "Color", + values: colorOptions, + }, + ], + images: [ + { + url: faker.image.urlPlaceholder({ + text: title + }), + }, + { + url: faker.image.urlPlaceholder({ + text: title + }), + }, + ], + variants: new Array(10).fill(0).map((_, variantIndex) => ({ + title: `${title} ${variantIndex}`, + sku: `variant-${variantIndex}${index}`, + prices: new Array(10).fill(0).map((_, priceIndex) => ({ + currency_code, + amount: 10 * priceIndex, + })), + options: { + Size: sizeOptions[Math.floor(Math.random() * 3)], + }, + })), + sales_channels: [ + { + id: defaultSalesChannel[0].id, + }, + ], + } +}) + +// TODO seed products +``` + +You generate fifty products using the sales channel and variables you initialized, and using Faker for random data, such as the product's title or images. + +Then, replace the new `TODO` with the following: + +```ts title="src/scripts/demo-products.ts" +const { result: products } = await createProductsWorkflow(container).run({ + input: { + products: productsData + }, +}) + +logger.info(`Seeded ${products.length} products.`) + +// TODO add inventory levels +``` + +You create the generated products using the `createProductsWorkflow` imported previously from `@medusajs/medusa/core-flows`. It accepts the product data as input, and returns the created products. + +Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following: + +```ts title="src/scripts/demo-products.ts" +logger.info("Seeding inventory levels."); + +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: ["id"] +}) + +const { data: inventoryItems } = await query.graph({ + entity: 'inventory_item', + fields: ['id'] +}) + +const inventoryLevels = inventoryItems.map((inventoryItem) => ({ + location_id: stockLocations[0].id, + stocked_quantity: 1000000, + inventory_item_id: inventoryItem.id, +})) + +await createInventoryLevelsWorkflow(container).run({ + input: { + inventory_levels: inventoryLevels + }, +}) + +logger.info("Finished seeding inventory levels data."); +``` + +You use Query to retrieve the stock location, to use the first location in the application, and the inventory items. + +Then, you generate inventory levels for each inventory item, associating it with the first stock location. + +Finally, you use the `createInventoryLevelsWorkflow` imported from `@medusajs/medusa/core-flows` to create the inventory levels. + +### Test Script + +To test out the script, run the following command in your project's directory: + +```bash +npx medusa exec ./src/scripts/demo-products.ts +``` + +This seeds the products to your database. If you run your Medusa application and view the products in the dashboard, you'll find fifty new products. diff --git a/www/apps/book/app/more-resources/examples/page.mdx b/www/apps/book/app/more-resources/examples/page.mdx index 3569534e84291..e7e36592a8286 100644 --- a/www/apps/book/app/more-resources/examples/page.mdx +++ b/www/apps/book/app/more-resources/examples/page.mdx @@ -65,6 +65,12 @@ This chapter provides links to example sections on different Medusa topics. --- +## Custom CLI Scripts + +- [Seed Dummy Products](../../advanced-development/custom-cli-scripts/seed-data/page.mdx) + +--- + ## Admin Customizations - [Send a request to custom API routes from widgets or UI routes](../../customization/customize-admin/widget/page.mdx) diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs index 589b0bb79a71e..6b5cd407f3fe1 100644 --- a/www/apps/book/generated/edit-dates.mjs +++ b/www/apps/book/generated/edit-dates.mjs @@ -110,8 +110,9 @@ export const generatedEditDates = { "app/customization/next-steps/page.mdx": "2024-09-12T10:50:04.873Z", "app/customization/page.mdx": "2024-09-12T11:16:18.504Z", "app/more-resources/cheatsheet/page.mdx": "2024-07-11T16:11:26.480Z", - "app/more-resources/examples/page.mdx": "2024-09-27T07:17:16.892Z", + "app/more-resources/examples/page.mdx": "2024-10-03T11:12:50.956Z", "app/architecture/architectural-modules/page.mdx": "2024-09-23T12:51:04.520Z", "app/architecture/overview/page.mdx": "2024-09-23T12:55:01.339Z", - "app/advanced-development/data-models/infer-type/page.mdx": "2024-09-30T08:43:53.123Z" + "app/advanced-development/data-models/infer-type/page.mdx": "2024-09-30T08:43:53.123Z", + "app/advanced-development/custom-cli-scripts/seed-data/page.mdx": "2024-10-03T11:11:07.181Z" } \ No newline at end of file diff --git a/www/apps/book/sidebar.mjs b/www/apps/book/sidebar.mjs index ba11e80aca245..b291608267679 100644 --- a/www/apps/book/sidebar.mjs +++ b/www/apps/book/sidebar.mjs @@ -477,6 +477,13 @@ export const sidebar = numberSidebarItems( type: "link", path: "/advanced-development/custom-cli-scripts", title: "Custom CLI Scripts", + children: [ + { + type: "link", + path: "/advanced-development/custom-cli-scripts/seed-data", + title: "Seed Data", + }, + ], }, { type: "link",