Merge branch 'master' into develop

This commit is contained in:
olivermrbl
2022-12-12 19:26:00 +01:00
63 changed files with 539 additions and 418 deletions
+4 -1
View File
@@ -1,8 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request
url: https://github.com/medusajs/medusa/discussions/categories/feature-requests
about: Use GitHub Discussions to open a feature request.
- name: Ask a question
url: https://discord.gg/medusajs
about: Join our community to ask questions and participate in discussions
- name: Want to work with us?
url: mailto:hello@medusajs.com?subject=[GitHub]%20Working%20with%20Medusa
about: Interested in working with Medusa? Get in touch!
about: Interested in working with Medusa? Get in touch!
-29
View File
@@ -1,29 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'type: feature or enhancement'
assignees: ''
---
<!--
Thank you for submitting an issue!
Please make sure your issue is understandable.
To make your issue readable make sure you use valid Markdown syntax: https://guides.github.com/features/mastering-markdown/
Please ensure you have also read and understood our contribution guide: https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+6 -6
View File
@@ -85,7 +85,7 @@ Where `<YOUR_APP_ID>` and `<YOUR_ADMIN_API_KEY>` are respectively the Applicatio
Finally, in `medusa-config.js` add the following item into the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -170,7 +170,7 @@ The Next.js storefront has the Algolia integration available out of the box. To
First, ensure that the search feature is enabled in `store.config.json`:
```json
```json title=store.config.json
{
"features": {
"search": true
@@ -190,7 +190,7 @@ Where `<YOUR_APP_ID>` and `<YOUR_SEARCH_API_KEY>` are respectively the Applicati
Finally, change the code in `src/lib/search-client.ts` to the following:
```jsx
```jsx title=src/lib/search-client.ts
import algoliasearch from "algoliasearch/lite"
const appId = process.env.NEXT_PUBLIC_SEARCH_APP_ID || "" // You should add this to your environment variables
@@ -247,7 +247,7 @@ In Gatsby, environment variables that should be public and available in the brow
Then, create the file `src/components/header/search.jsx` with the following content:
```jsx
```jsx title=src/components/header/search.jsx
import {
Highlight,
Hits,
@@ -310,13 +310,13 @@ If you named your environment variables differently based on your framework, mak
Finally, import this file at the beginning of `src/components/header/index.jsx`:
```jsx
```jsx title=src/components/header/index.jsx
import Search from "./search"
```
And add the `Search` component in the returned JSX before `RegionPopover`:
```jsx
```jsx title=src/components/header/index.jsx
//...
<Search />
<RegionPopover regions={mockData.regions} />
@@ -22,7 +22,7 @@ The Contentful migrations are located in the `contentful-migrations` directory i
Heres an example of a migration created in a new file `contentful-migrations/rich-text.js`:
```jsx
```jsx title=contentful-migrations/rich-text.js
#! /usr/bin/env node
require("dotenv").config();
@@ -152,7 +152,7 @@ After creating a new content model in your Contentful Space, you must add the ne
To render the Rich Text content you created in the previous example, create the file `src/components/rich-text/rich-text.js` with the following content:
```jsx
```jsx title=src/components/rich-text/rich-text.js
import React from "react"
import { renderRichText } from "gatsby-source-contentful/rich-text"
@@ -180,13 +180,13 @@ Since the Rich Text model can be added to any page, you must edit `src/pages/{Co
In `src/pages/{ContentfulPage.slug}.js`, import the `RichText` component at the top of the file:
```jsx
```jsx title=src/pages/{ContentfulPage.slug}.js
import RichText from "../components/rich-text/rich-text"
```
Then, in the returned JSX add a new case to the switch statement:
```jsx
```jsx title=src/pages/{ContentfulPage.slug}.js
switch (cm.internal.type) {
//...
case "ContentfulRichText":
@@ -200,7 +200,7 @@ If the content model of a tile is Rich Text, youll display it with the `RichT
Finally, to retrieve all necessary data of the Rich Text content, in the `query` GraphQL variable add the following after the `... on ContentfulTileSection` fragment:
```jsx
```jsx title=src/pages/{ContentfulPage.slug}.js
export const query = graphql`
# find the following line
... on ContentfulTileSection {
@@ -246,7 +246,7 @@ In the example migration, you also edited the product page to include a new Cont
To render them on the Product Page, add the following in the GraphQL query defined in the `query` variable inside `product`:
```jsx
```jsx title=src/pages/{ContentfulPage.slug}.js
export const query = graphql`
query ($id: String!) {
product: contentfulProduct(id: { eq: $id }) {
@@ -275,13 +275,13 @@ If you added other accepted Content Models to the `contentModules` field of the
Then, in `src/views/product.js` import the `RichText` component:
```jsx
```jsx title=src/views/product.js
import RichText from "../components/rich-text/rich-text"
```
And in the returned JSX add the following before the last `</div>`:
```jsx
```jsx title=src/views/product.js
<div className={styles.contentModules}>
{product.contentModules?.map((cm) => {
switch (cm.internal.type) {
+3 -3
View File
@@ -38,7 +38,7 @@ This installs a new Medusa server in the directory `medusa-contentful`.
Change to the `medusa-contentful` directory. In `.env` youll find three variables:
```bash
```bash title=.env
CONTENTFUL_SPACE_ID=
CONTENTFUL_ACCESS_TOKEN=
CONTENTFUL_ENV=
@@ -116,7 +116,7 @@ You can find the format of the PostgreSQL database URL in [PostgreSQLs docume
Then, in `medusa-config.js` in the exported object, comment out or remove the SQLite database configurations and add the PostgreSQL database configurations:
```jsx
```jsx title=medusa-config.js
module.exports = {
projectConfig: {
//...
@@ -239,7 +239,7 @@ mv .env.template .env
Then, open `.env`. You should find the following environment variables:
```bash
```bash title=.env
CONTENTFUL_SPACE_ID=
CONTENTFUL_ACCESS_TOKEN=
```
+75 -8
View File
@@ -1,13 +1,80 @@
---
hide_footer: true
---
# Klarna
:::note
In this document, youll learn how to integrate Klarna as a payment provider in Medusa.
This guide is coming soon.
## Introduction
:::
[Klarna](https://www.klarna.com/) is a payment provider that allows customers to pay in different ways including direct payment, installment payments, payment after delivery, and more.
[View plugin](https://github.com/medusajs/medusa/tree/master/packages/medusa-payment-klarna)
You can integrate Klarna into Medusa using the [official plugin](https://github.com/medusajs/medusa/tree/master/packages/medusa-payment-klarna).
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa server installed and set up. If not, you can follow the [quickstart guide](../quickstart/quick-start.md).
In addition, youll need to use the [Medusa Admin](../admin/quickstart.md) to enable the payment provider in later steps. You can alternatively use the [REST APIs](/api/admin/#tag/Region/operation/PostRegionsRegionPaymentProviders).
### Needed Accounts
- A [Klarna business account](https://portal.klarna.com/)
## Install Plugin
On your Medusa server, run the following command to install the plugin:
```bash
npm install medusa-payment-klarna
```
Then, add the following environment variables:
```bash
KLARNA_BACKEND_URL=<YOUR_KLARNA_BACKEND_URL>
KLARNA_URL=<YOUR_KLARNA_URL>
KLARNA_USER=<YOUR_KLARNA_USER>
KLARNA_PASSWORD=<YOUR_KLARNA_PASSWORD>
KLARNA_TERMS_URL=<YOUR_KLARNA_TERMS_URL>
KLARNA_CHECKOUT_URL=<YOUR_KLARNA_CHECKOUT_URL>
KLARNA_CONFIRMATION_URL=<YOUR_KLARNA_CONFIRMATION_URL>
```
Where:
- `<YOUR_KLARNA_BACKEND_URL>` is your Klarna URL.
- `<YOUR_KLARNA_URL>` is the [base Klarna URL based on your environment](https://docs.klarna.com/api/api-urls/).
- `<YOUR_KLARNA_USER>` and `<YOUR_KLARNA_PASSWORD>` are your [API credentials](https://docs.klarna.com/api/authentication/).
- `<YOUR_KLARNA_TERMS_URL>`, `<YOUR_KLARNA_CHECKOUT_URL>`, and `<YOUR_KLARNA_CONFIRMATION_URL>` are the terms, checkout, and confirmation URL of your Klarna account.
Finally, in `medusa-config.js`, add the Klarna plugin to the `plugins` array with the necessary configurations:
```jsx title=medusa-config.js
const plugins = [
//other plugins...
{
resolve: `medusa-payment-klarnal`,
options: {
backend_url: process.env.KLARNA_BACKEND_URL
url: process.env.KLARNA_URL,
user: process.env.KLARNA_USER,
password: process.env.KLARNA_PASSWORD,
merchant_urls: {
terms: process.env.KLARNA_TERMS_URL,
checkout: process.env.KLARNA_CHECKOUT_URL,
confirmation: process.env.KLARNA_CONFIRMATION_URL
}
}
}
];
```
## Enable Klarna in Regions
To use Klarna in your store, you must enable it in at least one region.
You can follow [this user guide to learn how to enable a payment provider in a region](../user-guide/regions/providers#manage-payment-providers). You can alternatively use the [REST APIs](/api/admin/#tag/Region/operation/PostRegionsRegionPaymentProviders).
## Whats Next
- Check out [more plugins](https://github.com/medusajs/medusa/tree/master/packages) you can add to your store.
+4 -4
View File
@@ -51,7 +51,7 @@ Make sure to replace `<YOUR_API_KEY>` with your API Key and `<YOUR_NEWSLETTER_LI
Open `medusa-config.js` and add the new plugin into the `plugins` array:
```js
```js title=medusa-config.js
const plugins = [
...,
{
@@ -72,7 +72,7 @@ This plugin adds a new `POST` endpoint at `/mailchimp/subscribe`. This endpoint
Try sending a `POST` request to `/mailchimp/subscribe` with the following JSON body:
```json noHeader
```json noReport
{
"email": "example@gmail.com"
}
@@ -90,7 +90,7 @@ If you check your Mailchimp dashboard, you should find the email added to your A
Heres an example of sending additional data with the subscription:
```json noHeader
```json noReport
{
"email": "example@gmail.com",
"data": {
@@ -107,7 +107,7 @@ If you want to subscribe to users without using this endpoint or at a specific p
Heres an example of using the `mailchimpService` inside an endpoint:
```jsx
```jsx title=src/api/index.ts
const mailchimpService = req.scope.resolve("mailchimpService")
mailchimpService.subscribeNewsletter(
+5 -5
View File
@@ -51,7 +51,7 @@ Where `<YOUR_MEILISEARCH_HOST>` is the host of your MeiliSearch instance. By def
Finally, in `medusa-config.js` add the following item into the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -144,7 +144,7 @@ The Next.js storefront has the MeiliSearch integration available out of the box.
First, ensure that the search feature is enabled in `store.config.json`:
```json
```json title=store.config.json
{
"features": {
"search": true
@@ -206,7 +206,7 @@ In Gatsby, environment variables that should be public and available in the brow
Then, create the file `src/components/header/search.jsx` with the following content:
```jsx
```jsx title=src/components/header/search.jsx
import {
Highlight,
Hits,
@@ -269,13 +269,13 @@ If you named your environment variables differently based on your framework, mak
Finally, import this file at the beginning of `src/components/header/index.jsx`:
```jsx
```jsx title=src/components/header/index.jsx
import Search from "./search"
```
And add the `Search` component in the returned JSX before `RegionPopover`:
```jsx
```jsx title=src/components/header/index.jsx
//...
<Search />
<RegionPopover regions={mockData.regions} />
+4 -4
View File
@@ -97,7 +97,7 @@ Where `<ENDPOINT>` is the URL of your MinIO server, `<BUCKET>` is the name of th
Finally, configure your `medusa-config.js` to include the plugin with the required options:
```bash
```js title=medusa-config.js
{
resolve: `medusa-file-minio`,
options: {
@@ -145,7 +145,7 @@ MINIO_PRIVATE_BUCKET=exports
Then, add a new option to the plugins options in `medusa-config.js`:
```jsx
```jsx title=medusa-config.js
{
resolve: `medusa-file-minio`,
options: {
@@ -170,7 +170,7 @@ Where `<YOUR_PRIVATE_ACCESS_KEY>` and `<YOUR_PRIVATE_SECRET_KEY>` are the access
Then, add two new options to the plugins options in `medusa-config.js`:
```jsx
```jsx title=medusa-config.js
{
resolve: `medusa-file-minio`,
options: {
@@ -189,7 +189,7 @@ If this configuration is not added, youll receive the error ["next/image Un-c
In `next.config.js` add the following option in the exported object:
```jsx
```jsx title=next.config.js
const { withStoreConfig } = require("./store-config")
//...
+5 -5
View File
@@ -51,7 +51,7 @@ Notice that during development its highly recommended to set `PAYPAL_SANDBOX`
Then, in `medusa-config.js`, add the PayPal plugin to the `plugins` array with the configurations necessary:
```jsx
```jsx title=medusa-config.js
const plugins = [
//other plugins...
{
@@ -119,7 +119,7 @@ Medusa has a Next.js storefront that you can easily use with your Medusa server.
In your `.env.local` file (or the file youre using for your environment variables), add the following variable:
```bash
```bash title=.env.local
NEXT_PUBLIC_PAYPAL_CLIENT_ID=<YOUR_CLIENT_ID>
```
@@ -137,7 +137,7 @@ Medusa also has a Gatsby storefront that you can use as your ecommerce storefron
In your `.env.development` file (or the file youre using for your environment variables) add the following variable with its value set to the Client ID:
```bash
```bash title=.env.development
GATSBY_PAYPAL_CLIENT_ID=<CLIENT_ID>
```
@@ -149,7 +149,7 @@ npm install @paypal/react-paypal-js
Next, create a new file `src/components/payment/paypal-payment/index.jsx` with the following content:
```jsx
```jsx title=src/components/payment/paypal-payment/index.jsx
import { PayPalButtons, PayPalScriptProvider } from "@paypal/react-paypal-js";
import React, { useMemo, useState } from "react";
@@ -253,7 +253,7 @@ The last step is to add this component as the component to render when PayPal is
In `src/components/payment/index.js` youll find in the return statement a switch statement that checks the payment provider for each payment session and renders the component based on the ID. Add before the `default` case a case for `paypal`:
```jsx
```jsx title=src/components/payment/index.js
switch (ps.provider_id) {
case "stripe":
//...
+2 -2
View File
@@ -108,7 +108,7 @@ Where:
Finally, in `medusa-config.js`, add to the `plugins` array the following new item:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -156,7 +156,7 @@ If this configuration is not added, youll receive the error ["next/image Un-
In `next.config.js` add the following option in the exported object:
```jsx
```jsx title=next.config.js
const { withStoreConfig } = require("./store-config")
//...
+2 -2
View File
@@ -102,7 +102,7 @@ Where `<YOUR_SEGMENT_WRITE_KEY>` is the Write Key shown on the page of the Segme
Finally, in `medusa-config.js`, add the following new item to the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -142,7 +142,7 @@ In some cases, you might want to track more events or custom events. You can do
For example, you can add the following subscriber to listen to the `customer.created` event and add tracking for every customer created:
```jsx
```jsx title=src/subscribers/customer.ts
class CustomerSubscriber {
constructor({ segmentService, eventBusService }) {
this.segmentService = segmentService;
+14 -14
View File
@@ -79,7 +79,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"beforeInsert": [Function],
"billing_address": null,
@@ -306,7 +306,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"beforeInsert": [Function],
"billing_address": null,
@@ -537,7 +537,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"date": Any<String>,
"email": "test@testson.com",
@@ -819,7 +819,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"date": Any<String>,
"email": "test@testson.com",
@@ -1261,7 +1261,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"date": Any<String>,
"email": "test@testson.com",
@@ -1703,7 +1703,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"claim": Object {
"canceled_at": null,
@@ -2000,7 +2000,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"locale": null,
"swap": Object {
@@ -2509,7 +2509,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
Object {
"additional_total": "16.88 USD",
"date": Any<String>,
@@ -3089,7 +3089,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
{
"locale": null,
"swap": Object {
@@ -3599,7 +3599,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
Object {
"code": Any<String>,
"value": 4,
@@ -3766,7 +3766,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
Object {
"id": Any<String>,
"email": "test@testson.com",
@@ -3786,7 +3786,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
Object {
"email": "test@testson.com",
"token": Any<String>,
@@ -3803,7 +3803,7 @@ You dont have to create a template for every type in the reference. You can s
<details>
<summary>Example Data</summary>
```json noHeader
```json noReport
Object {
"product": Object {
"collection_id": null,
@@ -3918,7 +3918,7 @@ Where `<ORDER_PLACED_TEMPLATE_ID` is the ID of your template for order placed em
Finally, in your `medusa-config.js` file, add the SendGrid plugin into the array of plugins:
```jsx
```jsx title=medusa-config.js
const plugins = [
...,
{
+1 -1
View File
@@ -75,7 +75,7 @@ npm install medusa-plugin-slack-notification
After that, open `medusa-config.js` and add the new plugin with its configurations in the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
...,
{
+2 -2
View File
@@ -95,7 +95,7 @@ Where:
Finally, in `medusa-config.js` add a new item to the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -143,7 +143,7 @@ If this configuration is not added, youll receive the error ["next/image Un-
In `next.config.js` add the following option in the exported object:
```jsx
```jsx title=next.config.js
const { withStoreConfig } = require("./store-config")
//...
+2 -2
View File
@@ -82,7 +82,7 @@ The `--seed` flag creates an SQLite database and seeds it with some demo data.
Once the command is done executing, change to the newly created `medusa-server` directory. Then, in `medusa-config.js`, change the exported object at the end of the file to enable Redis:
```jsx
```jsx title=medusa-config.js
module.exports = {
projectConfig: {
redis_url: REDIS_URL,
@@ -128,7 +128,7 @@ Where:
Finally, open `medusa-config.js` and add the following new item to the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
+3 -3
View File
@@ -42,7 +42,7 @@ Next, you need to add configurations for your stripe plugin.
In `medusa-config.js` add the following at the end of the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
...,
{
@@ -123,7 +123,7 @@ Medusa has a Next.js storefront that you can easily use with your Medusa server.
In your `.env.local` file (or the file youre using for your environment variables), add the following variable:
```bash
```bash title=.env.local
NEXT_PUBLIC_STRIPE_KEY=<YOUR_PUBLISHABLE_KEY>
```
@@ -139,7 +139,7 @@ Medusa also has a Gatsby storefront that you can use as your ecommerce store. If
In your `.env.development` file (or the file youre using for your environment variables) add the following variable with the value set to the Publishable Key:
```jsx
```jsx title=.env.development
GATSBY_STRIPE_KEY=pk_
```
+2 -2
View File
@@ -44,7 +44,7 @@ Make sure to replace `<YOUR_ACCOUNT_SID>`, `<YOUR_AUTH_TOKEN>`, and `<YOUR_TWILI
Finally, add the plugin and its options in the `medusa-config.js` file to the `plugins` array:
```jsx
```jsx title=medusa-config.js
const plugins = [
...,
{
@@ -72,7 +72,7 @@ For this example to work, youll need to install and configure Redis on your s
Create the file `src/services/sms.js` in your Medusa server with the following content:
```jsx
```jsx title=src/services/sms.js
class SmsSubscriber {
constructor({ twilioSmsService, orderService, eventBusService }) {
this.twilioSmsService_ = twilioSmsService;
+2 -2
View File
@@ -43,7 +43,7 @@ Before running your Medusa admin, make sure that your Medusa server is running.
To run your Medusa server, go to the directory holding the server and run:
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm run start
```
@@ -65,7 +65,7 @@ Use your Medusa admins user credentials to log in.
If you installed the demo data when you installed the Medusa server by using the `--seed` option or running:
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm run seed
```
+14 -14
View File
@@ -1,9 +1,9 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Import Prices
# How to Bulk Import Prices
In this document, youll learn how to import prices into a price list using the Admin APIs.
In this document, youll learn how to bulk import prices into a price list using the Admin APIs.
## Overview
@@ -95,10 +95,10 @@ fetch(`<YOUR_SERVER>/admin/uploads`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/uploads' \
--header 'Authorization: Bearer {api_token}' \
--header 'Content-Type: text/csv' \
--form 'files=@"<FILE_PATH_1>"'
curl -L -X POST '<YOUR_SERVER>/admin/uploads' \
-H 'Authorization: Bearer {api_token}' \
-H 'Content-Type: text/csv' \
-F 'files=@"<FILE_PATH_1>"'
```
</TabItem>
@@ -158,9 +158,9 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/batch-jobs' \
--header 'Authorization: Bearer {api_token}' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/batch-jobs' \
-H 'Authorization: Bearer {api_token}' \
-H 'Content-Type: application/json' \
--data-raw '{
"type": "price-list-import",
"context": {
@@ -225,8 +225,8 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs/${batchJobId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
--header 'Authorization: Bearer {api_token}'
curl -L -X GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
-H 'Authorization: Bearer {api_token}'
# <BATCH_JOB_ID> is the ID of the batch job
```
@@ -239,7 +239,7 @@ If the batch job has been pre-processed, the status of the batch job will be `pr
Heres an example of the `result` property:
```json noHeader
```json noReport
"result": {
"count": 5, // Total number of prices to be added
"stat_descriptors": [ //details about the prices to be added
@@ -287,8 +287,8 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs/${batchJobId}/confirm`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
--header 'Authorization: Bearer {api_token}'
curl -L -X POST '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
-H 'Authorization: Bearer {api_token}'
# <BATCH_JOB_ID> is the ID of the batch job
```
+14 -14
View File
@@ -1,9 +1,9 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Import Products
# How to Bulk Import Products
In this document, youll learn how to use the Admin APIs to import products into a Medusa server.
In this document, youll learn how to use the Admin APIs to bulk import products into a Medusa server.
## Overview
@@ -89,10 +89,10 @@ fetch(`<YOUR_SERVER>/admin/uploads`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/uploads' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: text/csv' \
--form 'files=@"<FILE_PATH_1>"'
curl -L -X POST '<YOUR_SERVER>/admin/uploads' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: text/csv' \
-F 'files=@"<FILE_PATH_1>"'
```
</TabItem>
@@ -150,9 +150,9 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/batch-jobs' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/batch-jobs' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"type": "product-import",
"context": {
@@ -213,8 +213,8 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs/${batchJobId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
# <BATCH_JOB_ID> is the ID of the batch job
```
@@ -227,7 +227,7 @@ If the batch job has been pre-processed, the status of the batch job will be `pr
Heres an example of the `result` property:
```json noHeader
```json noReport
"result": {
"count": 5, // Total number of products to be created or updated
"stat_descriptors": [ //details about the products to be created/updated
@@ -275,8 +275,8 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs/${batchJobId}/confirm`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X POST '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
-H 'Authorization: Bearer <API_TOKEN>'
# <BATCH_JOB_ID> is the ID of the batch job
```
@@ -105,9 +105,9 @@ fetch(`<YOUR_SERVER>/admin/discounts`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/discounts' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/discounts' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"code": "<CODE>",
"rule": {
@@ -179,9 +179,9 @@ fetch(`<YOUR_SERVER>/admin/discounts/${discountId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"description": "New description",
"is_disabled": true
@@ -253,9 +253,9 @@ fetch(`<YOUR_SERVER>/admin/discounts/${discountId}/conditions`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"operator": "in",
"products": [
@@ -317,8 +317,8 @@ fetch(`<YOUR_SERVER>/admin/discounts/${discountId}/conditions/${conditionId}&exp
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions/<CONDITION_ID>&expand=products' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions/<CONDITION_ID>&expand=products' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -378,9 +378,9 @@ fetch(`<YOUR_SERVER>/admin/discounts/${discountId}/conditions/${conditionId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions/<CONDITION_ID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions/<CONDITION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"products": [
"<PRODUCT_ID_1>",
@@ -430,8 +430,8 @@ fetch(`<YOUR_SERVER>/admin/discounts/${discountId}/conditions/${conditionId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request DELETE '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions/<CONDITION_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>/conditions/<CONDITION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -473,8 +473,8 @@ fetch(`<YOUR_SERVER>/admin/discounts/${discountId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request DELETE '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<YOUR_SERVER>/admin/discounts/<DISCOUNT_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -75,9 +75,9 @@ fetch(`<SERVER_URL>/admin/customer-groups`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/customer-groups' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/customer-groups' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"name": "VIP"
}'
@@ -121,8 +121,8 @@ fetch(`<SERVER_URL>/admin/customer-groups`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<SERVER_URL>/admin/customer-groups' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/customer-groups' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -165,8 +165,8 @@ fetch(`<SERVER_URL>/admin/customer-groups/${customerGroupId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -220,9 +220,9 @@ fetch(`<SERVER_URL>/admin/customer-groups/${customerGroupId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"metadata": {
"is_seller": true
@@ -269,8 +269,8 @@ fetch(`<SERVER_URL>/admin/customer-groups/${customerGroupId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request DELETE '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -330,9 +330,9 @@ fetch(`<SERVER_URL>/admin/customer-groups/${customerGroupId}/customers/batch`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>/customers/batch' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>/customers/batch' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"customer_ids": [
{
@@ -378,8 +378,8 @@ fetch(`<SERVER_URL>/admin/customer-groups/${customerGroupId}/customers`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>/customers' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>/customers' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -441,9 +441,9 @@ fetch(`<SERVER_URL>/admin/customer-groups/${customerGroupId}/customers/batch`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request DELETE '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>/customers/batch' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X DELETE '<SERVER_URL>/admin/customer-groups/<CUSTOMER_GROUP_ID>/customers/batch' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"customer_ids": [
{
@@ -43,7 +43,7 @@ Batch job strategies must extend the abstract class `AbstractBatchJobStrategy` a
Add the following content to the file you created:
```tsx
```tsx title=src/strategies/publish.ts
import { AbstractBatchJobStrategy, BatchJobService } from '@medusajs/medusa'
import { EntityManager } from 'typeorm'
@@ -294,9 +294,9 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/batch-jobs' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER>/admin/batch-jobs' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"type": "publish-products",
"context": { },
@@ -342,8 +342,8 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs/${batchJobId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
# <BATCH_JOB_ID> is the ID of the batch job
```
@@ -352,7 +352,7 @@ curl --location --request GET '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>' \
Based on the batch job strategy implemented in this documentation, the `result` property could be something like this:
```json noHeader
```json noReport
"result": {
"count": 1,
"stat_descriptors": [
@@ -398,8 +398,8 @@ fetch(`<YOUR_SERVER>/admin/batch-jobs/${batchJobId}/confirm`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X POST '<YOUR_SERVER>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
-H 'Authorization: Bearer <API_TOKEN>'
# <BATCH_JOB_ID> is the ID of the batch job
```
@@ -40,7 +40,7 @@ The batch job strategy class must extend the `AbstractBatchJobStrategy` class wh
For example, you can define the following class in the file you created:
```typescript
```typescript title=src/strategies/import.ts
import { AbstractBatchJobStrategy, BatchJobService } from '@medusajs/medusa'
import { EntityManager } from 'typeorm'
@@ -30,7 +30,7 @@ For the example in this tutorial, you can create the file `src/loaders/publish.t
To create a cron job, add the following code in the file you created, which is `src/loaders/publish.ts` in this example:
```ts
```ts title=src/loaders/publish.ts
const publishJob = async (container, options) => {
const eventBus = container.resolve("eventBusService");
eventBus.createCronJob("publish-products", {}, "0 0 * * *", async () => {
@@ -111,7 +111,7 @@ Once it is time to run your cron job based on the cron job expression pattern, t
For example, the above cron job will run at 12 AM and, when it runs, you can see the following logged on your Medusa server:
```bash noHeader
```bash noReport
info: Processing cron job: publish-products
```
@@ -24,7 +24,7 @@ A customer group is stored in the database as a [CustomerGroup](../../../refere
Similar to all entities in Medusa, you can use the `metadata` object attribute to store any custom data you want. For example, you can add some flag or tag to the customer group for a custom use case:
```jsx noHeader
```jsx noReport
metadata: {
is_seller: true
}
@@ -10,7 +10,7 @@ Custom endpoints reside under the `src/api` directory in your Medusa Backend. T
To create a new endpoint, start by creating a new file in `src/api` called `index.ts`. At its basic format, `index.ts` should look something like this:
```ts
```ts title=src/api/index.ts
import { Router } from "express"
export default (rootDirectory, pluginOptions) => {
@@ -86,7 +86,7 @@ router.get("/admin/hello", cors(corsOptions), (req, res) => {
You can add more than one endpoint in `src/api/index.ts`:
```ts
```ts title=src/api/index.ts
router.options("/store/hello", cors(storeCorsOptions))
router.get("/store/hello", cors(storeCorsOptions), (req, res) => {
res.json({
@@ -108,7 +108,7 @@ Alternatively, you can add multiple files for each endpoint or set of endpoints
To do that with the previous example, first, create the file `src/api/store.ts` with the following content:
```ts
```ts title=src/api/store.ts
import cors from "cors"
import { projectConfig } from "../../medusa-config"
@@ -130,7 +130,7 @@ You export a function that receives an Express router as a parameter and adds th
Next, create the file `src/api/admin.ts` with the following content:
```ts
```ts title=src/api/admin.ts
import cors from "cors"
import { projectConfig } from "../../medusa-config"
@@ -152,14 +152,15 @@ Again, you export a function that receives an Express router as a parameter and
Finally, in `src/api/index.ts` import the two functions at the beginning of the file:
```ts
```ts title=src/api/index.ts
import { Router } from "express"
import storeRoutes from "./store"
import adminRoutes from "./admin"
```
and in the exported function, call each of the functions passing them the Express router:
```ts
```ts title=src/api/index.ts
export default () => {
const router = Router()
@@ -6,7 +6,7 @@ In this document, youll learn how you can create an [Entity](overview.md).
To create an entity, create a TypeScript file in `src/models`. For example, heres a `Post` entity defined in the file `src/models/post.ts`:
```tsx
```tsx title=src/models/post.ts
import { BeforeInsert, Column, Entity, PrimaryColumn } from "typeorm";
import { BaseEntity} from "@medusajs/medusa";
import { generateEntityId } from "@medusajs/medusa/dist/utils"
@@ -50,9 +50,9 @@ You can learn more about Migrations, how to create them, and how to run them in
### Create a Repository
Entities data can be easily accessed and modified using Typeorm [Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, heres a repository `PostRepository` that resides in `src/repositories/post.ts`:
Entities data can be easily accessed and modified using Typeorm [Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, heres a repository `PostRepository` created in `src/repositories/post.ts`:
```tsx
```tsx title=src/repositories/post.ts
import { EntityRepository, Repository } from "typeorm"
import { Post } from "../models/post"
@@ -75,7 +75,7 @@ Be careful with your file names as it can cause unclear errors in Typeorm. Make
Before trying this step make sure that youve created and run your migrations. You also need to re-build your code using:
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm run build
```
@@ -44,7 +44,7 @@ You can enable a feature by using the server settings in `medusa-config.js`. You
For example, to enable the Tax-Inclusive Pricing beta feature, add the following to the exported object in `medusa-config.js`:
```jsx
```jsx title=medusa-config.js
module.exports = {
featureFlags: {
tax_inclusive_pricing: true
@@ -20,7 +20,7 @@ Creating a Notification Provider is as simple as creating a TypeScript or JavaS
For example, create the file `src/services/email-sender.ts` with the following content:
```ts
```ts title=src/services/email-sender.ts
import { AbstractNotificationService } from "@medusajs/medusa";
import { EntityManager } from "typeorm";
@@ -89,7 +89,6 @@ class EmailSenderService extends AbstractNotificationService {
static identifier = "email-sender";
protected orderService: OrderService;
// highlight-start
constructor(container, options) {
super(container);
//you can access options here in case you're
@@ -97,7 +96,6 @@ class EmailSenderService extends AbstractNotificationService {
this.orderService = container.orderService;
}
// highlight-end
//...
}
@@ -219,7 +217,7 @@ This section will not cover the basics of Subscribers. You can read the [Subscri
Following the previous example, to make sure the `email-sender` Notification Provider handles the `order.placed` event, create the file `src/subscribers/notification.js` with the following content:
```ts
```ts title=src/subscribers/notification.js
class NotificationSubscriber {
constructor({ notificationService }) {
notificationService.subscribe('order.placed', 'email-sender');
@@ -48,7 +48,7 @@ The first step to create a payment provider is to create a JavaScript or TypeScr
For example, create the file `src/services/my-payment.ts` with the following content:
```ts
```ts title=src/services/my-payment.ts
import { AbstractPaymentService, Cart, Data, Payment, PaymentSession, PaymentSessionStatus, TransactionBaseService } from "@medusajs/medusa"
import { EntityManager } from "typeorm";
+11 -11
View File
@@ -38,7 +38,7 @@ Update the `name` field in the `package.json` file to the name of your plugin. T
A basic Medusa server installed with the `medusa new` command has dependencies similar to this:
```json
```json title=package.json
"dependencies": {
"@medusajs/medusa": "^1.3.1",
"@medusajs/medusa-cli": "^1.3.0",
@@ -60,7 +60,7 @@ For a plugin, a lot of these dependencies are not necessary or should be labeled
The recommended change is the following:
```json
```json title=package.json
"peerDependencies": {
"@medusajs/medusa": "^1.3.1",
"medusa-interfaces": "^1.3.0",
@@ -98,7 +98,7 @@ If you don't make changes to the `build` and `watch` commands, please be aware o
A basic Medusa installation comes with the following scripts:
```json
```json title=package.json
"scripts": {
"seed": "medusa seed -f ./data/seed.json",
"build": "babel src --out-dir . --ignore **/__tests__ --extensions \".ts,.js\"",
@@ -110,7 +110,7 @@ The `seed` and `start` scripts aren't necessary for plugin development so you ca
Its also recommended to add the `watch` script that automatically compiles your files if they are changed:
```json
```json title=package.json
"watch": "babel -w src --out-dir . --ignore **/__tests__ --extensions \".ts,.js\""
```
@@ -124,7 +124,7 @@ Testing the plugin is covered in a [later section](#test-your-plugin).
Another recommended script is the `prepare` script that builds your files under a “production” environment:
```json
```json title=package.json
"prepare": "cross-env NODE_ENV=production npm run build"
```
@@ -161,7 +161,7 @@ If files and directories aren't placed in the root of your plugin, the Medusa se
An example of a plugin's directory before testing or publishing:
```bash noHeader
```bash noReport
medusa-plugin-custom
|
|_ _ _ api
@@ -202,7 +202,7 @@ Plugins often allow developers that will later use them to enter their own confi
To pass a plugin its configurations on a Medusa server, you have to add it to the `plugins` array in `medusa-config.js`:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -216,7 +216,7 @@ const plugins = [
Then, you can have access to your plugin configuration in the constructor of services in your plugin:
```jsx
```jsx title=src/service/test.ts
//In a service in your plugin
constructor({}, options) {
//options contains plugin configurations
@@ -226,7 +226,7 @@ constructor({}, options) {
You can also have access to the configurations in endpoints in your plugin:
```jsx
```jsx title=src/api/index.ts
//in an endpoint in your plugin
export default (rootDirectory, options) => {
//options contain the plugin configurations
@@ -281,7 +281,7 @@ If youre running the `watch` command, you dont need to run the `build` com
Then, add your plugin into the array of plugins in `medusa-config.js`:
```jsx
```jsx title=medusa-config.js
const plugins = [
//...
{
@@ -364,7 +364,7 @@ So, you can ignore files and directories like `src` from the final published NPM
To do that, create the file `.npmignore` with the following content:
```bash
```bash title=.npmignore
/lib
node_modules
.DS_store
@@ -37,7 +37,7 @@ When you create a price list, you can specify different conditions to control wh
In the body of your request, aside from the required fields, you can send the following fields to apply different conditions:
```js noHeader
```js noReport
{
prices: [
{
@@ -135,9 +135,9 @@ fetch(`<SERVER_URL>/admin/price-lists`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<YOUR_SERVER_URL>/admin/price-lists' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<YOUR_SERVER_URL>/admin/price-lists' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"name": "New Price List",
"description": "A new price list",
@@ -200,8 +200,8 @@ fetch(`<SERVER_URL>/admin/price-lists/${priceListId}`, {
<TabItem value="curl" label="cURL">
```jsx
curl --location --request GET '<SERVER_URL>/admin/price-lists/{id}' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/price-lists/{id}' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -251,9 +251,9 @@ fetch(`<SERVER_URL>/admin/price-lists/${priceListId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"ends_at": "2022-10-11"
}'
@@ -326,9 +326,9 @@ fetch(`<SERVER_URL>/admin/price-lists/${priceListId}/prices/batch`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"prices": [
{
@@ -379,8 +379,8 @@ fetch(`<SERVER_URL>/admin/price-lists/${priceListId}/products/${productId}/price
<TabItem value="curl" label="cURL">
```bash
curl --location --request DELETE '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>/products/<PRODUCT_ID>/prices' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>/products/<PRODUCT_ID>/prices' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -420,8 +420,8 @@ fetch(`<SERVER_URL>/admin/price-lists/${priceListId}/variants/${variantId}/price
<TabItem value="curl" label="cURL">
```jsx
curl --location --request DELETE '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>/variants/<VARIANT_ID>/prices' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>/variants/<VARIANT_ID>/prices' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -463,8 +463,8 @@ fetch(`<SERVER_URL>/admin/price-lists/${priceListId}`, {
<TabItem value="curl" label="cURL">
```jsx
curl --location --request DELETE '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<SERVER_URL>/admin/price-lists/<PRICE_LIST_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -12,7 +12,7 @@ If youre interested in learning what a price selection strategy is and how it
Create a TypeScript or JavaScript file in `src/strategies` of your Medusa server project with a class that extends the `AbstractPriceSelectionStrategy` class:
```typescript
```typescript title=src/strategies/price.ts
import { AbstractPriceSelectionStrategy, IPriceSelectionStrategy, PriceSelectionContext, PriceSelectionResult } from "@medusajs/medusa";
import { EntityManager } from "typeorm";
@@ -80,7 +80,7 @@ This method accepts the variant ID as a first parameter and the [context](./inde
This method must return an object having the following fields:
```typescript noHeader
```typescript noReport
{
originalPrice, //number | null
calculatedPrice, //number | null
@@ -90,9 +90,9 @@ fetch(`<SERVER_URL>/admin/sales-channels`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/sales-channels' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/sales-channels' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
name: 'App',
description: 'Mobile app'
@@ -139,8 +139,8 @@ fetch(`<SERVER_URL>/admin/sales-channels`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<SERVER_URL>/admin/sales-channels' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/sales-channels' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -181,8 +181,8 @@ fetch(`<SERVER_URL>/admin/sales-channels/${salesChannelId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -232,9 +232,9 @@ fetch(`<SERVER_URL>/admin/sales-channels/${salesChannelId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"is_disabled": false
}'
@@ -283,8 +283,8 @@ fetch(`<SERVER_URL>/admin/sales-channels/${salesChannelId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request DELETE '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X DELETE '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -344,9 +344,9 @@ fetch(`<SERVER_URL>/admin/sales-channels/${salesChannelId}/products/batch`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request POST '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>/products/batch' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X POST '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>/products/batch' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"product_ids": [
{
@@ -398,8 +398,8 @@ fetch(`<SERVER_URL>/admin/products?sales_channel_id[0]=${salesChannelId}`, {
<TabItem value="curl" label="cURL">
```bash
curl --location --request GET '<SERVER_URL>/admin/products?sales_channel_id[0]=<SALES_CHANNEL_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/products?sales_channel_id[0]=<SALES_CHANNEL_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -461,9 +461,9 @@ fetch(`<SERVER_URL>/admin/sales-channels/${salesChannelId}/products/batch`, {
<TabItem value="curl" label="cURL">
```jsx
curl --location --request DELETE '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>/products/batch' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
curl -L -X DELETE '<SERVER_URL>/admin/sales-channels/<SALES_CHANNEL_ID>/products/batch' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"product_ids": [
{
@@ -519,8 +519,8 @@ fetch(`<SERVER_URL>/admin/orders?sales_channel_id[0]=${salesChannelId}`, {
<TabItem value="curl" label="cURL">
```jsx
curl --location --request GET '<SERVER_URL>/admin/orders?sales_channel_id[0]=<SALES_CHANNEL_ID>' \
--header 'Authorization: Bearer <API_TOKEN>'
curl -L -X GET '<SERVER_URL>/admin/orders?sales_channel_id[0]=<SALES_CHANNEL_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
@@ -8,7 +8,7 @@ To create a service, create a TypeScript or JavaScript file in `src/services` to
For example, if you want to create a service `helloService`, create the file `hello.ts` in `src/services` with the following content:
```ts
```ts title=/src/services/hello.ts
import { TransactionBaseService } from '@medusajs/medusa';
import { EntityManager } from 'typeorm';
@@ -54,7 +54,7 @@ In this section, you'll learn how to use services throughout your Medusa server.
Before using your service, make sure you run the `build` command:
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm run build
```
@@ -23,7 +23,7 @@ Fulfillment providers are loaded and installed on the server startup.
The first step is to create a JavaScript or TypeScript file under `src/services`. For example, create the file `src/services/my-fulfillment.ts` with the following content:
```ts
```ts title=src/services/my-fulfillment.ts
import { FulfillmentService } from "medusa-interfaces"
class MyFulfillmentService extends FulfillmentService {
@@ -16,9 +16,9 @@ After creating the file under `src/subscribers`, in the constructor of your subs
The `eventBusService.subscribe` method receives the name of the event as a first parameter and as a second parameter a method in your subscriber that will handle this event.
For example, here is the `OrderNotifierSubscriber` class which is created in `src/subscribers/orderNotifier.js`:
For example, here is the `OrderNotifierSubscriber` class created in `src/subscribers/orderNotifier.js`:
```ts
```ts title=src/subscribers/orderNotifier.js
class OrderNotifierSubscriber {
constructor({ eventBusService }) {
eventBusService.subscribe("order.placed", this.handleOrder);
@@ -58,7 +58,7 @@ The `ShippingMethod` entity also has the `includes_tax` attribute. Its value is
When a price is tax-inclusive, the tax amount is calculated using the following formula:
```jsx noHeader
```jsx noReport
const taxAmount = (taxRate * taxInclusivePrice) / (1 + taxRate)
```
@@ -110,7 +110,7 @@ Price lists include a list of prices that can be used to override the original p
Each variants price in the price list is compared to the variants original price using the following condition:
```jsx noHeader
```jsx noReport
amount < (1 + taxRate) * calculatedPrice
```
@@ -120,7 +120,7 @@ Where `amount` is the amount of the variants price in the price list, `taxRat
Here is an example of these fields when tax inclusivity is enabled for both the currency and the price list:
```jsx noHeader
```jsx noReport
{
original_price: 110,
calculated_price: 100,
+61 -17
View File
@@ -93,32 +93,76 @@ If the admonition does not match any of the mentioned criteria, always default t
If you are adding images to a documentation page, you can host the image on [Imgur](https://imgur.com) for free.
## Code Block Types
## Code Blocks
In the Medusa documentation, there are two code block types: code blocks with headers and code blocks without headers.
### Use Tabs with Code Blocks
Code blocks without headers should be used when:
To use Tabs with Code Blocks, you have to use [Docusaurus's `Tabs` and `TabItem` components](https://docusaurus.io/docs/markdown-features/code-blocks#multi-language-support-code-blocks).
- The code block is used inside an Admonition.
- The content of the code block can't be reported (for example, if the code block contains only a text of the expected output).
You must also pass to the `Tabs` component the prop `wrapperClassName="code-tabs"` to ensure correct styling.
In all other cases, code blocks with headers should be used.
### Code Blocks with Headers
By default, all code blocks have headers and no additional actions are required to add the header.
### Code Blocks without Headers
To add a code block without a header, simply add `noHeader` after the beginning backticks of the code block. For example:
For example:
~~~md
```bash noHeader
this code block does not have a header
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.admin.uploads.create(file) //file is an instance of File
.then(({ uploads }) => {
const key = uploads[0].key;
});
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<YOUR_SERVER>/admin/uploads' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: text/csv' \
-F 'files=@"<FILE_PATH_1>"'
```
</TabItem>
</Tabs>
~~~
### Add Title to Code Block with Tabs
If you want to add a title to a code block with tabs, add the `codeTitle` prop to the `Tabs` component.
For example:
```md
<Tabs groupId="request-type" wrapperClassName="code-tabs" codeTitle="/src/services/hello.ts">
```
### Add Title to Code Block without Tabs
To add a title to a code block without tabs:
~~~md
```js title=src/index.ts
console.log("hello")
```
~~~
`noHeader` should be added after the language of the code block (which is `bash` in the above example). If you used `npm2yarn` as well, `noHeader` should be after it.
### Remove Report Buttons
Some code block don't need a report button. To remove the report button, use the `noReport` metadata.
For example:
~~~md
```bash noReport
medusa new my-medusa-store --seed
```
~~~
## NPM and Yarn Code Blocks
@@ -180,7 +180,7 @@ Youll have to follow five steps for the initialization:
Youll be asked to either connect to an existing Netlify website or create a new one. Choose the second option to create a new site:
```bash noHeader
```bash noReport
? What would you like to do?
⇄ Connect this directory to an existing Netlify site
+ Create & configure a new site
@@ -198,7 +198,7 @@ Youll be asked to optionally enter a site name.
At this point, the website is created on Netlify. However, Netlify needs to configure Webhooks and deployment keys. Youll be asked to either authorize GitHub through Netlifys website or through a personal access token. Youre free to choose either:
```bash noHeader
```bash noReport
? Netlify CLI needs access to your GitHub account to configure Webhooks and Depl
oy Keys. What would you like to do? (Use arrow keys)
Authorize with GitHub through app.netlify.com
@@ -138,7 +138,7 @@ If you havent added any products to your Medusa server, the build process mig
Alternatively, you can seed the server with demo data by running this command in the root directory of the server:
```bash noHeader
```bash noReport
medusa seed -f data/seed.json
```
@@ -198,7 +198,7 @@ Youll have to follow five steps for the initialization:
Youll be asked to either connect to an existing Netlify website or create a new one. Choose the second option to create a new site:
```bash noHeader
```bash noReport
? What would you like to do?
⇄ Connect this directory to an existing Netlify site
+ Create & configure a new site
@@ -216,7 +216,7 @@ Youll be asked to optionally enter a site name.
At this point, the website is created on Netlify. However, Netlify needs to configure Webhooks and deployment keys. Youll be asked to either authorize GitHub through Netlifys website or through a personal access token. Youre free to choose either:
```bash noHeader
```bash noReport
? Netlify CLI needs access to your GitHub account to configure Webhooks and Depl
oy Keys. What would you like to do? (Use arrow keys)
Authorize with GitHub through app.netlify.com
@@ -271,7 +271,7 @@ If you havent added any products to your Medusa server, the build process mig
Alternatively, you can seed the server with demo data by running this command in the root directory of the server:
```bash noHeader
```bash noReport
medusa seed -f data/seed.json
```
@@ -7,7 +7,7 @@ In this document, you will learn how to make a container of Medusa's app on Dock
### Node.js
Medusa supports Node versions 14 and 16. You can check which version of Node you have by running the following command:
```bash noHeader
```bash noReport
node -v
```
@@ -21,18 +21,18 @@ It is assumed that you have Docker installed on your system. You can install it
### 1. Clone Medusa's starter project from GitHub
```bash noHeader
```bash noReport
git clone https://github.com/medusajs/medusa-starter-default.git my-medusa-starter
```
### 2. Change to the newly created project directory
```bash noHeader
```bash noReport
cd my-medusa-starter
```
### 3. Rename the environment variables(.env) file
```bash noHeader
```bash noReport
mv .env.template .env
```
@@ -40,7 +40,7 @@ mv .env.template .env
Make sure the Docker Desktop app is running. Then, run the following command:
```bash noHeader
```bash noReport
docker-compose up --build
```
@@ -48,7 +48,7 @@ docker-compose up --build
If you get the error `ERROR: for postgres Cannot start service postgres: Ports are not available`, change the ports used for PostgreSQL in `docker-compose.yml` to something like this:
```yml noHeader
```yml noReport
postgres:
ports:
- "5433:5433"
@@ -68,7 +68,7 @@ Once done, your server will be accessible at `http://localhost:9000`.
You can test out your server using tools like Postman or by sending a cURL request:
```bash noHeader
```bash noReport
curl -X GET localhost:9000/store/products | python -m json.tool
```
@@ -76,7 +76,7 @@ curl -X GET localhost:9000/store/products | python -m json.tool
This command uses Python to format the result of the request better in your command line. If you don't want to use Python you can simply send a request without the formatting:
```bash noHeader
```bash noReport
curl localhost:9000/store/products
```
+6 -6
View File
@@ -6,7 +6,7 @@ This document will guide you through setting up your Medusa server in a few minu
Medusa supports Node versions 14 and 16. You can check which version of Node you have by running the following command:
```bash noHeader
```bash noReport
node -v
```
@@ -16,7 +16,7 @@ You can install Node from the [official website](https://nodejs.org/en/).
### 1. Install Medusa CLI
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm install @medusajs/medusa-cli -g
```
@@ -28,13 +28,13 @@ If you run into any errors while installing the CLI tool, check out the [trouble
### 2. Create a new Medusa project
```bash noHeader
```bash noReport
medusa new my-medusa-store --seed
```
### 3. Start your Medusa server
```bash noHeader
```bash noReport
cd my-medusa-store
medusa develop
```
@@ -43,7 +43,7 @@ If you run into any errors while installing the CLI tool, check out the [trouble
After these three steps and in only a couple of minutes, you now have a complete commerce engine running locally. You can test it out by sending a request using a tool like Postman or through the command line:
```bash noHeader
```bash noReport
curl localhost:9000/store/products | python -m json.tool
```
@@ -51,7 +51,7 @@ curl localhost:9000/store/products | python -m json.tool
This command uses Python to format the result of the request better in your command line. If you don't want to use Python you can simply send a request without the formatting:
```bash noHeader
```bash noReport
curl localhost:9000/store/products
```
@@ -19,7 +19,7 @@ You can check out more information in [NPMs documentation](https://docs.npmjs
If you install the Medusa CLI tool with Yarn, then try to use the CLI tool but get the error:
```bash noHeader
```bash noReport
command not found: medusa
```
+1 -1
View File
@@ -10,7 +10,7 @@ In your `medusa-config.js` , you should ensure that you've configured your CORS
The default configuration uses the following CORS settings:
```jsx
```jsx title=medusa-config.js
// CORS when consuming Medusa from admin
const ADMIN_CORS = process.env.ADMIN_CORS || "http://localhost:7000,http://localhost:7001"
@@ -12,7 +12,7 @@ For that reason, when the `start` and `build` scripts in `www/docs` are used, th
If you receive the following error when you run the `build` command in `www/docs`:
```bash noHeader
```bash noReport
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
```
@@ -2,7 +2,7 @@
You add payment providers to your Medusa instance by adding them as plugins in `medusa-config.js`:
```jsx
```jsx title=medusa-config.js
const plugins = [
...
// You can create a Stripe account via: https://stripe.com
+1 -1
View File
@@ -8,7 +8,7 @@ You can learn how to [install Redis in the Set Up your Development Environment d
After installing it, make sure to configure your Medusa server to use Redis:
```jsx
```jsx title=medusa-config.js
module.exports = {
projectConfig: {
//...
+1 -1
View File
@@ -2,7 +2,7 @@
If you're using the [S3 Plugin](../add-plugins/s3.md) and, when you upload an image, you receive the following error on your Medusa server:
```bash noHeader
```bash noReport
AccessControlListNotSupported: The bucket does not allow ACLs
```
@@ -2,7 +2,7 @@
If you've created a new Medusa server with the `--seed` option or used the `seed` script or command, the default credentials are:
```bash noHeader
```bash noReport
email: admin@medusa-test.com
password: supersecret
```
@@ -6,7 +6,7 @@ However, this comes at the expense of important features that are needed in a pr
Therefore, you might experience the following error when going through a checkout flow in one of Medusa's starters while using SQLite:
```bash noHeader
```bash noReport
Error: Transaction already started for the given connection, commit current transaction before starting a new one.
```
@@ -16,7 +16,7 @@ You can learn how to install PostgreSQL on your machine in the [Set Up your Deve
Then in your `medusa-config.js`, you should change the project configuration to use Postgres as the database type:
```jsx
```jsx title=medusa-config.js
module.exports = {
projectConfig: {
//...
@@ -31,7 +31,7 @@ Where `DATABASE_URL` is the connection string to your PostgreSQL database. You c
Make sure to also remove the following lines that are used to configure an SQLite database:
```jsx
```jsx title=medusa-config.js
database_type: "sqlite",
database_database: "./medusa-db.sql",
```
@@ -46,7 +46,7 @@ medusa migrations run
If you want to add demo data into your server, you should also seed the database using the following command:
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm run seed
```
@@ -18,7 +18,7 @@ Node.js is the environment that makes it possible for Medusa to run, so you must
Medusa supports versions 14 and 16 of Node.js. You can check your Node.js version using the following command:
```bash noHeader
```bash noReport
node -v
```
@@ -49,7 +49,7 @@ For other Linux distributions, you can check out [Node.jss guide](https://nod
You can use the following commands to install Node.js on macOS:
<Tabs>
<Tabs groupId="homebrew" wrapperClassName='code-tabs'>
<TabItem value="homebrew" label="Homebrew">
```bash
@@ -60,7 +60,12 @@ brew install node
<TabItem value="no-homebrew" label="Without Homebrew">
```bash
curl "https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- https://nodejs.org/dist/latest/ | sed -nE 's|.*>node-(.*)\.pkg</a>.*|\1|p')}.pkg" > "$HOME/Downloads/node-latest.pkg" && sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"
curl \
"https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- \
https://nodejs.org/dist/latest/ | sed -nE \
's|.*>node-(.*)\.pkg</a>.*|\1|p')}.pkg" \
> "$HOME/Downloads/node-latest.pkg" &&
sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"
```
</TabItem>
@@ -118,7 +123,7 @@ The final installation required to get started with Medusa is the Medusa CLI. It
You can install Medusas CLI with the following command:
```bash npm2yarn noHeader
```bash npm2yarn noReport
npm install @medusajs/medusa-cli -g
```
@@ -153,8 +158,10 @@ You can [download the PostgreSQL Windows installer](https://www.postgresql.org/d
If youre using Ubuntu, you can use the following commands to download and install PostgreSQL:
```bash
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo sh -c \
'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - \
https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install postgresql
```
@@ -172,7 +179,9 @@ You can download PostgreSQL on your macOS using [the installer on their website]
Make sure the Docker Desktop app is running, then run the following command to quickly spin up a PostgreSQL instance:
```bash
docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=medusa-docker -p 5432:5432 -d postgres
docker run --name postgres -e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres -e \
POSTGRES_DB=medusa-docker -p 5432:5432 -d postgres
```
Where:
@@ -221,9 +230,11 @@ sudo service redis-server start
If you use Ubuntu you can use the following commands to install Redis:
```bash
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
curl -fsSL https://packages.redis.io/gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis
+5 -5
View File
@@ -19,7 +19,7 @@ When you run the `create-medusa-app` command, youll install a Medusa server,
In your terminal, run the following command:
<Tabs groupId="npxyarn">
<Tabs groupId="npxyarn" wrapperClassName='code-tabs'>
<TabItem value="npx" label="NPX" default>
```bash
@@ -44,7 +44,7 @@ Youll then be asked to enter the name of the directory you want the project t
Next, youll be asked to choose the Medusa starter used to install the Medusa server. You can either pick the default Medusa starter, the Contentful starter or enter a starter URL by choosing `Other`:
```bash noHeader
```bash noReport
? Which Medusa starter would you like to install? …
medusa-starter-default
medusa-starter-contentful
@@ -63,7 +63,7 @@ You can learn more about the Contentful starter in the [Contentful Integration d
After choosing the Medusa starter, youll be asked to choose the storefront starter. You can choose one of the starters in the list included or choose `None` to skip installing a storefront:
```bash noHeader
```bash noReport
? Which storefront starter would you like to install?
Gatsby Starter
Next.js Starter
@@ -85,7 +85,7 @@ Learn more about the [Next.js](../starters/nextjs-medusa-starter.md) and [Gatsby
After choosing the above starters, the installation of each component will begin along with its dependencies. Once the installation is done, youll see instructions related to how to start each component.
```bash noHeader
```bash noReport
Your project is ready. The available commands are:
Medusa API
@@ -107,7 +107,7 @@ The commands will differ based on your choices in previous prompts.
Inside the root project directory which was specified at the beginning of the installation process youll find the following directory structure:
```bash noHeader
```bash noReport
/my-medusa-store
/storefront // Medusa storefront starter
/backend // Medusa starter as a backend option
+37 -24
View File
@@ -47,37 +47,23 @@ html[data-theme="dark"] .docusaurus-highlight-code-line {
position: relative;
}
.code-tabs .tabs,
.code-header {
padding: 8px 16px;
padding: 12px 16px;
}
.code-header {
background-color: #161616;
display: flex;
justify-content: flex-end;
justify-content: space-between;
border-radius: var(--ifm-code-border-radius) var(--ifm-code-border-radius) 0px 0px;
}
.code-tabs .tabs {
background-color: transparent;
position: absolute;
top: 0;
left: 0;
z-index: 1;
overflow-x: auto;
backface-visibility: hidden;
-ms-overflow-style: none; /* Internet Explorer 10+ */
scrollbar-width: none; /* Firefox */
}
.code-tabs .tabs::-webkit-scrollbar {
display: none;
}
.code-tabs .tabs__item {
color: #6F6F6F;
color: var(--ifm-code-tabs-color);
font-weight: 600;
font-size: 13px;
padding: 0 12px;
@@ -90,6 +76,12 @@ html[data-theme="dark"] .docusaurus-highlight-code-line {
margin-left: 4px;
}
.code-title {
font-size: 13px;
line-height: var(--ifm-line-height-base);
color: var(--ifm-code-title-color);
}
.code-tabs .tabs__item--active {
border-color: #393939;
background-color: var(--ifm-code-tabs-active-bg);
@@ -97,7 +89,8 @@ html[data-theme="dark"] .docusaurus-highlight-code-line {
font-size: 13px;
}
.theme-code-block:not(.no-header-block) {
.theme-code-block:not(.no-header-block),
.code-tabs .theme-code-block {
border-radius: 0 0 var(--ifm-code-border-radius) var(--ifm-code-border-radius) !important;
}
@@ -132,11 +125,11 @@ a.code-action {
width: 20px;
}
.theme-code-block:not(.no-header-block) [class*=buttonGroup] {
/* .theme-code-block:not(.no-header-block) [class*=buttonGroup] {
display: none;
}
} */
.theme-code-block.no-header-block [class*=buttonGroup] button {
.theme-code-block [class*=buttonGroup] button {
opacity: 1 !important;
border: none;
}
@@ -170,9 +163,29 @@ html:not([data-theme="dark"]) .theme-code-block:not(.no-header-block) {
}
@media screen and (min-width: 568px) {
.code-tabs .tabs {
.prism-code {
max-width: 90%;
}
.prism-code:after {
content: '';
position: absolute;
right: 0;
top: 0;
width: calc(10% + 24px);
height: 100%;
background: linear-gradient(90deg, #17171700, #262626 24px);
}
.prism-code:not(:hover)::-webkit-scrollbar-thumb,
.prism-code:not(:hover)::-webkit-scrollbar-track {
visibility: hidden;
}
.prism-code:hover::-webkit-scrollbar-thumb,
.prism-code:hover::-webkit-scrollbar-track {
opacity: 1;
}
}
@media screen and (max-width: 567px) {
@@ -180,7 +193,7 @@ html:not([data-theme="dark"]) .theme-code-block:not(.no-header-block) {
visibility: hidden;
}
.code-tabs .tabs {
max-width: 100%;
.tablist-wrapper.code-header .code-title {
display: none;
}
}
+1 -1
View File
@@ -129,7 +129,7 @@ html:not([data-theme="dark"]) .doc-footer {
z-index: var(--ifm-z-index-overlay);
}
.tabs-wrapper:not(.code-tabs) .tabs-container > div {
.tabs-wrapper:not(.code-tabs) > .tabs-container > div:not(.tablist-wrapper) {
padding-top: var(--ifm-base-margin-vertical);
}
+2
View File
@@ -83,11 +83,13 @@
--ifm-code-border-color: #E5E7EB;
--ifm-code-background: #F9FAFB;
--ifm-code-color: #4B5563;
--ifm-code-tabs-color: #6F6F6F;
--ifm-code-tabs-active-bg: transparent;
--ifm-code-tabs-active-color: #F4F4F4;
--ifm-code-action-hover-bg: rgba(141, 141, 141, 0.16);
--ifm-pre-padding: 16px;
--docusaurus-highlighted-code-line-bg: #393939;
--ifm-code-title-color: #737373;
/* Tooltip */
--ifm-tooltip-background-color: #fff;
+24 -9
View File
@@ -1,20 +1,23 @@
import React from 'react';
import clsx from 'clsx';
import {useThemeConfig, usePrismTheme} from '@docusaurus/theme-common';
import Highlight, {defaultProps} from 'prism-react-renderer';
import React, { useEffect } from 'react';
import {
containsLineNumbers,
parseCodeBlockTitle,
parseLanguage,
parseLines,
containsLineNumbers,
useCodeWordWrap,
} from '@docusaurus/theme-common/internal';
import Highlight, {defaultProps} from 'prism-react-renderer';
import Line from '@theme/CodeBlock/Line';
import {usePrismTheme, useThemeConfig} from '@docusaurus/theme-common';
import Container from '@theme/CodeBlock/Container';
import styles from './styles.module.css';
import CopyButton from '../../CopyButton';
import Line from '@theme/CodeBlock/Line';
import ThemedImage from '@theme/ThemedImage';
import Tooltip from '../../Tooltip';
import clsx from 'clsx';
import styles from './styles.module.css';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useIsBrowser from '@docusaurus/useIsBrowser';
export default function CodeBlockString({
children,
@@ -23,14 +26,17 @@ export default function CodeBlockString({
title: titleProp,
showLineNumbers: showLineNumbersProp,
language: languageProp,
noReport = false
}) {
const {
prism: {defaultLanguage, magicComments},
reportCodeLinkPrefix
} = useThemeConfig();
const language =
languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage;
const prismTheme = usePrismTheme();
const wordWrap = useCodeWordWrap();
const isBrowser = useIsBrowser();
// We still parse the metastring in case we want to support more syntax in the
// future. Note that MDX doesn't strip quotes when parsing metastring:
// "title=\"xyz\"" => title: "\"xyz\""
@@ -63,7 +69,7 @@ export default function CodeBlockString({
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
tabIndex={0}
ref={wordWrap.codeBlockRef}
className={clsx(className, styles.codeBlock, 'thin-scrollbar')}>
className={clsx(className, styles.codeBlock, 'thin-scrollbar', tokens.length === 1 ? styles.thinCodeWrapper : '')}>
<code
className={clsx(
styles.codeBlockLines,
@@ -85,7 +91,16 @@ export default function CodeBlockString({
)}
</Highlight>
<div className={styles.buttonGroup}>
{/* <CopyButton className={styles.codeButton} code={code} /> */}
{!noReport && (
<Tooltip text="Report Incorrect Code">
<a href={`${reportCodeLinkPrefix}&title=${encodeURIComponent(`Docs(Code Issue): Code Issue in ${isBrowser ? location.pathname : ''}`)}`} target="_blank" className='report-code code-action img-url'>
<ThemedImage alt='Report Incorrect Code' sources={{
light: useBaseUrl('/img/alert-code.png'),
dark: useBaseUrl('/img/alert-code-dark.png')
}} className="no-zoom-img" />
</a>
</Tooltip>
)}
<CopyButton buttonClassName='code-action' text={code}>
<ThemedImage alt='Copy to Clipboard' sources={{
light: useBaseUrl('/img/clipboard-copy.png'),
@@ -58,11 +58,11 @@
display: flex;
column-gap: 0.2rem;
position: absolute;
right: calc(var(--ifm-pre-padding) / 4 - 2px);
top: calc(var(--ifm-pre-padding) / 4 - 2px);
right: var(--ifm-pre-padding);
top: var(--ifm-pre-padding);
}
.buttonGroup button {
.buttonGroup .code-action {
display: flex;
align-items: center;
background: var(--prism-background-color);
@@ -80,6 +80,10 @@
opacity: 1 !important;
}
.thinCodeWrapper + .buttonGroup {
top: calc(var(--ifm-pre-padding) / 4 - 2px) !important;
}
:global(.theme-code-block:hover) .buttonGroup button {
opacity: 0.4;
}
+7 -23
View File
@@ -1,13 +1,8 @@
import React, {isValidElement} from 'react';
import CopyButton from '../CopyButton';
import ElementContent from '@theme/CodeBlock/Content/Element';
import StringContent from '@theme/CodeBlock/Content/String';
import ThemedImage from '@theme/ThemedImage';
import Tooltip from '../Tooltip';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useIsBrowser from '@docusaurus/useIsBrowser';
import {useThemeConfig} from '@docusaurus/theme-common';
/**
* Best attempt to make the children a plain string so it is copyable. If there
@@ -22,7 +17,7 @@ function maybeStringifyChildren(children) {
// The children is now guaranteed to be one/more plain strings
return Array.isArray(children) ? children.join('') : children;
}
export default function CodeBlock({children: rawChildren, noHeader = false, ...props}) {
export default function CodeBlock({children: rawChildren, noReport = false, ...props}) {
// The Prism theme on SSR is always the default theme but the site theme can
// be in a different mode. React hydration doesn't update DOM styles that come
// from SSR. Hence force a re-render after mounting to apply the current
@@ -31,29 +26,18 @@ export default function CodeBlock({children: rawChildren, noHeader = false, ...p
const children = maybeStringifyChildren(rawChildren);
const CodeBlockComp =
typeof children === 'string' ? StringContent : ElementContent;
const {reportCodeLinkPrefix} = useThemeConfig();
const title = props.title;
delete props.title;
return (
<div className='code-wrapper'>
{!noHeader && (
{title && (
<div className='code-header'>
<Tooltip text="Report Incorrect Code">
<a href={`${reportCodeLinkPrefix}&title=${encodeURIComponent(`Docs(Code Issue): Code Issue in ${isBrowser ? location.pathname : ''}`)}`} target="_blank" className='report-code code-action img-url'>
<ThemedImage alt='Report Incorrect Code' sources={{
light: useBaseUrl('/img/alert-code.png'),
dark: useBaseUrl('/img/alert-code-dark.png')
}} className="no-zoom-img" />
</a>
</Tooltip>
<CopyButton buttonClassName='code-action' text={children}>
<ThemedImage alt='Copy to Clipboard' sources={{
light: useBaseUrl('/img/clipboard-copy.png'),
dark: useBaseUrl('/img/clipboard-copy-dark.png')
}} className="no-zoom-img" />
</CopyButton>
{title}
</div>
)}
<CodeBlockComp key={String(isBrowser)} {...props} className={noHeader ? 'no-header-block' : ''}>
<CodeBlockComp key={String(isBrowser)} {...props} noReport={noReport} className={title ? '' : 'no-header-block'}>
{children}
</CodeBlockComp>
</div>
+45 -37
View File
@@ -1,12 +1,14 @@
import React, {useState, cloneElement, isValidElement, useEffect} from 'react';
import clsx from 'clsx';
import useIsBrowser from '@docusaurus/useIsBrowser';
import {duplicates} from '@docusaurus/theme-common';
import React, {cloneElement, isValidElement, useEffect, useState} from 'react';
import {
useScrollPositionBlocker,
useTabGroupChoice,
} from '@docusaurus/theme-common/internal';
import clsx from 'clsx';
import {duplicates} from '@docusaurus/theme-common';
import styles from './styles.module.css';
import useIsBrowser from '@docusaurus/useIsBrowser';
// A very rough duck type, but good enough to guard against mistakes while
// allowing customization
function isTabItem(comp) {
@@ -20,6 +22,8 @@ function TabsComponent(props) {
values: valuesProp,
groupId,
className,
isCodeTabs = false,
codeTitle
} = props;
const children = React.Children.map(props.children, (child) => {
if (isValidElement(child) && isTabItem(child)) {
@@ -113,39 +117,42 @@ function TabsComponent(props) {
};
return (
<div className={clsx('tabs-container', styles.tabList)}>
<ul
role="tablist"
aria-orientation="horizontal"
className={clsx(
'tabs',
{
'tabs--block': block,
},
className,
)}>
{values.map(({value, label, attributes}) => (
<li
role="tab"
tabIndex={selectedValue === value ? 0 : -1}
aria-selected={selectedValue === value}
key={value}
ref={(tabControl) => tabRefs.push(tabControl)}
onKeyDown={handleKeydown}
onFocus={handleTabChange}
onClick={handleTabChange}
{...attributes}
className={clsx(
'tabs__item',
styles.tabItem,
attributes?.className,
{
'tabs__item--active': selectedValue === value,
},
)}>
{label ?? value}
</li>
))}
</ul>
<div className={`tablist-wrapper ${isCodeTabs ? 'code-header' : ''}`}>
{isCodeTabs && <span className='code-title'>{codeTitle}</span>}
<ul
role="tablist"
aria-orientation="horizontal"
className={clsx(
'tabs',
{
'tabs--block': block,
},
className,
)}>
{values.map(({value, label, attributes}) => (
<li
role="tab"
tabIndex={selectedValue === value ? 0 : -1}
aria-selected={selectedValue === value}
key={value}
ref={(tabControl) => tabRefs.push(tabControl)}
onKeyDown={handleKeydown}
onFocus={handleTabChange}
onClick={handleTabChange}
{...attributes}
className={clsx(
'tabs__item',
styles.tabItem,
attributes?.className,
{
'tabs__item--active': selectedValue === value,
},
)}>
{label ?? value}
</li>
))}
</ul>
</div>
{lazy ? (
cloneElement(
@@ -183,6 +190,7 @@ export default function Tabs(props) {
// Remount tabs after hydration
// Temporary fix for https://github.com/facebook/docusaurus/issues/5653
key={String(isBrowser)}
isCodeTabs={props.wrapperClassName?.search('code-tabs') !== -1 || props.groupId === 'npm2yarn'}
{...props}
/>
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB