Merge branch 'master' into docs/development

This commit is contained in:
Shahed Nasser authored and GitHub committed 2022-05-18 19:14:52 +03:00
commit 12570631cf
414 files changed
+28835 -10082

No files matched your search

+13 -13
View File
@@ -5,19 +5,19 @@
/packages/medusa/src/subscribers/notification.js /packages/medusa/src/subscribers/notification.js
/packages/medusa/src/subscribers/order.js /packages/medusa/src/subscribers/order.js
/packages/medusa/src/subscribers/product.js /packages/medusa/src/subscribers/product.js
/packages/medusa/src/loaders/api.js /packages/medusa/src/loaders/api.ts
/packages/medusa/src/loaders/database.js /packages/medusa/src/loaders/database.ts
/packages/medusa/src/loaders/defaults.js /packages/medusa/src/loaders/defaults.ts
/packages/medusa/src/loaders/express.js /packages/medusa/src/loaders/express.ts
/packages/medusa/src/loaders/index.js /packages/medusa/src/loaders/index.ts
/packages/medusa/src/loaders/logger.js /packages/medusa/src/loaders/logger.ts
/packages/medusa/src/loaders/models.js /packages/medusa/src/loaders/models.ts
/packages/medusa/src/loaders/passport.js /packages/medusa/src/loaders/passport.ts
/packages/medusa/src/loaders/plugins.js /packages/medusa/src/loaders/plugins.ts
/packages/medusa/src/loaders/redis.js /packages/medusa/src/loaders/redis.ts
/packages/medusa/src/loaders/repositories.js /packages/medusa/src/loaders/repositories.ts
/packages/medusa/src/loaders/services.js /packages/medusa/src/loaders/services.ts
/packages/medusa/src/loaders/subscribers.js /packages/medusa/src/loaders/subscribers.ts
/packages/medusa/src/api/routes/admin/auth /packages/medusa/src/api/routes/admin/auth
/packages/medusa/src/api/routes/admin/collections /packages/medusa/src/api/routes/admin/collections
/packages/medusa/src/api/routes/store/carts /packages/medusa/src/api/routes/store/carts
+4
View File
@@ -21,17 +21,21 @@ Thank you for considering contributing to Medusa! This document will outline how
### Branches ### Branches
All changes should be part of a branch and submitted as a pull request - your branches should be prefixed with one of: All changes should be part of a branch and submitted as a pull request - your branches should be prefixed with one of:
- `fix/` for bug fixes - `fix/` for bug fixes
- `feat/` for features - `feat/` for features
- `docs/` for documentation changes - `docs/` for documentation changes
### Commits ### Commits
Strive towards keeping your commits small and isolated - this helps the reviewer understand what is going on and makes it easier to process your requests. Strive towards keeping your commits small and isolated - this helps the reviewer understand what is going on and makes it easier to process your requests.
### Pull Requests ### Pull Requests
Once your changes are ready you must submit your branch as a pull request. Your pull request should be opened against the `develop` branch in the main Medusa repo. Once your changes are ready you must submit your branch as a pull request. Your pull request should be opened against the `develop` branch in the main Medusa repo.
In your PR's description you should follow the structure: In your PR's description you should follow the structure:
- **What** - what changes are in this PR - **What** - what changes are in this PR
- **Why** - why are these changes relevant - **Why** - why are these changes relevant
- **How** - how have the changes been implemented - **How** - how have the changes been implemented
+216 -216
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -57,7 +57,7 @@ And that's all! You've now enabled Algolia for your Medusa store engine. The plu
**Usage** **Usage**
This article will not go too much into depth about how the search functionality works under the hood when querying the API. We refer to the [previous article on MeiliSearch](https://www.medusajs.com/post/meilisearch-and-medusa) if this is of your interest. In there, you will find a quick showcase using Postman as well as a thorough walkthrough of how you can display the results in your storefront using ReactJS (GatsbyJS). This article will not go too much into depth about how the search functionality works under the hood when querying the API. We refer to the [previous article on MeiliSearch](https://medusajs.com/blog/meilisearch-medusa) if this is of your interest. In there, you will find a quick showcase using Postman as well as a thorough walkthrough of how you can display the results in your storefront using ReactJS (GatsbyJS).
Instead, to illustrate the power of our Search API and search engine plugins, we'll switch out a MeiliSearch plugin with our new Algolia plugin in a store with existing products. Upon restarting the server with the new configuration, your products will automatically be fed into Algolia and the search functionality in your frontend will remain unchanged. Instead, to illustrate the power of our Search API and search engine plugins, we'll switch out a MeiliSearch plugin with our new Algolia plugin in a store with existing products. Upon restarting the server with the new configuration, your products will automatically be fed into Algolia and the search functionality in your frontend will remain unchanged.
+1 -1
View File
@@ -297,7 +297,7 @@ You don’t have to create a template for every type in the reference. You can s
``` ```
</details> </details>
### Order Cancelled ### Order Canceled
**Key in plugin options:** `order_canceled_template` **Key in plugin options:** `order_canceled_template`
@@ -24,7 +24,7 @@ export default () => {
router.get("/admin/hello", (req, res) => { router.get("/admin/hello", (req, res) => {
res.json({ res.json({
message: "Welcome to Your Store!" message: "Welcome to Your Store!",
}) })
}) })
@@ -61,8 +61,8 @@ Then, create an object that will hold the CORS configurations:
```js ```js
const corsOptions = { const corsOptions = {
origin: projectConfig.admin_cors.split(","), origin: projectConfig.admin_cors.split(","),
credentials: true, credentials: true,
} }
``` ```
@@ -72,7 +72,7 @@ Finally, for each route you add, create an `OPTIONS` request:
router.options("/admin/hello", cors(corsOptions)) router.options("/admin/hello", cors(corsOptions))
router.get("/admin/hello", (req, res) => { router.get("/admin/hello", (req, res) => {
//... //...
}); })
``` ```
## Multiple Endpoints ## Multiple Endpoints
@@ -84,13 +84,13 @@ You can add more than one endpoints in `src/api/index.js`:
```js ```js
router.get("/admin/hello", (req, res) => { router.get("/admin/hello", (req, res) => {
res.json({ res.json({
message: "Welcome to Your Store!" message: "Welcome to Your Store!",
}) })
}) })
router.get("/admin/bye", (req, res) => { router.get("/admin/bye", (req, res) => {
res.json({ res.json({
message: "Come back again!" message: "Come back again!",
}) })
}) })
``` ```
@@ -105,7 +105,7 @@ To do that with the previous example, first, create the file `src/api/hello.js`
export default (router) => { export default (router) => {
router.get("/admin/hello", (req, res) => { router.get("/admin/hello", (req, res) => {
res.json({ res.json({
message: "Welcome to Your Store!" message: "Welcome to Your Store!",
}) })
}) })
} }
@@ -119,7 +119,7 @@ Next, create the file `src/api/bye.js` with the following content:
export default (router) => { export default (router) => {
router.get("/admin/bye", (req, res) => { router.get("/admin/bye", (req, res) => {
res.json({ res.json({
message: "Come back again!" message: "Come back again!",
}) })
}) })
} }
@@ -156,17 +156,15 @@ You can retrieve any registered service in your endpoint using `req.scope.resolv
Here’s an example of an endpoint that retrieves the count of products in your store: Here’s an example of an endpoint that retrieves the count of products in your store:
```js ```js
router.get('/admin/products/count', (req, res) => { router.get("/admin/products/count", (req, res) => {
const productService = req.scope.resolve('productService') const productService = req.scope.resolve("productService")
productService productService.count().then((count) => {
.count() res.json({
.then((count) => { count,
res.json({ })
count
})
})
}) })
})
``` ```
The `productService` has a `count` method that returns a Promise. This Promise resolves to the count of the products. You return a JSON of the product count. The `productService` has a `count` method that returns a Promise. This Promise resolves to the count of the products. You return a JSON of the product count.
@@ -178,13 +176,13 @@ Protected routes are routes that should be accessible by logged-in users only.
To make a route protected, first, import the `authenticate` middleware: To make a route protected, first, import the `authenticate` middleware:
```js ```js
import authenticate from '@medusajs/medusa/dist/api/middlewares/authenticate' import authenticate from "@medusajs/medusa/dist/api/middlewares/authenticate"
``` ```
Then, add the middleware to your route: Then, add the middleware to your route:
```js ```js
router.get('/store/products/count', authenticate(), (req, res) => { router.get("/store/products/count", authenticate(), (req, res) => {
//... //...
}) })
``` ```
@@ -24,7 +24,7 @@ export default () => {
router.get("/store/hello", (req, res) => { router.get("/store/hello", (req, res) => {
res.json({ res.json({
message: "Welcome to My Store!" message: "Welcome to My Store!",
}) })
}) })
@@ -55,13 +55,13 @@ You can add more than one endpoints in `src/api/index.js`:
```js ```js
router.get("/store/hello", (req, res) => { router.get("/store/hello", (req, res) => {
res.json({ res.json({
message: "Welcome to My Store!" message: "Welcome to My Store!",
}) })
}) })
router.get("/store/bye", (req, res) => { router.get("/store/bye", (req, res) => {
res.json({ res.json({
message: "Come back again!" message: "Come back again!",
}) })
}) })
``` ```
@@ -76,7 +76,7 @@ To do that with the previous example, first, create the file `src/api/hello.js`
export default (router) => { export default (router) => {
router.get("/store/hello", (req, res) => { router.get("/store/hello", (req, res) => {
res.json({ res.json({
message: "Welcome to My Store!" message: "Welcome to My Store!",
}) })
}) })
} }
@@ -90,7 +90,7 @@ Next, create the file `src/api/bye.js` with the following content:
export default (router) => { export default (router) => {
router.get("/store/bye", (req, res) => { router.get("/store/bye", (req, res) => {
res.json({ res.json({
message: "Come back again!" message: "Come back again!",
}) })
}) })
} }
@@ -127,17 +127,15 @@ You can retrieve any registered service in your endpoint using `req.scope.resolv
Here’s an example of an endpoint that retrieves the count of products in your store: Here’s an example of an endpoint that retrieves the count of products in your store:
```js ```js
router.get('/store/products/count', (req, res) => { router.get("/store/products/count", (req, res) => {
const productService = req.scope.resolve('productService') const productService = req.scope.resolve("productService")
productService productService.count().then((count) => {
.count() res.json({
.then((count) => { count,
res.json({ })
count
})
})
}) })
})
``` ```
The `productService` has a `count` method that returns a Promise. This Promise resolves to the count of the products. You return a JSON of the product count. The `productService` has a `count` method that returns a Promise. This Promise resolves to the count of the products. You return a JSON of the product count.
@@ -149,13 +147,13 @@ Protected routes are routes that should be accessible by logged-in customers onl
To make a route protected, first, import the `authenticate` middleware: To make a route protected, first, import the `authenticate` middleware:
```js ```js
import authenticate from '@medusajs/medusa/dist/api/middlewares/authenticate' import authenticate from "@medusajs/medusa/dist/api/middlewares/authenticate"
``` ```
Then, add the middleware to your route: Then, add the middleware to your route:
```jsx ```jsx
router.get('/store/products/count', authenticate(), (req, res) => { router.get("/store/products/count", authenticate(), (req, res) => {
//... //...
}) })
``` ```
@@ -0,0 +1,97 @@
# Migrations
In this document, you’ll learn about what Migrations are, their purpose, how you can run them, and how you can create your own Migrations.
:::note
Medusa’s Migrations do not work with SQLite databases. They are intended to be used with PostgreSQL databases, which is the recommended Database for your Medusa production server.
:::
## Overview
Migrations are scripts that are used to make additions or changes to your database schema. In Medusa, they are essential for both when you first install your server and for subsequent server upgrades later on.
When you first create your Medusa server, the database schema used must have all the tables necessary for the server to run.
When a new Medusa version introduces changes to the database schema, you'll have to run migrations to apply them to your own database.
:::tip
Migrations are used to apply changes to the database schema. However, there are some version updates of Medusa that require updating the data in your database to fit the new schema. Those are specific to each version and you should check out the version under Upgrade Guides for details on the steps.
:::
## How to Run Migrations
Migrations in Medusa can be done in one of two ways:
### Migrate Command
Using the Medusa CLI tool, you can run migrations with the following command:
```bash
medusa migrations run
```
This will check for any migrations that contain changes to your database schema that aren't applied yet and run them on your server.
### Seed Command
Seeding is the process of filling your database with data that is either essential or for testing and demo purposes. In Medusa, the `seed` command will run the migrations to your database if necessary before it seeds your database with dummy data.
You can use the following command to seed your database:
```bash npm2yarn
npm run seed
```
This will use the underlying `seed` command provided by Medusa's CLI to seed your database with data from the file `data/seed.json` on your Medusa server.
## How to Create Migrations
In this section, you’ll learn how to create your own migrations using [Typeorm](https://typeorm.io). This will allow you to modify Medusa’s predefined tables or create your own tables.
### Create Migration
To create a migration that makes changes to your Medusa schema, run the following command:
```bash
npx typeorm migration:create -n src/path/to/UserChanged
```
:::tip
The migration file should be inside the src directory to make sure it is built when the build command is run next.
:::
This will create the migration file in the path you specify. You can use this without the need to install Typeorm's CLI tool. You can then go ahead and make changes to it as necessary.
:::tip
You can learn more about writing migrations in [Typeorm’s Documentation](https://typeorm.io/migrations).
:::
### Build Files
Before you can run the migrations you need to run the build command to transpile the TypeScript files to JavaScript files:
```bash npm2yarn
npm run build
```
### Run Migration
The last step is to run the migration with the command detailed earlier
```bash
medusa migrations run
```
If you check your database now you should see that the change defined by the migration has been applied successfully.
## What’s Next 🚀
- Learn more about [setting up your development server](../../tutorial/0-set-up-your-development-environment.md).
@@ -6,7 +6,7 @@ In this document, you’ll learn how to add a Payment Provider to your Medusa se
A Payment Provider is the payment method used to authorize, capture, and refund payment, among other actions. An example of a Payment Provider is Stripe. A Payment Provider is the payment method used to authorize, capture, and refund payment, among other actions. An example of a Payment Provider is Stripe.
By default, Medusa has a [manual payment provider](https://github.com/medusajs/medusa/tree/2e6622ec5d0ae19d1782e583e099000f0a93b051/packages/medusa-fulfillment-manual) that has minimal implementation. It can be synonymous with a Cash on Delivery payment method. It allows store operators to manage the payment themselves but still keep track of its different stages on Medusa. By default, Medusa has a [manual payment provider](https://github.com/medusajs/medusa/tree/master/packages/medusa-payment-manual) that has minimal implementation. It can be synonymous with a Cash on Delivery payment method. It allows store operators to manage the payment themselves but still keep track of its different stages on Medusa.
Adding a Payment Provider is as simple as creating a [service](../services/create-service.md) file in `src/services`. A Payment Provider is essentially a service that extends `PaymentService` from `medusa-interfaces`. Adding a Payment Provider is as simple as creating a [service](../services/create-service.md) file in `src/services`. A Payment Provider is essentially a service that extends `PaymentService` from `medusa-interfaces`.
@@ -42,9 +42,9 @@ These methods are used at different points in the Checkout flow as well as when
![Payment Flows.jpg](https://i.imgur.com/WeDr0ph.jpg) ![Payment Flows.jpg](https://i.imgur.com/WeDr0ph.jpg)
## Create a Fulfillment Provider ## Create a Payment Provider
The first step to create a fulfillment provider is to create a file in `src/services` with the following content: The first step to create a payment provider is to create a file in `src/services` with the following content:
```jsx ```jsx
import { PaymentService } from "medusa-interfaces" import { PaymentService } from "medusa-interfaces"
@@ -1,7 +1,5 @@
--- ---
title: Create a Service title: Create a Service
--- ---
# Create a Service # Create a Service
@@ -31,7 +29,7 @@ To create a service, you should create a JavaScript file in `src/services` to ho
For example, if you want to create a service `helloService`, create the file `hello.js` in `src/services` with the following content: For example, if you want to create a service `helloService`, create the file `hello.js` in `src/services` with the following content:
```js ```js
import { BaseService } from "medusa-interfaces"; import { BaseService } from "medusa-interfaces"
class HelloService extends BaseService { class HelloService extends BaseService {
getMessage() { getMessage() {
@@ -39,7 +37,7 @@ class HelloService extends BaseService {
} }
} }
export default HelloService; export default HelloService
``` ```
## Service Constructor ## Service Constructor
@@ -85,10 +83,10 @@ constructor({ helloService }) {
To use your custom service in an endpoint, you can use `req.scope.resolve` passing it the service’s registration name: To use your custom service in an endpoint, you can use `req.scope.resolve` passing it the service’s registration name:
```js ```js
const helloService = req.scope.resolve('helloService'); const helloService = req.scope.resolve("helloService")
res.json({ res.json({
message: helloService.getMessage() message: helloService.getMessage(),
}) })
``` ```
@@ -0,0 +1,51 @@
# v1.3.1
Version 1.3.1 of Medusa introduces new features including the addition of Line Item Adjustments and a more advanced Promotions API. The changes do not affect the public APIs and require only running necessary data migrations.
## Prerequisites
Both the actions required for this update need you to set the following environment variables:
```bash
TYPEORM_CONNECTION=postgres
TYPEORM_URL=<DATABASE_URL>
TYPEORM_LOGGING=true
TYPEORM_ENTITIES=./node_modules/@medusajs/medusa/dist/models/*.js
TYPEORM_MIGRATIONS=./node_modules/@medusajs/medusa/dist/migrations/*.js
```
These environment variables are used in the data migration scripts in this upgrade. Make sure to replace `<DATABASE_URL>` with your PostgreSQL database URL.
## Line Item Adjustments
This new version of Medusa allows store operators to adjust line items in an order or a swap which provides more customization capabilities.
It introduces a new model `LineItemAdjustment` which gives more flexibility to adjust the pricing of line items in a cart, order, or swap. A discount can be added, removed, or modified and the price will reflect on the total calculation of the cart, order, or swap.
This also introduces an optimization to the calculation of totals, as it is no longer necessary to calculate the discounts every time the totals are retrieved.
### Actions Required
This new version adds a new data migration script that will go through your list of existing orders and add line item adjustments for each of the line items in the order.
For that reason, it’s essential to run the data migration script after upgrading your server and before starting your Medusa server:
```bash
node ./node_modules/@medusajs/medusa/dist/scripts/line-item-adjustment-migration.js
```
## Advanced Discount Conditions
This new version of Medusa holds advanced promotions functionalities to provide store operators with even more customization capabilities when creating discounts. You can now add even more conditions to your discounts to make them specific for a set of products, collections, customer groups, and more.
This change required creating a new model `DiscountCondition` which belongs to `DiscountRule` and includes a few relationships with other models to make the aforementioned feature possible.
### Actions Required
To ensure your old discount rules play well with the new Promotions API and schema, this version includes a migration script that will go through your existing discount rules, create discount conditions for these rules, and move the former direct relationship between discount rules and products to become between discount conditions and products.
For that reason, it’s essential to run the data migration script after upgrading your server and before starting your Medusa server:
```bash
node ./node_modules/@medusajs/medusa/dist/scripts/discount-rule-migration.js
```
+5 -5
View File
@@ -55,7 +55,7 @@ For the walkthrough purposes, we assume that the selected starter is `medusa-sta
### Selecting a Storefront ### Selecting a Storefront
After selecting your Medusa starter you will be given the option to install one of our storefront starters. At the moment we have starters for Gatsby and Next.js: After selecting your Medusa starter, you will be given the option to install one of our storefront starters. At the moment, we have starters for Gatsby and Next.js:
```bash ```bash
Which storefront starter would you like to install? … Which storefront starter would you like to install? …
@@ -74,7 +74,7 @@ Creating new project from git: https://github.com/medusajs/medusa-starter-defaul
Installing packages... Installing packages...
``` ```
Once the installation has been completed you will have a Medusa backend, a demo storefront, and an admin dashboard. Once the installation has been completed, you will have a Medusa backend, a Demo storefront, and an Admin dashboard.
## What's inside ## What's inside
@@ -87,7 +87,7 @@ Inside the root folder which was specified at the beginning of the installation
/admin // Medusa admin panel /admin // Medusa admin panel
``` ```
`create-medusa-app` prints out the commands that are available to you after installation. When each project is started you can visit your storefront, complete the order, and view the order in Medusa admin. `create-medusa-app` prints out the commands that are available to you after installation. When each project is started, you can visit your storefront, complete the order, and view the order in Medusa admin.
```bash ```bash
⠴ Installing packages... ⠴ Installing packages...
@@ -112,10 +112,10 @@ Create initial git commit in my-medusa-store/admin
## **What's next?** ## **What's next?**
To learn more about Medusa to go through our docs to get some inspiration and guidance for the next steps and further development: To learn more about Medusa, go through our docs to get some inspiration and guidance for the next steps and further development:
- [Find out how to set up a Medusa project with Gatsby and Contentful](https://docs.medusajs.com/how-to/headless-ecommerce-store-with-gatsby-contentful-medusa) - [Find out how to set up a Medusa project with Gatsby and Contentful](https://docs.medusajs.com/how-to/headless-ecommerce-store-with-gatsby-contentful-medusa)
- [Move your Medusa setup to the next level with some custom functionality](https://docs.medusajs.com/tutorial/adding-custom-functionality) - [Move your Medusa setup to the next level with some custom functionality](https://docs.medusajs.com/tutorial/adding-custom-functionality)
- [Create your own Medusa plugin](https://docs.medusajs.com/guides/plugins) - [Create your own Medusa plugin](https://docs.medusajs.com/guides/plugins)
If you have any follow-up questions or want to chat directly with our engineering team we are always happy to meet you at our [Discord](https://discord.gg/DSHySyMu). If you have any follow-up questions or want to chat directly with our engineering team, we are always happy to meet you at our [Discord](https://discord.gg/DSHySyMu).
+6 -6
View File
@@ -4,7 +4,7 @@ title: "Deploying on Heroku"
# Deploying on Heroku # Deploying on Heroku
This is a guide for deploying a Medusa project on Heroku. Heroku is at PaaS that allows you to easily deploy your applications in the cloud. This is a guide for deploying a Medusa project on Heroku. Heroku is a PaaS (Platform as a Service) that allows you to easily deploy your applications in the cloud.
<div> <div>
<video width="100%" height="100%" playsinline autoplay muted controls> <video width="100%" height="100%" playsinline autoplay muted controls>
@@ -57,7 +57,7 @@ Follow the instructions on your terminal
### 3. Create an app on Heroku ### 3. Create an app on Heroku
In your **Medusa project directory** run the following commands to create an app on Heroku and add it as a remote origin. In your **Medusa project directory**, run the following commands to create an app on Heroku and add it as a remote origin.
```bash ```bash
heroku create medusa-test-app heroku create medusa-test-app
@@ -70,13 +70,13 @@ Medusa requires a Postgres database and a Redis instance to work. These are adde
:::tip :::tip
In this below example, we initialize the resources on free plans. This is not a valid configuration for a production environment. In this example below, we initialize the resources on free plans. This is not a valid configuration for a production environment.
::: :::
#### Postgresql #### Postgresql
Add a Postgres addon to your Heroku app Add a Postgres add-on to your Heroku app
```bash ```bash
heroku addons:create heroku-postgresql:hobby-dev heroku addons:create heroku-postgresql:hobby-dev
@@ -90,7 +90,7 @@ Add a Redis instance to your Heroku app
:::note :::note
The addon `redistogo:nano` is free, but Heroku requires you to add a payment method to proceed. The add-on `redistogo:nano` is free, but Heroku requires you to add a payment method to proceed.
::: :::
@@ -102,7 +102,7 @@ You can find more informations, plans and pricing about Redis To Go [here](https
### 5. Configure environment variables on Heroku ### 5. Configure environment variables on Heroku
Medusa requires a set of environment variables. From you project repository run the following commands:. Medusa requires a set of environment variables. From you project repository, run the following commands:.
```bash ```bash
heroku config:set NODE_ENV=production heroku config:set NODE_ENV=production
@@ -15,7 +15,7 @@ This article assumes you already have the Medusa project created and ready to be
## Getting started ## Getting started
In order to get started let's open the terminal and use the following command to create an instance of your storefront: In order to get started, let's open the terminal and use the following command to create an instance of your storefront:
```zsh ```zsh
npx create-next-app -e https://github.com/medusajs/nextjs-starter-medusa my-medusa-storefront npx create-next-app -e https://github.com/medusajs/nextjs-starter-medusa my-medusa-storefront
@@ -31,7 +31,7 @@ Let's jump to these two.
For this part, we should navigate to a `client.js` file which you can find in the utils folder. For this part, we should navigate to a `client.js` file which you can find in the utils folder.
We don't need to do much in here, but to make sure that our storefront is pointing to the port, where the server is running We don't need to do much in here, but to make sure that our storefront is pointing to the port where the server is running
```js ```js
import Medusa from "@medusajs/medusa-js" import Medusa from "@medusajs/medusa-js"
@@ -39,13 +39,13 @@ const BACKEND_URL = process.env.GATSBY_STORE_URL || "http://localhost:9000" // <
export const createClient = () => new Medusa({ baseUrl: BACKEND_URL }) export const createClient = () => new Medusa({ baseUrl: BACKEND_URL })
``` ```
By default the Medusa server is running at port 9000, so if you didn't change that we are good to go to our next step. By default, the Medusa server is running at port 9000. So if you didn't change that, we are good to go to our next step.
## Update the `STORE_CORS` variable ## Update the `STORE_CORS` variable
Here let's navigate to your Medusa server and open `medusa-config.js` Here let's navigate to your Medusa server and open `medusa-config.js`
Let's locate the `STORE_CORS` variable and make sure it's the right port (which is 3000 by default for Next.js projects) Let's locate the `STORE_CORS` variable and make sure it's the right port (which is 3000 by default for Next.js projects).
```js ```js
/* /*
+2 -4
View File
@@ -1,7 +1,5 @@
--- ---
title: Introduction title: Introduction
--- ---
## Architecture overview ## Architecture overview
@@ -20,9 +18,9 @@ Your Medusa server will include all functionalities related to your store’s ch
The admin dashboard is accessible by store operators. Store operators can use the admin dashboard to view, create, and modify data such as orders and products. The admin dashboard is accessible by store operators. Store operators can use the admin dashboard to view, create, and modify data such as orders and products.
Medusa provides a beautiful [admin dashboard](https://demo.medusajs.com) that you can use right off the bat. Our admin dashboard provides a lot of functionalities to manage your store including Order management, product management, user management and more. Medusa provides a beautiful [admin dashboard](https://demo.medusajs.com) that you can use right off the bat. Our admin dashboard provides a lot of functionalities to manage your store including Order management, Product management, User management and more.
You can also create your own admin dashboard by utilizing the [Admin REST APIs](https://docs.medusajs.com/api/admin/auth). You can also create your own admin dashboard by utilizing the [Admin REST APIs](https://docs.medusajs.com/api/admin/auth).
### Storefront ### Storefront
+22 -20
View File
@@ -1,6 +1,12 @@
# Quickstart # Quickstart
This quickstart is intended for experienced developers, that are accustomed with concepts like JavaScript, Node.js, SQL and the command line. For a more gentle introduction, see our tutorial on [how to set up your development environment](/tutorial/set-up-your-development-environment). This document will guide you through setting up your Medusa server in a few minutes.
:::tip
For a more detailed guide on how to set up your local environment to work with Medusa, check out the documentation on [how to set up your development environment](/tutorial/set-up-your-development-environment).
:::
## Prerequisites ## Prerequisites
@@ -29,33 +35,29 @@ You can install Node from the [official website](https://nodejs.org/en/).
3. **Start your Medusa engine** 3. **Start your Medusa engine**
```bash ```bash
cd my-medusa-store
medusa develop medusa develop
``` ```
4. **Use the API** After these 3 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 ```bash
curl localhost:9000/store/products | python -m json.tool curl localhost:9000/store/products | python -m json.tool
``` ```
After these four steps and only a couple of minutes, you now have a complete commerce engine running locally. You may now explore [the documentation](https://docs.medusajs.com/api) to learn how to interact with the Medusa API. You may also add [plugins](https://github.com/medusajs/medusa/tree/master/packages) to your Medusa store by specifying them in your `medusa-config.js` file. :::note
We have a prebuilt admin dashboard that you can use to configure and manage your store find it here: [Medusa Admin](https://github.com/medusajs/admin)
## What's next? 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:
### Set up a storefront for your Medusa project ```bash
curl localhost:9000/store/products
We have created two starters for you that can help you lay a foundation for your storefront. The starters work with your new server with minimal configuration simply clone the starters from here: ```
- [Nextjs Starter](https://github.com/medusajs/nextjs-starter-medusa)
- [Gatsby Starter](https://github.com/medusajs/gatsby-starter-medusa)
:::tip
Medusa runs on port 9000 by default and the storefront starters are both configured to run on port 8000. If you wish to run your storefront starter on another port you should update your CORS settings in your project's `medusa-config.js`.
::: :::
<!-- ### Link you local development to Medusa Cloud (Coming soon!) ## What's next :rocket:
With your project in local development you can link your Medusa instance to Medusa Cloud - this will allow you to manage your store, view orders and test out the amazing functionalities that you are building. [Get started here](https://docs.medusajs.com/tutorial/linking-your-local-project-with-medusa-cloud). --> - Install our [Next.js](http://localhost:3000/starters/nextjs-medusa-starter) or [Gatsby](http://localhost:3000/starters/gatsby-medusa-starter) storefronts to set up your ecommerce storefront quickly.
- Install the [Medusa Admin](../admin/quickstart.md) to supercharge your ecommerce experience with easy access to configurations and features.
- Check our the [API reference](https://docs.medusajs.com/api) to learn more about available endpoints available on your Medusa server.
- Install [plugins](https://github.com/medusajs/medusa/tree/master/packages) for features like Payment, CMS, Notifications, among other features.
@@ -6,15 +6,15 @@ title: Creating your Medusa server
## Introduction ## Introduction
With the required software installed on your computer you are ready to start working on your first Medusa project. With the required software installed on your computer, you are ready to start working on your first Medusa project.
In this part of the tutorial we will setup the skeleton for a Medusa store and will be making the first requests to your Medusa server. In this part of the tutorial, we will setup the skeleton for a Medusa store and will be making the first requests to your Medusa server.
Once you have completed this part of the tutorial you will have a powerful backend for digital commerce experiences. The server will be capable of handling orders, ensuring payments are going through, keeping basic product and customer data in sync, etc. You can use one of the frontend starters to quickly hook up your server to a presentation layer ([Gatsby](https://github.com/medusajs/gatsby-starter-medusa) or [Next](https://github.com/medusajs/nextjs-starter-medusa)). Once you have completed this part of the tutorial, you will have a powerful backend for digital commerce experiences. The server will be capable of handling orders, ensuring payments are going through, keeping basic product and customer data in sync, etc. You can use one of the frontend starters to quickly hook up your server to a presentation layer ([Gatsby](https://github.com/medusajs/gatsby-starter-medusa) or [Next](https://github.com/medusajs/nextjs-starter-medusa)).
## Setup a Medusa project ## Setup a Medusa project
With Medusa CLI installed it is very easy to setup a new Medusa project, with the `new` command. In your command line run: With Medusa CLI installed, it is very easy to setup a new Medusa project, with the `new` command. In your command line run:
```shell ```shell
medusa new my-medusa-server --seed medusa new my-medusa-server --seed
@@ -28,7 +28,7 @@ The command will do a number of things:
- create a database in postgres with the name my-medusa-server - create a database in postgres with the name my-medusa-server
- the `--seed` flag indicates that the database should be populated with some test data after the project has been set up - the `--seed` flag indicates that the database should be populated with some test data after the project has been set up
If you navigate to the root folder of your new project you will see the following files in your directory: If you navigate to the root folder of your new project, you will see the following files in your directory:
``` ```
. .
@@ -43,11 +43,11 @@ If you navigate to the root folder of your new project you will see the followin
└── package.json └── package.json
``` ```
There is not a lot of files needed to get your Medusa store setup and this is all due to the fact that the main Medusa core (`@medusajs/medusa`) is installed as a dependency in your project giving you all the fundamental needs for a digital commerce experience. There is not a lot of files needed to get your Medusa store setup and this is all due to the fact that the main Medusa core (`@medusajs/medusa`) is installed as a dependency in your project, giving you all the fundamental needs for a digital commerce experience.
Much of Medusa's power lies in the `medusa-config.js` which is the file that configures your store and orchestrates the plugins that you wish to use together with your store. There are some different types of plugin categories such as payment plugins, notification plugins and fulfillment plugins, but plugins can contain any form of extension that enhances your store. Much of Medusa's power lies in the `medusa-config.js` which is the file that configures your store and orchestrates the plugins that you wish to use together with your store. There are some different types of plugin categories such as payment plugins, notification plugins and fulfillment plugins, but plugins can contain any form of extension that enhances your store.
For customizations that are more particular to your project you can extend your Medusa server by adding files in the `api` and `services` directories. More about customizing your server will follow in the following parts. For customizations that are more particular to your project, you can extend your Medusa server by adding files in the `api` and `services` directories. More about customizing your server will follow in the following parts.
## Starting your Medusa server ## Starting your Medusa server
@@ -64,7 +64,7 @@ cd my-medusa-server
medusa develop medusa develop
``` ```
If you ran the new command with the `--seed` flag you will already have products available in your store. To view these you can run the following command in your command line: If you ran the new command with the `--seed` flag, you will already have products available in your store. To view these, you can run the following command in your command line:
```shell ```shell
curl -X GET localhost:9000/store/products | python -m json.tool curl -X GET localhost:9000/store/products | python -m json.tool
@@ -78,19 +78,19 @@ Other options you could take are:
### Add a frontend to your server ### Add a frontend to your server
We have created two starters for you that can help you lay a foundation for your storefront. The starters work with your new server with minimal configuration simply clone the starters from here: We have created two starters for you that can help you lay a foundation for your storefront. The starters work with your new server with minimal configuration, simply clone the starters from here:
- [Nextjs Starter](https://github.com/medusajs/nextjs-starter-medusa) - [Nextjs Starter](https://github.com/medusajs/nextjs-starter-medusa)
- [Gatsby Starter](https://github.com/medusajs/gatsby-starter-medusa) - [Gatsby Starter](https://github.com/medusajs/gatsby-starter-medusa)
### Browse the API reference ### Browse the API reference
In the API reference docs you can find all the available requests that are exposed by your new Medusa server. Interacting with the API is the first step to creating truly unique experiences. In the API reference docs, you can find all the available requests that are exposed by your new Medusa server. Interacting with the API is the first step to creating truly unique experiences.
### Setup Stripe as a payment provider (Guide coming soon) ### Setup Stripe as a payment provider (Guide coming soon)
One of the first things you may want to do when building out your store would be to add a payment provider. Your starter project comes with a dummy payment provider that simply fakes payments being processed. In the real world you want a payment provider that can handle credit card information securely and make sure that funds are being transferred to your account. Stripe is one of the most popular payment providers and Medusa has an official plugin that you can easily install in your project. One of the first things you may want to do when building out your store would be to add a payment provider. Your starter project comes with a dummy payment provider that simply fakes payments being processed. In the real world, you want a payment provider that can handle credit card information securely and make sure that funds are being transferred to your account. Stripe is one of the most popular payment providers and Medusa has an official plugin that you can easily install in your project.
## Summary ## Summary
In this part of the tutorial we have setup your first Medusa project using the `medusa new` command. You have now reached a key milestone as you are ready to start building your Medusa store; from here there are no limits to how you can use Medusa as you can customize and extend the functionality of the core. In the next part of the tutorial we will be exploring how you can add custom services and endpoints to fit your exact needs. In this part of the tutorial, we have setup your first Medusa project using the `medusa new` command. You have now reached a key milestone as you are ready to start building your Medusa store; from here there are no limits to how you can use Medusa, as you can customize and extend the functionality of the core. In the next part of the tutorial, we will be exploring how you can add custom services and endpoints to fit your exact needs.
@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/discounts POST /admin/discounts fails if multiple types of resources are provided on update 1`] = `
Object {
"message": "Only one of products, product_types is allowed, Only one of product_types, products is allowed",
"type": "invalid_data",
}
`;
exports[`/admin/discounts POST /admin/discounts fails to add condition on rule with existing comb. of type and operator 1`] = `
Object {
"code": "invalid_request_error",
"message": "Discount Condition with operator 'in' and type 'products' already exist on a Discount Rule",
"type": "duplicate_error",
}
`;
@@ -0,0 +1,293 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/price-lists GET /admin/price-lists returns a price list by :id 1`] = `
Object {
"created_at": Any<String>,
"customer_groups": Array [],
"deleted_at": null,
"description": "Winter sale for VIP customers. 25% off selected items.",
"ends_at": "2022-07-31T00:00:00.000Z",
"id": Any<String>,
"name": "VIP winter sale",
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 100,
"min_quantity": 1,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 500,
"min_quantity": 101,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 50,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 1000,
"min_quantity": 501,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
],
"starts_at": "2022-07-01T00:00:00.000Z",
"status": "active",
"type": "sale",
"updated_at": Any<String>,
}
`;
exports[`/admin/price-lists POST /admin/price-lists/:id updates a price list 1`] = `
Object {
"created_at": Any<String>,
"customer_groups": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "customer-group-1",
"metadata": null,
"name": "vip-customers",
"updated_at": Any<String>,
},
],
"deleted_at": null,
"description": "Winter sale for our most loyal customers",
"ends_at": "2022-12-31T00:00:00.000Z",
"id": "pl_no_customer_groups",
"name": "Loyalty Reward - Winter Sale",
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 100,
"min_quantity": 1,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 500,
"min_quantity": 101,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 50,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 1000,
"min_quantity": 501,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 85,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": null,
"min_quantity": null,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant_1",
},
Object {
"amount": 10,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": null,
"min_quantity": null,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
],
"starts_at": "2022-09-01T00:00:00.000Z",
"status": "draft",
"type": "sale",
"updated_at": Any<String>,
}
`;
exports[`/admin/price-lists POST /admin/price-lists/:id updates the amount and currency of a price in the price list 1`] = `
Object {
"amount": 250,
"created_at": Any<String>,
"currency_code": "eur",
"deleted_at": null,
"id": "ma_test_1",
"max_quantity": 100,
"min_quantity": 1,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
}
`;
exports[`/admin/price-lists POST /admin/price-lists/:id/prices/batch Adds a batch of new prices to a price list overriding existing prices 1`] = `
Array [
Object {
"amount": 45,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 2000,
"min_quantity": 1001,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 35,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 3000,
"min_quantity": 2001,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 25,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 4000,
"min_quantity": 3001,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
]
`;
exports[`/admin/price-lists POST /admin/price-lists/:id/prices/batch Adds a batch of new prices to a price list without overriding existing prices 1`] = `
Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 100,
"min_quantity": 1,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 500,
"min_quantity": 101,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 50,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 1000,
"min_quantity": 501,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 45,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 2000,
"min_quantity": 1001,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 35,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 3000,
"min_quantity": 2001,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 25,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": 4000,
"min_quantity": 3001,
"price_list_id": "pl_no_customer_groups",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
]
`;
@@ -21,7 +21,7 @@ Array [
"handle": "test-product", "handle": "test-product",
"height": null, "height": null,
"hs_code": null, "hs_code": null,
"id": StringMatching /\\^test-\\*/, "id": "test-product",
"images": Array [ "images": Array [
Object { Object {
"created_at": Any<String>, "created_at": Any<String>,
@@ -78,6 +78,7 @@ Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": "test-barcode", "barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean", "ean": "test-ean",
@@ -103,15 +104,19 @@ Array [
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
"created_at": Any<String>, "created_at": Any<String>,
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": StringMatching /\\^test-price\\*/, "id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/, "variant_id": StringMatching /\\^test-variant\\*/,
}, },
@@ -127,6 +132,7 @@ Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": null, "barcode": null,
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean2", "ean": "test-ean2",
@@ -152,6 +158,7 @@ Array [
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -159,10 +166,13 @@ Array [
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": StringMatching /\\^test-price\\*/, "id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/, "variant_id": "test-variant_2",
}, },
], ],
"product_id": StringMatching /\\^test-\\*/, "product_id": StringMatching /\\^test-\\*/,
@@ -176,6 +186,7 @@ Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": "test-barcode 1", "barcode": "test-barcode 1",
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean1", "ean": "test-ean1",
@@ -201,6 +212,7 @@ Array [
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -208,8 +220,11 @@ Array [
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": StringMatching /\\^test-price\\*/, "id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/, "variant_id": StringMatching /\\^test-variant\\*/,
}, },
@@ -222,6 +237,60 @@ Array [
"weight": null, "weight": null,
"width": null, "width": null,
}, },
Object {
"allow_backorder": false,
"barcode": "test-barcode-sale",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean-sale",
"height": null,
"hs_code": null,
"id": "test-variant-sale",
"inventory_quantity": 10,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^test-variant-option\\*/,
"metadata": null,
"option_id": StringMatching /\\^test-opt\\*/,
"updated_at": Any<String>,
"value": "Default variant",
"variant_id": StringMatching /\\^test-variant\\*/,
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 1000,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price-sale",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/,
},
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku-sale",
"title": "Test variant",
"upc": "test-upc-sale",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
], ],
"weight": null, "weight": null,
"width": null, "width": null,
@@ -245,7 +314,7 @@ Array [
"handle": "test-product1", "handle": "test-product1",
"height": null, "height": null,
"hs_code": null, "hs_code": null,
"id": StringMatching /\\^test-\\*/, "id": "test-product1",
"images": Array [], "images": Array [],
"is_giftcard": false, "is_giftcard": false,
"length": null, "length": null,
@@ -283,6 +352,7 @@ Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": null, "barcode": null,
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean4", "ean": "test-ean4",
@@ -308,6 +378,7 @@ Array [
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -315,8 +386,11 @@ Array [
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": StringMatching /\\^test-price\\*/, "id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/, "variant_id": StringMatching /\\^test-variant\\*/,
}, },
@@ -332,6 +406,7 @@ Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": null, "barcode": null,
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean3", "ean": "test-ean3",
@@ -357,6 +432,7 @@ Array [
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -364,8 +440,11 @@ Array [
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": StringMatching /\\^test-price\\*/, "id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": "test-region", "region_id": "test-region",
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/, "variant_id": StringMatching /\\^test-variant\\*/,
}, },
@@ -382,39 +461,6 @@ Array [
"weight": null, "weight": null,
"width": null, "width": null,
}, },
Object {
"collection": Any<Object>,
"collection_id": "test-collection1",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product_filtering_3",
"height": null,
"hs_code": null,
"id": StringMatching /\\^test-\\*/,
"images": Array [],
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"options": Any<Array>,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"tags": Any<Array>,
"thumbnail": null,
"title": "Test product filtering 3",
"type": Any<Object>,
"type_id": "test-type",
"updated_at": Any<String>,
"variants": Any<Array>,
"weight": null,
"width": null,
},
Object { Object {
"collection": Any<Object>, "collection": Any<Object>,
"collection_id": "test-collection1", "collection_id": "test-collection1",
@@ -426,7 +472,7 @@ Array [
"handle": "test-product_filtering_1", "handle": "test-product_filtering_1",
"height": null, "height": null,
"hs_code": null, "hs_code": null,
"id": StringMatching /\\^test-\\*/, "id": "test-product_filtering_1",
"images": Array [], "images": Array [],
"is_giftcard": false, "is_giftcard": false,
"length": null, "length": null,
@@ -459,7 +505,7 @@ Array [
"handle": "test-product_filtering_2", "handle": "test-product_filtering_2",
"height": null, "height": null,
"hs_code": null, "hs_code": null,
"id": StringMatching /\\^test-\\*/, "id": "test-product_filtering_2",
"images": Array [], "images": Array [],
"is_giftcard": false, "is_giftcard": false,
"length": null, "length": null,
@@ -481,6 +527,39 @@ Array [
"weight": null, "weight": null,
"width": null, "width": null,
}, },
Object {
"collection": Any<Object>,
"collection_id": "test-collection1",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product_filtering_3",
"height": null,
"hs_code": null,
"id": "test-product_filtering_3",
"images": Array [],
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"options": Any<Array>,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"tags": Any<Array>,
"thumbnail": null,
"title": "Test product filtering 3",
"type": Any<Object>,
"type_id": "test-type",
"updated_at": Any<String>,
"variants": Any<Array>,
"weight": null,
"width": null,
},
] ]
`; `;
@@ -529,6 +608,7 @@ Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": null, "barcode": null,
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": null, "ean": null,
@@ -554,6 +634,7 @@ Array [
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -561,8 +642,11 @@ Array [
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": Any<String>, "id": Any<String>,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": StringMatching /\\^variant_\\*/, "variant_id": StringMatching /\\^variant_\\*/,
}, },
@@ -581,3 +665,326 @@ Array [
}, },
] ]
`; `;
exports[`/admin/products POST /admin/products creates a product 1`] = `
Object {
"collection": Object {
"created_at": Any<String>,
"deleted_at": null,
"handle": "test-collection",
"id": "test-collection",
"metadata": null,
"title": "Test collection",
"updated_at": Any<String>,
},
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test",
"height": null,
"hs_code": null,
"id": StringMatching /\\^prod_\\*/,
"images": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": Any<String>,
"metadata": null,
"updated_at": Any<String>,
"url": "test-image.png",
},
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": Any<String>,
"metadata": null,
"updated_at": Any<String>,
"url": "test-image-2.png",
},
],
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^opt_\\*/,
"metadata": null,
"product_id": StringMatching /\\^prod_\\*/,
"title": "size",
"updated_at": Any<String>,
},
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^opt_\\*/,
"metadata": null,
"product_id": StringMatching /\\^prod_\\*/,
"title": "color",
"updated_at": Any<String>,
},
],
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"tags": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": Any<String>,
"metadata": null,
"updated_at": Any<String>,
"value": "123",
},
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": Any<String>,
"metadata": null,
"updated_at": Any<String>,
"value": "456",
},
],
"thumbnail": "test-image.png",
"title": "Test",
"type": Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "test-type",
"metadata": null,
"updated_at": Any<String>,
"value": "test-type",
},
"type_id": "test-type",
"updated_at": Any<String>,
"variants": Array [
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
"height": null,
"hs_code": null,
"id": StringMatching /\\^variant_\\*/,
"inventory_quantity": 10,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^optval_\\*/,
"metadata": null,
"option_id": StringMatching /\\^opt_\\*/,
"updated_at": Any<String>,
"value": "large",
"variant_id": StringMatching /\\^variant_\\*/,
},
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^optval_\\*/,
"metadata": null,
"option_id": StringMatching /\\^opt_\\*/,
"updated_at": Any<String>,
"value": "green",
"variant_id": StringMatching /\\^variant_\\*/,
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": StringMatching /\\^ma_\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^variant_\\*/,
},
Object {
"amount": 45,
"created_at": Any<String>,
"currency_code": "eur",
"deleted_at": null,
"id": StringMatching /\\^ma_\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^variant_\\*/,
},
Object {
"amount": 30,
"created_at": Any<String>,
"currency_code": "dkk",
"deleted_at": null,
"id": StringMatching /\\^ma_\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^variant_\\*/,
},
],
"product_id": StringMatching /\\^prod_\\*/,
"sku": null,
"title": "Test variant",
"upc": null,
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
"weight": null,
"width": null,
}
`;
exports[`/admin/products POST /admin/products updates a product (update prices, tags, update status, delete collection, delete type, replaces images) 1`] = `
Object {
"collection": null,
"collection_id": null,
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product",
"height": null,
"hs_code": null,
"id": "test-product",
"images": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^img_\\*/,
"metadata": null,
"updated_at": Any<String>,
"url": "test-image-2.png",
},
],
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "test-option",
"metadata": null,
"product_id": "test-product",
"title": "test-option",
"updated_at": Any<String>,
},
],
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "published",
"subtitle": null,
"tags": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "tag1",
"metadata": null,
"updated_at": Any<String>,
"value": "123",
},
],
"thumbnail": "test-image-2.png",
"title": "Test product",
"type": Object {
"created_at": Any<String>,
"deleted_at": null,
"id": StringMatching /\\^ptyp_\\*/,
"metadata": null,
"updated_at": Any<String>,
"value": "test-type-2",
},
"type_id": StringMatching /\\^ptyp_\\*/,
"updated_at": Any<String>,
"variants": Array [
Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
"height": null,
"hs_code": null,
"id": "test-variant",
"inventory_quantity": 10,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "test-variant-option",
"metadata": null,
"option_id": "test-option",
"updated_at": Any<String>,
"value": "Default variant",
"variant_id": "test-variant",
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 75,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
],
"product_id": "test-product",
"sku": "test-sku",
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
"weight": null,
"width": null,
}
`;
@@ -0,0 +1,49 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/regions Remove region from country on delete successfully creates a region with countries from a previously deleted region 1`] = `
Object {
"deleted": true,
"id": "test-region",
"object": "region",
}
`;
exports[`/admin/regions Remove region from country on delete successfully creates a region with countries from a previously deleted region 2`] = `
Object {
"automatic_taxes": true,
"countries": Array [
Object {
"display_name": "United States",
"id": 236,
"iso_2": "us",
"iso_3": "usa",
"name": "UNITED STATES",
"num_code": 840,
"region_id": Any<String>,
},
],
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"fulfillment_providers": Array [
Object {
"id": "test-ful",
"is_installed": true,
},
],
"gift_cards_taxable": true,
"id": Any<String>,
"metadata": null,
"name": "World",
"payment_providers": Array [
Object {
"id": "test-pay",
"is_installed": true,
},
],
"tax_code": null,
"tax_provider_id": null,
"tax_rate": 0,
"updated_at": Any<String>,
}
`;
@@ -77,7 +77,7 @@ describe("/admin/customer-groups", () => {
}, },
}) })
.catch((err) => { .catch((err) => {
expect(err.response.status).toEqual(402) expect(err.response.status).toEqual(422)
expect(err.response.data.type).toEqual("duplicate_error") expect(err.response.data.type).toEqual("duplicate_error")
expect(err.response.data.message).toEqual( expect(err.response.data.message).toEqual(
"Key (name)=(vip-customers) already exists." "Key (name)=(vip-customers) already exists."
@@ -130,7 +130,7 @@ describe("/admin/customer-groups", () => {
.catch((error) => { .catch((error) => {
expect(error.response.data.type).toEqual("not_found") expect(error.response.data.type).toEqual("not_found")
expect(error.response.data.message).toEqual( expect(error.response.data.message).toEqual(
`CustomerGroup with ${id} was not found` `CustomerGroup with id ${id} was not found`
) )
}) })
}) })
@@ -193,6 +193,229 @@ describe("/admin/customer-groups", () => {
}) })
}) })
describe("GET /admin/customer-groups/{id}/customers", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await customerSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("lists customers in group and count", async () => {
const api = useApi()
const response = await api
.get("/admin/customer-groups/test-group-5/customers", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(3)
expect(response.data.customers).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-customer-5",
}),
expect.objectContaining({
id: "test-customer-6",
}),
expect.objectContaining({
id: "test-customer-7",
}),
])
)
})
})
describe("POST /admin/customer-groups/{id}/customers/batch", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await customerSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("adds multiple customers to a group", async () => {
const api = useApi()
const payload = {
customer_ids: [{ id: "test-customer-1" }, { id: "test-customer-2" }],
}
const batchAddResponse = await api.post(
"/admin/customer-groups/customer-group-1/customers/batch",
payload,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
expect(batchAddResponse.status).toEqual(200)
expect(batchAddResponse.data.customer_group).toEqual(
expect.objectContaining({
name: "vip-customers",
})
)
const getCustomerResponse = await api.get(
"/admin/customers?expand=groups",
{
headers: { Authorization: "Bearer test_token" },
}
)
expect(getCustomerResponse.data.customers).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-customer-1",
groups: [
expect.objectContaining({
name: "vip-customers",
id: "customer-group-1",
}),
],
}),
expect.objectContaining({
id: "test-customer-2",
groups: [
expect.objectContaining({
name: "vip-customers",
id: "customer-group-1",
}),
],
}),
])
)
})
it("presents a descriptive error when presented with a non-existing group", async () => {
expect.assertions(2)
const api = useApi()
const payload = {
customer_ids: [{ id: "test-customer-1" }, { id: "test-customer-2" }],
}
await api
.post(
"/admin/customer-groups/non-existing-customer-group-1/customers/batch",
payload,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
expect(err.response.data.type).toEqual("not_found")
expect(err.response.data.message).toEqual(
"CustomerGroup with id non-existing-customer-group-1 was not found"
)
})
})
it("adds customers to a group idempotently", async () => {
expect.assertions(3)
const api = useApi()
// add customer-1 to the customer group
const payload_1 = {
customer_ids: [{ id: "test-customer-1" }],
}
await api
.post(
"/admin/customer-groups/customer-group-1/customers/batch",
payload_1,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => console.log(err))
// re-adding customer-1 to the customer group along with new addintion:
// customer-2 and some non-existing customers should cause the request to fail
const payload_2 = {
customer_ids: [
{ id: "test-customer-1" },
{ id: "test-customer-27" },
{ id: "test-customer-28" },
{ id: "test-customer-2" },
],
}
await api
.post(
"/admin/customer-groups/customer-group-1/customers/batch",
payload_2,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
expect(err.response.data.type).toEqual("not_found")
expect(err.response.data.message).toEqual(
'The following customer ids do not exist: "test-customer-27, test-customer-28"'
)
})
// check that customer-1 is only added once and that customer-2 is added correctly
const getCustomerResponse = await api.get(
"/admin/customers?expand=groups",
{
headers: { Authorization: "Bearer test_token" },
}
)
expect(getCustomerResponse.data.customers).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-customer-1",
groups: [
expect.objectContaining({
name: "vip-customers",
id: "customer-group-1",
}),
],
}),
expect.objectContaining({
id: "test-customer-2",
groups: [],
}),
])
)
})
})
describe("POST /admin/customer-groups/:id", () => { describe("POST /admin/customer-groups/:id", () => {
beforeEach(async () => { beforeEach(async () => {
try { try {
@@ -290,6 +513,66 @@ describe("/admin/customer-groups", () => {
const db = useDb() const db = useDb()
await db.teardown() await db.teardown()
}) })
it("retreive a list of customer groups", async () => {
const api = useApi()
const response = await api
.get(
`/admin/customer-groups?limit=5&offset=2&expand=customers&order=created_at`,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch(console.log)
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(7)
expect(response.data.customer_groups.length).toEqual(5)
expect(response.data.customer_groups[0]).toEqual(
expect.objectContaining({ id: "customer-group-3" })
)
expect(response.data.customer_groups[0]).toHaveProperty("customers")
})
it("retreive a list of customer groups filtered by name using `q` param", async () => {
const api = useApi()
const response = await api.get(`/admin/customer-groups?q=vip-customers`, {
headers: {
Authorization: "Bearer test_token",
},
})
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(1)
expect(response.data.customer_groups).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: "customer-group-1" }),
])
)
expect(response.data.customer_groups[0]).not.toHaveProperty("customers")
})
})
describe("GET /admin/customer-groups/:id", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await customerSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("gets customer group", async () => { it("gets customer group", async () => {
const api = useApi() const api = useApi()
@@ -336,8 +619,6 @@ describe("/admin/customer-groups", () => {
}) })
it("throws error when a customer group doesn't exist", async () => { it("throws error when a customer group doesn't exist", async () => {
expect.assertions(3)
const api = useApi() const api = useApi()
const id = "test-group-000" const id = "test-group-000"
@@ -352,7 +633,7 @@ describe("/admin/customer-groups", () => {
expect(err.response.status).toEqual(404) expect(err.response.status).toEqual(404)
expect(err.response.data.type).toEqual("not_found") expect(err.response.data.type).toEqual("not_found")
expect(err.response.data.message).toEqual( expect(err.response.data.message).toEqual(
`CustomerGroup with ${id} was not found` `CustomerGroup with id ${id} was not found`
) )
}) })
}) })
@@ -75,6 +75,36 @@ describe("/admin/customers", () => {
) )
}) })
it("lists customers in group and count", async () => {
const api = useApi()
const response = await api
.get("/admin/customers?groups[]=test-group-5", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(3)
expect(response.data.customers).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-customer-5",
}),
expect.objectContaining({
id: "test-customer-6",
}),
expect.objectContaining({
id: "test-customer-7",
}),
])
)
})
it("lists customers with specific query", async () => { it("lists customers with specific query", async () => {
const api = useApi() const api = useApi()
@@ -229,6 +259,32 @@ describe("/admin/customers", () => {
) )
}) })
it("fails when adding a customer group which doesn't exist", async () => {
expect.assertions(3)
// Try adding a non existing group
const api = useApi()
await api
.post(
"/admin/customers/test-customer-3?expand=groups",
{
groups: [{ id: "fake-group-0" }],
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((error) => {
expect(error.response.status).toEqual(404)
expect(error.response.data.type).toEqual("not_found")
expect(error.response.data.message).toEqual(
"Customer_group with customer_group_id fake-group-0 does not exist."
)
})
})
it("Correctly updates customer groups", async () => { it("Correctly updates customer groups", async () => {
const api = useApi() const api = useApi()
let response = await api let response = await api
@@ -250,33 +306,10 @@ describe("/admin/customers", () => {
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.customer.groups).toEqual( expect(response.data.customer.groups).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ id: "test-group-4", name: "test-group-4" }), expect.objectContaining({
]) id: "test-group-4",
) name: "test-group-4",
}),
// Try adding a non existing group
response = await api
.post(
"/admin/customers/test-customer-3?expand=groups",
{
groups: [{ id: "test-group-4" }, { id: "fake-group-0" }],
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.customer.groups.length).toEqual(1)
expect(response.data.customer.groups).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: "test-group-4", name: "test-group-4" }),
]) ])
) )
@@ -323,8 +356,11 @@ describe("/admin/customers", () => {
expect(response.data.customer.groups.length).toEqual(2) expect(response.data.customer.groups.length).toEqual(2)
expect(response.data.customer.groups).toEqual( expect(response.data.customer.groups).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ id: "test-group-4", name: "test-group-4" }),
expect.objectContaining({ id: "test-group-5", name: "test-group-5" }), expect.objectContaining({ id: "test-group-5", name: "test-group-5" }),
expect.objectContaining({
id: "test-group-4",
name: "test-group-4",
}),
]) ])
) )
}) })
+594 -18
View File
@@ -1,5 +1,11 @@
const path = require("path") const path = require("path")
const { Region, DiscountRule, Discount } = require("@medusajs/medusa") const {
Region,
DiscountRule,
Discount,
Customer,
CustomerGroup,
} = require("@medusajs/medusa")
const setupServer = require("../../../helpers/setup-server") const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api") const { useApi } = require("../../../helpers/use-api")
@@ -7,6 +13,10 @@ const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder") const adminSeeder = require("../../helpers/admin-seeder")
const discountSeeder = require("../../helpers/discount-seeder") const discountSeeder = require("../../helpers/discount-seeder")
const { exportAllDeclaration } = require("@babel/types") const { exportAllDeclaration } = require("@babel/types")
const { simpleProductFactory } = require("../../factories")
const {
simpleDiscountFactory,
} = require("../../factories/simple-discount-factory")
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -26,6 +36,205 @@ describe("/admin/discounts", () => {
medusaProcess.kill() medusaProcess.kill()
}) })
describe("GET /admin/discounts/:id", () => {
beforeEach(async () => {
const manager = dbConnection.manager
await adminSeeder(dbConnection)
await manager.insert(DiscountRule, {
id: "test-discount-rule-fixed",
description: "Test discount rule",
type: "fixed",
value: 10,
allocation: "total",
})
const prod = await simpleProductFactory(dbConnection, { type: "pants" })
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "products",
operator: "in",
products: [prod.id],
},
{
type: "product_types",
operator: "not_in",
product_types: [prod.type_id],
},
],
},
})
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("should retrieve discount with customer conditions created with factory", async () => {
const api = useApi()
const group = await dbConnection.manager.insert(CustomerGroup, {
id: "customer-group-1",
name: "vip-customers",
})
await dbConnection.manager.insert(Customer, {
id: "cus_1234",
email: "oli@email.com",
groups: [group],
})
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "in",
customer_groups: ["customer-group-1"],
},
],
},
})
const response = await api
.get(
"/admin/discounts/test-discount?expand=rule,rule.conditions,rule.conditions.customer_groups",
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
const disc = response.data.discount
expect(response.status).toEqual(200)
expect(disc).toEqual(
expect.objectContaining({
id: "test-discount",
code: "TEST",
})
)
expect(disc.rule.conditions).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "customer_groups",
operator: "in",
discount_rule_id: disc.rule.id,
}),
])
)
})
it("should retrieve discount and only select the id field and retrieve the relation parent_discount", async () => {
const api = useApi()
const group = await dbConnection.manager.insert(CustomerGroup, {
id: "customer-group-1",
name: "vip-customers",
})
await dbConnection.manager.insert(Customer, {
id: "cus_1234",
email: "oli@email.com",
groups: [group],
})
await simpleDiscountFactory(dbConnection, {
id: "test-discount",
code: "TEST",
rule: {
type: "percentage",
value: "10",
allocation: "total",
conditions: [
{
type: "customer_groups",
operator: "in",
customer_groups: ["customer-group-1"],
},
],
},
})
const response = await api
.get(
"/admin/discounts/test-discount?fields=id&expand=parent_discount",
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
const disc = response.data.discount
expect(response.status).toEqual(200)
expect(disc).toEqual({
id: "test-discount",
parent_discount: null,
})
})
it("should retrieve discount with product conditions created with factory", async () => {
const api = useApi()
const response = await api
.get(
"/admin/discounts/test-discount?expand=rule,rule.conditions,rule.conditions.products,rule.conditions.product_types",
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
const disc = response.data.discount
expect(response.status).toEqual(200)
expect(disc).toEqual(
expect.objectContaining({
id: "test-discount",
code: "TEST",
})
)
expect(disc.rule.conditions).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "products",
operator: "in",
discount_rule_id: disc.rule.id,
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
discount_rule_id: disc.rule.id,
}),
])
)
})
})
describe("GET /admin/discounts", () => { describe("GET /admin/discounts", () => {
beforeEach(async () => { beforeEach(async () => {
const manager = dbConnection.manager const manager = dbConnection.manager
@@ -267,27 +476,394 @@ describe("/admin/discounts", () => {
usage_limit: 10, usage_limit: 10,
}) })
) )
})
const test = await api.get( it("creates a discount with conditions", async () => {
`/admin/discounts/${response.data.discount.id}`, const api = useApi()
{ headers: { Authorization: "Bearer test_token" } }
)
expect(test.status).toEqual(200) const product = await simpleProductFactory(dbConnection, {
expect(test.data.discount).toEqual( type: "pants",
expect.objectContaining({ tags: ["ss22"],
code: "HELLOWORLD", })
usage_limit: 10,
rule: expect.objectContaining({ const anotherProduct = await simpleProductFactory(dbConnection, {
value: 10, type: "blouses",
type: "percentage", tags: ["ss23"],
description: "test", })
allocation: "total",
}), const response = await api
.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
{
products: [anotherProduct.id],
operator: "not_in",
},
{
product_types: [product.type_id],
operator: "not_in",
},
{
product_types: [anotherProduct.type_id],
operator: "in",
},
{
product_tags: [product.tags[0].id],
operator: "not_in",
},
{
product_tags: [anotherProduct.tags[0].id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
}) })
expect(response.status).toEqual(200)
expect(response.data.discount.rule.conditions).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "products",
operator: "in",
}),
expect.objectContaining({
type: "products",
operator: "not_in",
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
}),
expect.objectContaining({
type: "product_types",
operator: "in",
}),
expect.objectContaining({
type: "product_tags",
operator: "not_in",
}),
expect.objectContaining({
type: "product_tags",
operator: "in",
}),
])
) )
}) })
it("creates a discount with conditions and updates said conditions", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "pants",
})
const response = await api
.post(
"/admin/discounts?expand=rule,rule.conditions",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
{
product_types: [product.type_id],
operator: "not_in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.discount.rule.conditions).toEqual([
expect.objectContaining({
type: "products",
operator: "in",
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
}),
])
const createdRule = response.data.discount.rule
const condsToUpdate = createdRule.conditions[0]
const updated = await api
.post(
`/admin/discounts/${response.data.discount.id}?expand=rule,rule.conditions,rule.conditions.products`,
{
rule: {
id: createdRule.id,
type: createdRule.type,
value: createdRule.value,
allocation: createdRule.allocation,
conditions: [
{
id: condsToUpdate.id,
products: [product.id, anotherProduct.id],
},
],
},
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(updated.status).toEqual(200)
expect(updated.data.discount.rule.conditions).toEqual([
expect.objectContaining({
type: "products",
operator: "in",
products: expect.arrayContaining([
expect.objectContaining({
id: product.id,
}),
expect.objectContaining({
id: anotherProduct.id,
}),
]),
}),
expect.objectContaining({
type: "product_types",
operator: "not_in",
}),
])
})
it("fails to add condition on rule with existing comb. of type and operator", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "pants",
})
const response = await api
.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
const createdRule = response.data.discount.rule
await expect(
api
.post(
`/admin/discounts/${response.data.discount.id}?expand=rule,rule.conditions,rule.conditions.products`,
{
rule: {
id: createdRule.id,
type: createdRule.type,
value: createdRule.value,
allocation: createdRule.allocation,
conditions: [
{
products: [anotherProduct.id],
operator: "in",
},
],
},
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => err.response.data)
).resolves.toMatchSnapshot()
})
it("fails if multiple types of resources are provided on create", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
try {
await api.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
product_types: [product.type_id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
} catch (error) {
expect(error.response.data.type).toEqual("invalid_data")
expect(error.response.data.message).toEqual(
"Only one of products, product_types is allowed, Only one of product_types, products is allowed"
)
}
})
it("fails if multiple types of resources are provided on update", async () => {
const api = useApi()
const product = await simpleProductFactory(dbConnection, {
type: "pants",
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "pants",
})
const response = await api
.post(
"/admin/discounts",
{
code: "HELLOWORLD",
rule: {
description: "test",
type: "percentage",
value: 10,
allocation: "total",
conditions: [
{
products: [product.id],
operator: "in",
},
],
},
usage_limit: 10,
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
const createdRule = response.data.discount.rule
await expect(
api
.post(
`/admin/discounts/${response.data.discount.id}?expand=rule,rule.conditions,rule.conditions.products`,
{
rule: {
id: createdRule.id,
type: createdRule.type,
value: createdRule.value,
allocation: createdRule.allocation,
conditions: [
{
products: [anotherProduct.id],
product_types: [product.type_id],
operator: "in",
},
],
},
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => err.response.data)
).resolves.toMatchSnapshot()
})
it("creates a discount and updates it", async () => { it("creates a discount and updates it", async () => {
const api = useApi() const api = useApi()
@@ -882,8 +1458,8 @@ describe("/admin/discounts", () => {
} }
) )
} catch (error) { } catch (error) {
expect(error.response.data.message).toMatch( expect(error.response.data.message).toEqual(
/duplicate key value violates unique constraint/i "Discount with code TESTING already exists."
) )
} }
}) })
@@ -297,6 +297,62 @@ describe("/admin/draft-orders", () => {
) )
}) })
it("creates a draft order with discount and line item", async () => {
const api = useApi()
const payload = {
email: "oli@test.dk",
shipping_address: "oli-shipping",
discounts: [{ code: "TEST" }],
items: [
{
variant_id: "test-variant",
quantity: 2,
metadata: {},
},
],
region_id: "test-region",
customer_id: "oli-test",
shipping_methods: [
{
option_id: "test-option",
},
],
}
const response = await api
.post("/admin/draft-orders", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const draftOrder = response.data.draft_order
const lineItemId = draftOrder.cart.items[0].id
expect(response.status).toEqual(200)
expect(draftOrder.cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
variant_id: "test-variant",
unit_price: 8000,
quantity: 2,
adjustments: expect.arrayContaining([
expect.objectContaining({
item_id: lineItemId,
amount: 1600,
description: "discount",
discount_id: "test-discount",
}),
]),
}),
])
)
})
it("creates a draft order with created shipping address", async () => { it("creates a draft order with created shipping address", async () => {
const api = useApi() const api = useApi()
@@ -1555,6 +1555,116 @@ describe("/admin/orders", () => {
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
}) })
describe("Given an existing discount order", () => {
describe("When a store operator attemps to create a swap form the discount order", () => {
it("Then should successfully create the swap", async () => {
const api = useApi()
const response = await api.post(
"/admin/orders/test-order/swaps",
{
return_items: [
{
item_id: "test-item",
quantity: 1,
},
],
additional_items: [{ variant_id: "test-variant-2", quantity: 1 }],
},
{
headers: {
authorization: "Bearer test_token",
},
}
)
const swapCartId = response.data.order.swaps[0].cart_id
const swapCartRes = await api.get(`/store/carts/${swapCartId}`, {
headers: {
authorization: "Bearer test_token",
},
})
const cart = swapCartRes.data.cart
expect(response.status).toEqual(200)
expect(cart.items.length).toEqual(2)
expect(cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
unit_price: -8000,
adjustments: [
expect.objectContaining({
amount: -800,
}),
],
}),
expect.objectContaining({
unit_price: 8000,
adjustments: [
expect.objectContaining({
amount: 800,
}),
],
}),
])
)
expect(cart.total).toEqual(0)
})
})
describe("And given a swap cart", () => {
describe("When a line item is added to the swap cart", () => {
it("Then should not delete existing return line item adjustments", async () => {
const api = useApi()
const createSwapRes = await api.post(
"/admin/orders/test-order/swaps",
{
return_items: [
{
item_id: "test-item",
quantity: 1,
},
],
additional_items: [{ variant_id: "test-variant", quantity: 1 }],
},
{
headers: {
authorization: "Bearer test_token",
},
}
)
const swapCartId = createSwapRes.data.order.swaps[0].cart_id
const response = await api.post(
`/store/carts/${swapCartId}/line-items`,
{
variant_id: "test-variant-2",
quantity: 1,
},
{
headers: {
authorization: "Bearer test_token",
},
}
)
const cart = response.data.cart
const items = cart.items
const [returnItem] = items.filter((i) => i.is_return)
expect(returnItem.adjustments).toEqual([
expect.objectContaining({
amount: -800,
}),
])
expect(cart.total).toBe(7200)
})
})
})
})
it("creates a swap with custom shipping options", async () => { it("creates a swap with custom shipping options", async () => {
const api = useApi() const api = useApi()
@@ -0,0 +1,854 @@
const path = require("path")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { useDb, initDb } = require("../../../helpers/use-db")
const {
simpleProductFactory,
simplePriceListFactory,
} = require("../../factories")
const adminSeeder = require("../../helpers/admin-seeder")
const customerSeeder = require("../../helpers/customer-seeder")
const priceListSeeder = require("../../helpers/price-list-seeder")
const productSeeder = require("../../helpers/product-seeder")
jest.setTimeout(30000)
describe("/admin/price-lists", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
describe("POST /admin/price-list", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await customerSeeder(dbConnection)
await productSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("creates a price list", async () => {
const api = useApi()
const payload = {
name: "VIP Summer sale",
description: "Summer sale for VIP customers. 25% off selected items.",
type: "sale",
status: "active",
starts_at: "2022-07-01T00:00:00.000Z",
ends_at: "2022-07-31T00:00:00.000Z",
customer_groups: [
{
id: "customer-group-1",
},
],
prices: [
{
amount: 85,
currency_code: "usd",
variant_id: "test-variant",
},
],
}
const response = await api
.post("/admin/price-lists", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.price_list).toEqual(
expect.objectContaining({
id: expect.any(String),
name: "VIP Summer sale",
description: "Summer sale for VIP customers. 25% off selected items.",
type: "sale",
status: "active",
starts_at: "2022-07-01T00:00:00.000Z",
ends_at: "2022-07-31T00:00:00.000Z",
customer_groups: [
expect.objectContaining({
id: "customer-group-1",
}),
],
prices: [
expect.objectContaining({
id: expect.any(String),
amount: 85,
currency_code: "usd",
variant_id: "test-variant",
}),
],
})
)
})
})
describe("GET /admin/price-lists", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await productSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("returns a price list by :id", async () => {
const api = useApi()
const response = await api
.get("/admin/price-lists/pl_no_customer_groups", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.price_list).toMatchSnapshot({
id: expect.any(String),
name: "VIP winter sale",
description: "Winter sale for VIP customers. 25% off selected items.",
type: "sale",
status: "active",
starts_at: "2022-07-01T00:00:00.000Z",
ends_at: "2022-07-31T00:00:00.000Z",
prices: [
{
id: expect.any(String),
amount: 100,
currency_code: "usd",
min_quantity: 1,
max_quantity: 100,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
amount: 80,
currency_code: "usd",
min_quantity: 101,
max_quantity: 500,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
amount: 50,
currency_code: "usd",
min_quantity: 501,
max_quantity: 1000,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
created_at: expect.any(String),
updated_at: expect.any(String),
})
})
it("returns a list of price lists", async () => {
const api = useApi()
const response = await api
.get("/admin/price-lists", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.price_lists).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "pl_no_customer_groups",
}),
])
)
})
})
describe("POST /admin/price-lists/:id", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await customerSeeder(dbConnection)
await productSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("updates a price list", async () => {
const api = useApi()
const payload = {
name: "Loyalty Reward - Winter Sale",
description: "Winter sale for our most loyal customers",
type: "sale",
status: "draft",
starts_at: "2022-09-01T00:00:00.000Z",
ends_at: "2022-12-31T00:00:00.000Z",
customer_groups: [
{
id: "customer-group-1",
},
],
prices: [
{
amount: 85,
currency_code: "usd",
variant_id: "test-variant_1",
},
{
amount: 10,
currency_code: "usd",
variant_id: "test-variant",
},
],
}
const response = await api
.post("/admin/price-lists/pl_no_customer_groups", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.price_list).toMatchSnapshot({
id: "pl_no_customer_groups",
name: "Loyalty Reward - Winter Sale",
description: "Winter sale for our most loyal customers",
type: "sale",
status: "draft",
starts_at: "2022-09-01T00:00:00.000Z",
ends_at: "2022-12-31T00:00:00.000Z",
prices: [
{
id: expect.any(String),
amount: 100,
currency_code: "usd",
min_quantity: 1,
max_quantity: 100,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
region_id: null,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
amount: 80,
currency_code: "usd",
min_quantity: 101,
max_quantity: 500,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
region_id: null,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
amount: 50,
currency_code: "usd",
min_quantity: 501,
max_quantity: 1000,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
region_id: null,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
amount: 85,
currency_code: "usd",
variant_id: "test-variant_1",
price_list_id: "pl_no_customer_groups",
min_quantity: null,
max_quantity: null,
region_id: null,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
},
{
id: expect.any(String),
amount: 10,
currency_code: "usd",
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
min_quantity: null,
max_quantity: null,
region_id: null,
created_at: expect.any(String),
updated_at: expect.any(String),
deleted_at: null,
},
],
customer_groups: [
{
id: "customer-group-1",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
created_at: expect.any(String),
updated_at: expect.any(String),
})
})
it("updates the amount and currency of a price in the price list", async () => {
const api = useApi()
const payload = {
prices: [
{
id: "ma_test_1",
amount: 250,
currency_code: "eur",
variant_id: "test-variant",
},
],
}
const response = await api
.post("/admin/price-lists/pl_no_customer_groups", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
const updatedPrice = response.data.price_list.prices.find(
(p) => p.id === "ma_test_1"
)
expect(updatedPrice).toMatchSnapshot({
id: "ma_test_1",
amount: 250,
currency_code: "eur",
min_quantity: 1,
max_quantity: 100,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
region_id: null,
created_at: expect.any(String),
updated_at: expect.any(String),
})
})
})
describe("POST /admin/price-lists/:id/prices/batch", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await productSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Adds a batch of new prices to a price list without overriding existing prices", async () => {
const api = useApi()
const payload = {
prices: [
{
amount: 45,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 1001,
max_quantity: 2000,
},
{
amount: 35,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 2001,
max_quantity: 3000,
},
{
amount: 25,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 3001,
max_quantity: 4000,
},
],
}
const response = await api
.post(
"/admin/price-lists/pl_no_customer_groups/prices/batch",
payload,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.price_list.prices.length).toEqual(6)
expect(response.data.price_list.prices).toMatchSnapshot([
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 100,
currency_code: "usd",
min_quantity: 1,
max_quantity: 100,
variant_id: "test-variant",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 80,
currency_code: "usd",
min_quantity: 101,
max_quantity: 500,
variant_id: "test-variant",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 50,
currency_code: "usd",
min_quantity: 501,
max_quantity: 1000,
variant_id: "test-variant",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 45,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 1001,
max_quantity: 2000,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 35,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 2001,
max_quantity: 3000,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 25,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 3001,
max_quantity: 4000,
created_at: expect.any(String),
updated_at: expect.any(String),
},
])
})
it("Adds a batch of new prices to a price list overriding existing prices", async () => {
const api = useApi()
const payload = {
prices: [
{
amount: 45,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 1001,
max_quantity: 2000,
},
{
amount: 35,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 2001,
max_quantity: 3000,
},
{
amount: 25,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 3001,
max_quantity: 4000,
},
],
override: true,
}
const response = await api
.post(
"/admin/price-lists/pl_no_customer_groups/prices/batch",
payload,
{
headers: {
Authorization: "Bearer test_token",
},
}
)
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.price_list.prices.length).toEqual(3)
expect(response.data.price_list.prices).toMatchSnapshot([
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 45,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 1001,
max_quantity: 2000,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 35,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 2001,
max_quantity: 3000,
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
price_list_id: "pl_no_customer_groups",
amount: 25,
currency_code: "usd",
variant_id: "test-variant",
min_quantity: 3001,
max_quantity: 4000,
created_at: expect.any(String),
updated_at: expect.any(String),
},
])
})
})
describe("DELETE /admin/price-lists/:id", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await productSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Deletes a price list", async () => {
const api = useApi()
const response = await api
.delete("/admin/price-lists/pl_no_customer_groups", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data).toEqual({
id: "pl_no_customer_groups",
object: "price-list",
deleted: true,
})
})
})
describe("tests cascade on delete", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await productSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Deletes a variant and ensures that prices associated with the variant are deleted from PriceList", async () => {
const api = useApi()
await api
.delete("/admin/products/test-product/variants/test-variant", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
const response = await api.get(
"/admin/price-lists/pl_no_customer_groups",
{
headers: {
Authorization: "Bearer test_token",
},
}
)
expect(response.status).toEqual(200)
expect(response.data.price_list.prices.length).toEqual(0)
})
})
describe("DELETE /admin/price-lists/:id/prices/batch", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await productSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Deletes several prices associated with a price list", async () => {
const api = useApi()
const response = await api
.delete("/admin/price-lists/pl_no_customer_groups/prices/batch", {
headers: {
Authorization: "Bearer test_token",
},
data: {
price_ids: ["ma_test_1", "ma_test_2"],
},
})
.catch((err) => {
console.warn(err.response.data)
})
const getPriceListResponse = await api
.get("/admin/price-lists/pl_no_customer_groups", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data).toEqual({
ids: ["ma_test_1", "ma_test_2"],
object: "money-amount",
deleted: true,
})
expect(getPriceListResponse.data.price_list.prices.length).toEqual(1)
expect(getPriceListResponse.data.price_list.prices[0].id).toEqual(
"ma_test_3"
)
})
})
describe("GET /admin/price-lists/:id/products", () => {
let tag
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await simpleProductFactory(
dbConnection,
{
id: "test-prod-1",
title: "MedusaHeadphones",
variants: [{ id: "test-variant-1" }, { id: "test-variant-2" }],
},
1
)
const prod = await simpleProductFactory(
dbConnection,
{
id: "test-prod-2",
tags: ["test-tag"],
variants: [{ id: "test-variant-3" }, { id: "test-variant-4" }],
},
2
)
tag = prod.tags[0].id
await simpleProductFactory(
dbConnection,
{
id: "test-prod-3",
variants: [{ id: "test-variant-5" }],
},
3
)
await simplePriceListFactory(dbConnection, {
id: "test-list",
prices: [
{ variant_id: "test-variant-1", currency_code: "usd", amount: 100 },
{ variant_id: "test-variant-4", currency_code: "usd", amount: 100 },
],
})
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("lists only product 1, 2", async () => {
const api = useApi()
const response = await api
.get(`/admin/price-lists/test-list/products?order=-created_at`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(2)
expect(response.data.products).toEqual([
expect.objectContaining({ id: "test-prod-1" }),
expect.objectContaining({ id: "test-prod-2" }),
])
})
it("lists only product 2", async () => {
const api = useApi()
const response = await api
.get(`/admin/price-lists/test-list/products?tags[]=${tag}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.warn(err.response.data)
})
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(1)
expect(response.data.products).toEqual([
expect.objectContaining({ id: "test-prod-2" }),
])
})
})
})
+378 -176
View File
@@ -7,6 +7,7 @@ const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder") const adminSeeder = require("../../helpers/admin-seeder")
const productSeeder = require("../../helpers/product-seeder") const productSeeder = require("../../helpers/product-seeder")
const { ProductVariant } = require("@medusajs/medusa") const { ProductVariant } = require("@medusajs/medusa")
const priceListSeeder = require("../../helpers/price-list-seeder")
jest.setTimeout(50000) jest.setTimeout(50000)
@@ -508,6 +509,50 @@ describe("/admin/products", () => {
]) ])
}) })
it("returns a list of products not containing a giftcard in list", async () => {
const api = useApi()
const payload = {
title: "Test Giftcard",
is_giftcard: true,
description: "test-giftcard-description",
options: [{ title: "Denominations" }],
variants: [
{
title: "Test variant",
prices: [{ currency_code: "usd", amount: 100 }],
options: [{ value: "100" }],
},
],
}
await api
.post("/admin/products", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const response = await api
.get("/admin/products?is_giftcard=false", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.data.products).toEqual(
expect.not.arrayContaining([
expect.objectContaining({ is_giftcard: true }),
])
)
})
it("returns a list of products with child entities", async () => { it("returns a list of products with child entities", async () => {
const api = useApi() const api = useApi()
@@ -521,10 +566,13 @@ describe("/admin/products", () => {
console.log(err) console.log(err)
}) })
response.data.products.sort((a, b) =>
a.created_at > b.created_at ? 1 : -1
)
expect(response.data.products).toMatchSnapshot([ expect(response.data.products).toMatchSnapshot([
{ {
id: expect.stringMatching(/^test-*/), id: "test-product",
created_at: expect.any(String),
options: [ options: [
{ {
id: expect.stringMatching(/^test-*/), id: expect.stringMatching(/^test-*/),
@@ -542,7 +590,53 @@ describe("/admin/products", () => {
], ],
variants: [ variants: [
{ {
id: "test-variant", // expect.stringMatching(/^test-variant*/), id: "test-variant",
created_at: expect.any(String),
updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/),
prices: [
{
id: "test-price",
variant_id: expect.stringMatching(/^test-variant*/),
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
options: [
{
id: expect.stringMatching(/^test-variant-option*/),
variant_id: expect.stringMatching(/^test-variant*/),
option_id: expect.stringMatching(/^test-opt*/),
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
},
{
id: "test-variant_2",
created_at: expect.any(String),
updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/),
prices: [
{
id: expect.stringMatching(/^test-price*/),
variant_id: "test-variant_2",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
options: [
{
id: expect.stringMatching(/^test-variant-option*/),
variant_id: expect.stringMatching(/^test-variant*/),
option_id: expect.stringMatching(/^test-opt*/),
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
},
{
id: "test-variant_1",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/), product_id: expect.stringMatching(/^test-*/),
@@ -565,36 +659,13 @@ describe("/admin/products", () => {
], ],
}, },
{ {
id: "test-variant_2", // expect.stringMatching(/^test-variant*/), id: "test-variant-sale",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/), product_id: expect.stringMatching(/^test-*/),
prices: [ prices: [
{ {
id: expect.stringMatching(/^test-price*/), id: "test-price-sale",
variant_id: expect.stringMatching(/^test-variant*/),
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
options: [
{
id: expect.stringMatching(/^test-variant-option*/),
variant_id: expect.stringMatching(/^test-variant*/),
option_id: expect.stringMatching(/^test-opt*/),
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
},
{
id: "test-variant_1", // expect.stringMatching(/^test-variant*/),
created_at: expect.any(String),
updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/),
prices: [
{
id: expect.stringMatching(/^test-price*/),
variant_id: expect.stringMatching(/^test-variant*/), variant_id: expect.stringMatching(/^test-variant*/),
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
@@ -633,12 +704,12 @@ describe("/admin/products", () => {
updated_at: expect.any(String), updated_at: expect.any(String),
}, },
{ {
id: expect.stringMatching(/^test-*/), id: "test-product1",
created_at: expect.any(String), created_at: expect.any(String),
options: [], options: [],
variants: [ variants: [
{ {
id: "test-variant_4", // expect.stringMatching(/^test-variant*/), id: "test-variant_4",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/), product_id: expect.stringMatching(/^test-*/),
@@ -661,7 +732,7 @@ describe("/admin/products", () => {
], ],
}, },
{ {
id: "test-variant_3", // expect.stringMatching(/^test-variant*/), id: "test-variant_3",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/), product_id: expect.stringMatching(/^test-*/),
@@ -702,11 +773,10 @@ describe("/admin/products", () => {
updated_at: expect.any(String), updated_at: expect.any(String),
}, },
profile_id: expect.stringMatching(/^sp_*/), profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
}, },
{ {
id: expect.stringMatching(/^test-*/), id: "test-product_filtering_1",
profile_id: expect.stringMatching(/^sp_*/), profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String), created_at: expect.any(String),
type: expect.any(Object), type: expect.any(Object),
@@ -717,7 +787,7 @@ describe("/admin/products", () => {
updated_at: expect.any(String), updated_at: expect.any(String),
}, },
{ {
id: expect.stringMatching(/^test-*/), id: "test-product_filtering_2",
profile_id: expect.stringMatching(/^sp_*/), profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String), created_at: expect.any(String),
type: expect.any(Object), type: expect.any(Object),
@@ -728,7 +798,7 @@ describe("/admin/products", () => {
updated_at: expect.any(String), updated_at: expect.any(String),
}, },
{ {
id: expect.stringMatching(/^test-*/), id: "test-product_filtering_3",
profile_id: expect.stringMatching(/^sp_*/), profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String), created_at: expect.any(String),
type: expect.any(Object), type: expect.any(Object),
@@ -773,7 +843,20 @@ describe("/admin/products", () => {
{ {
title: "Test variant", title: "Test variant",
inventory_quantity: 10, inventory_quantity: 10,
prices: [{ currency_code: "usd", amount: 100 }], prices: [
{
currency_code: "usd",
amount: 100,
},
{
currency_code: "eur",
amount: 45,
},
{
currency_code: "dkk",
amount: 30,
},
],
options: [{ value: "large" }, { value: "green" }], options: [{ value: "large" }, { value: "green" }],
}, },
], ],
@@ -790,66 +873,126 @@ describe("/admin/products", () => {
}) })
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.product).toEqual( expect(response.data.product).toMatchSnapshot({
expect.objectContaining({ id: expect.stringMatching(/^prod_*/),
title: "Test", title: "Test",
discountable: true, discountable: true,
is_giftcard: false, is_giftcard: false,
handle: "test", handle: "test",
status: "draft", status: "draft",
images: expect.arrayContaining([ created_at: expect.any(String),
expect.objectContaining({ updated_at: expect.any(String),
url: "test-image.png", profile_id: expect.stringMatching(/^sp_*/),
}), images: [
expect.objectContaining({ {
url: "test-image-2.png", id: expect.any(String),
}), url: "test-image.png",
]), created_at: expect.any(String),
thumbnail: "test-image.png", updated_at: expect.any(String),
tags: [ },
expect.objectContaining({ {
value: "123", id: expect.any(String),
}), url: "test-image-2.png",
expect.objectContaining({ created_at: expect.any(String),
value: "456", updated_at: expect.any(String),
}), },
], ],
type: expect.objectContaining({ thumbnail: "test-image.png",
value: "test-type", tags: [
}), {
collection: expect.objectContaining({ id: expect.any(String),
id: "test-collection", value: "123",
title: "Test collection", created_at: expect.any(String),
}), updated_at: expect.any(String),
options: [ },
expect.objectContaining({ {
title: "size", id: expect.any(String),
}), value: "456",
expect.objectContaining({ created_at: expect.any(String),
title: "color", updated_at: expect.any(String),
}), },
], ],
variants: [ type: {
expect.objectContaining({ value: "test-type",
title: "Test variant", created_at: expect.any(String),
prices: [ updated_at: expect.any(String),
expect.objectContaining({ },
currency_code: "usd", collection: {
amount: 100, id: "test-collection",
}), title: "Test collection",
], created_at: expect.any(String),
options: [ updated_at: expect.any(String),
expect.objectContaining({ },
value: "large", options: [
}), {
expect.objectContaining({ id: expect.stringMatching(/^opt_*/),
value: "green", product_id: expect.stringMatching(/^prod_*/),
}), title: "size",
], created_at: expect.any(String),
}), updated_at: expect.any(String),
], },
}) {
) id: expect.stringMatching(/^opt_*/),
product_id: expect.stringMatching(/^prod_*/),
title: "color",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
variants: [
{
id: expect.stringMatching(/^variant_*/),
product_id: expect.stringMatching(/^prod_*/),
updated_at: expect.any(String),
created_at: expect.any(String),
title: "Test variant",
prices: [
{
id: expect.stringMatching(/^ma_*/),
currency_code: "usd",
amount: 100,
created_at: expect.any(String),
updated_at: expect.any(String),
variant_id: expect.stringMatching(/^variant_*/),
},
{
id: expect.stringMatching(/^ma_*/),
currency_code: "eur",
amount: 45,
created_at: expect.any(String),
updated_at: expect.any(String),
variant_id: expect.stringMatching(/^variant_*/),
},
{
id: expect.stringMatching(/^ma_*/),
currency_code: "dkk",
amount: 30,
created_at: expect.any(String),
updated_at: expect.any(String),
variant_id: expect.stringMatching(/^variant_*/),
},
],
options: [
{
value: "large",
created_at: expect.any(String),
updated_at: expect.any(String),
variant_id: expect.stringMatching(/^variant_*/),
option_id: expect.stringMatching(/^opt_*/),
id: expect.stringMatching(/^optval_*/),
},
{
value: "green",
created_at: expect.any(String),
updated_at: expect.any(String),
variant_id: expect.stringMatching(/^variant_*/),
option_id: expect.stringMatching(/^opt_*/),
id: expect.stringMatching(/^optval_*/),
},
],
},
],
})
}) })
it("creates a product that is not discountable", async () => { it("creates a product that is not discountable", async () => {
@@ -1006,8 +1149,7 @@ describe("/admin/products", () => {
prices: [ prices: [
{ {
currency_code: "usd", currency_code: "usd",
amount: 100, amount: 75,
sale_amount: 75,
}, },
], ],
}, },
@@ -1030,36 +1172,92 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.product).toEqual( expect(response.data.product).toMatchSnapshot({
expect.objectContaining({ id: "test-product",
images: expect.arrayContaining([ created_at: expect.any(String),
expect.objectContaining({ description: "test-product-description",
url: "test-image-2.png", discountable: true,
}), handle: "test-product",
]), images: [
thumbnail: "test-image-2.png", {
tags: [ created_at: expect.any(String),
expect.objectContaining({ deleted_at: null,
value: "123", id: expect.stringMatching(/^img_*/),
}), metadata: null,
], updated_at: expect.any(String),
variants: [ url: "test-image-2.png",
expect.objectContaining({ },
prices: [ ],
expect.objectContaining({ is_giftcard: false,
sale_amount: 75, options: [
amount: 100, {
}), created_at: expect.any(String),
], id: "test-option",
}), product_id: "test-product",
], title: "test-option",
status: "published", updated_at: expect.any(String),
collection: null, },
type: expect.objectContaining({ ],
value: "test-type-2", profile_id: expect.stringMatching(/^sp_*/),
}), status: "published",
}) tags: [
) {
created_at: expect.any(String),
id: "tag1",
updated_at: expect.any(String),
value: "123",
},
],
thumbnail: "test-image-2.png",
title: "Test product",
type: {
created_at: expect.any(String),
id: expect.stringMatching(/^ptyp_*/),
updated_at: expect.any(String),
value: "test-type-2",
},
type_id: expect.stringMatching(/^ptyp_*/),
updated_at: expect.any(String),
variants: [
{
allow_backorder: false,
barcode: "test-barcode",
created_at: expect.any(String),
ean: "test-ean",
id: "test-variant",
inventory_quantity: 10,
manage_inventory: true,
options: [
{
created_at: expect.any(String),
deleted_at: null,
id: "test-variant-option",
metadata: null,
option_id: "test-option",
updated_at: expect.any(String),
value: "Default variant",
variant_id: "test-variant",
},
],
origin_country: null,
prices: [
{
amount: 75,
created_at: expect.any(String),
currency_code: "usd",
id: "test-price",
updated_at: expect.any(String),
variant_id: "test-variant",
},
],
product_id: "test-product",
sku: "test-sku",
title: "Test variant",
upc: "test-upc",
updated_at: expect.any(String),
},
],
})
}) })
it("updates product (removes images when empty array included)", async () => { it("updates product (removes images when empty array included)", async () => {
@@ -1189,11 +1387,12 @@ describe("/admin/products", () => {
}) })
}) })
describe("updates a variant's prices", () => { describe("updates a variant's default prices (ignores prices associated with a Price List)", () => {
beforeEach(async () => { beforeEach(async () => {
try { try {
await productSeeder(dbConnection) await productSeeder(dbConnection)
await adminSeeder(dbConnection) await adminSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) { } catch (err) {
console.log(err) console.log(err)
throw err throw err
@@ -1205,7 +1404,7 @@ describe("/admin/products", () => {
await db.teardown() await db.teardown()
}) })
it("successfully updates a variant's prices by changing an existing price (currency_code)", async () => { it("successfully updates a variant's default prices by changing an existing price (currency_code)", async () => {
const api = useApi() const api = useApi()
const data = { const data = {
prices: [ prices: [
@@ -1227,7 +1426,6 @@ describe("/admin/products", () => {
}) })
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data).toEqual({ expect(response.data).toEqual({
product: expect.objectContaining({ product: expect.objectContaining({
id: "test-product", id: "test-product",
@@ -1294,8 +1492,9 @@ describe("/admin/products", () => {
prices: [ prices: [
// usd price coming from the product seeder // usd price coming from the product seeder
{ {
currency_code: "usd", id: "test-price",
amount: 100, amount: 100,
currency_code: "usd",
}, },
{ {
currency_code: "eur", currency_code: "eur",
@@ -1316,26 +1515,28 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data).toEqual({ expect(response.data).toEqual(
product: expect.objectContaining({ expect.objectContaining({
id: "test-product", product: expect.objectContaining({
variants: expect.arrayContaining([ id: "test-product",
expect.objectContaining({ variants: expect.arrayContaining([
id: "test-variant", expect.objectContaining({
prices: [ id: "test-variant",
expect.objectContaining({ prices: expect.arrayContaining([
amount: 100, expect.objectContaining({
currency_code: "usd", amount: 100,
}), currency_code: "usd",
expect.objectContaining({ }),
amount: 4500, expect.objectContaining({
currency_code: "eur", amount: 4500,
}), currency_code: "eur",
], }),
}), ]),
]), }),
}), ]),
}) }),
})
)
}) })
it("successfully updates a variant's prices by replacing a price", async () => { it("successfully updates a variant's prices by replacing a price", async () => {
@@ -1343,7 +1544,7 @@ describe("/admin/products", () => {
const data = { const data = {
prices: [ prices: [
{ {
currency_code: "eur", currency_code: "usd",
amount: 4500, amount: 4500,
}, },
], ],
@@ -1361,15 +1562,15 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.product.variants[0].prices.length).toEqual( expect(response.data.product.variants[0].prices.length).toEqual(1)
data.prices.length expect(response.data.product.variants[0].prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
amount: 4500,
currency_code: "usd",
}),
])
) )
expect(response.data.product.variants[0].prices).toEqual([
expect.objectContaining({
amount: 4500,
currency_code: "eur",
}),
])
}) })
it("successfully updates a variant's prices by deleting a price and adding another price", async () => { it("successfully updates a variant's prices by deleting a price and adding another price", async () => {
@@ -1399,19 +1600,20 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.product.variants[0].prices.length).toEqual( expect(response.data.product.variants[0].prices.length).toEqual(2)
data.prices.length
expect(response.data.product.variants[0].prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
amount: 8000,
currency_code: "dkk",
}),
expect.objectContaining({
amount: 900,
currency_code: "eur",
}),
])
) )
expect(response.data.product.variants[0].prices).toEqual([
expect.objectContaining({
amount: 8000,
currency_code: "dkk",
}),
expect.objectContaining({
amount: 900,
currency_code: "eur",
}),
])
}) })
it("successfully updates a variant's prices by updating an existing price (using region_id) and adding another price", async () => { it("successfully updates a variant's prices by updating an existing price (using region_id) and adding another price", async () => {
@@ -1627,7 +1829,7 @@ describe("/admin/products", () => {
}) })
} catch (error) { } catch (error) {
expect(error.response.data.message).toMatch( expect(error.response.data.message).toMatch(
/duplicate key value violates unique constraint/i "Product with handle test-product already exists."
) )
} }
}) })
@@ -1699,7 +1901,7 @@ describe("/admin/products", () => {
}) })
} catch (error) { } catch (error) {
expect(error.response.data.message).toMatch( expect(error.response.data.message).toMatch(
/duplicate key value violates unique constraint/i "Product_collection with handle test-collection already exists."
) )
} }
}) })
@@ -1707,7 +1909,7 @@ describe("/admin/products", () => {
it("successfully creates soft-deleted product variant", async () => { it("successfully creates soft-deleted product variant", async () => {
const api = useApi() const api = useApi()
const product = await api await api
.get("/admin/products/test-product", { .get("/admin/products/test-product", {
headers: { headers: {
Authorization: "bearer test_token", Authorization: "bearer test_token",
+135 -11
View File
@@ -11,9 +11,9 @@ jest.setTimeout(30000)
describe("/admin/regions", () => { describe("/admin/regions", () => {
let medusaProcess let medusaProcess
let dbConnection let dbConnection
const cwd = path.resolve(path.join(__dirname, "..", ".."))
beforeAll(async () => { beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd }) dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd }) medusaProcess = await setupServer({ cwd })
}) })
@@ -21,11 +21,98 @@ describe("/admin/regions", () => {
afterAll(async () => { afterAll(async () => {
const db = useDb() const db = useDb()
await db.shutdown() await db.shutdown()
medusaProcess.kill() medusaProcess.kill()
}) })
describe("Remove region from country on delete", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
const manager = dbConnection.manager
await manager.insert(Region, {
id: "test-region",
name: "Test Region",
currency_code: "usd",
tax_rate: 0,
})
await manager.query(
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("successfully creates a region with countries from a previously deleted region", async () => {
const api = useApi()
const response = await api
.delete(`/admin/regions/test-region`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data).toMatchSnapshot({
id: "test-region",
object: "region",
deleted: true,
})
const newReg = await api.post(
`/admin/regions`,
{
name: "World",
currency_code: "usd",
tax_rate: 0,
payment_providers: ["test-pay"],
fulfillment_providers: ["test-ful"],
countries: ["us"],
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
expect(newReg.status).toEqual(200)
expect(newReg.data.region).toMatchSnapshot({
id: expect.any(String),
name: "World",
currency_code: "usd",
countries: [
{
region_id: expect.any(String),
},
],
tax_rate: 0,
fulfillment_providers: [
{
id: "test-ful",
is_installed: true,
},
],
created_at: expect.any(String),
updated_at: expect.any(String),
})
})
})
describe("GET /admin/regions", () => { describe("GET /admin/regions", () => {
beforeAll(async () => { beforeEach(async () => {
const manager = dbConnection.manager const manager = dbConnection.manager
await adminSeeder(dbConnection) await adminSeeder(dbConnection)
await manager.insert(Region, { await manager.insert(Region, {
@@ -57,7 +144,7 @@ describe("/admin/regions", () => {
}) })
}) })
afterAll(async () => { afterEach(async () => {
const db = useDb() const db = useDb()
await db.teardown() await db.teardown()
}) })
@@ -116,14 +203,23 @@ describe("/admin/regions", () => {
describe("DELETE /admin/regions/:id", () => { describe("DELETE /admin/regions/:id", () => {
beforeEach(async () => { beforeEach(async () => {
const manager = dbConnection.manager try {
await adminSeeder(dbConnection) await adminSeeder(dbConnection)
await manager.insert(Region, { const manager = dbConnection.manager
id: "test-region", await manager.insert(Region, {
name: "Test Region", id: "test-region",
currency_code: "usd", name: "Test Region",
tax_rate: 0, currency_code: "usd",
}) tax_rate: 0,
})
await manager.query(
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
)
} catch (err) {
console.log(err)
throw err
}
}) })
afterEach(async () => { afterEach(async () => {
@@ -164,5 +260,33 @@ describe("/admin/regions", () => {
}) })
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
}) })
it("fails to create when countries exists in different region", async () => {
const api = useApi()
try {
await api.post(
`/admin/regions`,
{
name: "World",
currency_code: "usd",
tax_rate: 0,
payment_providers: ["test-pay"],
fulfillment_providers: ["test-ful"],
countries: ["us"],
},
{
headers: {
Authorization: "Bearer test_token",
},
}
)
} catch (error) {
expect(error.response.status).toEqual(422)
expect(error.response.data.message).toEqual(
"United States already exists in region test-region"
)
}
})
}) })
}) })
@@ -32,8 +32,8 @@ describe("/admin/store", () => {
afterEach(async () => { afterEach(async () => {
const db = useDb() const db = useDb()
db.teardown() await db.teardown({ forceDelete: ["store"] })
medusaProcess.kill() await medusaProcess.kill()
}) })
it("has created store with default currency", async () => { it("has created store with default currency", async () => {
@@ -35,6 +35,7 @@ exports[`Claims creates a replace claim 1`] = `
Object { Object {
"additional_items": Array [ "additional_items": Array [
Object { Object {
"adjustments": Array [],
"allow_discounts": true, "allow_discounts": true,
"cart_id": null, "cart_id": null,
"claim_order_id": StringMatching /\\^claim_\\*/, "claim_order_id": StringMatching /\\^claim_\\*/,
@@ -0,0 +1,222 @@
const path = require("path")
const { LineItemAdjustment } = require("@medusajs/medusa")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const cartSeeder = require("../../helpers/cart-seeder")
const { simpleCartFactory, simpleLineItemFactory } = require("../../factories")
const {
simpleDiscountFactory,
} = require("../../factories/simple-discount-factory")
jest.setTimeout(30000)
describe("Line Item Adjustments", () => {
let dbConnection
let medusaProcess
const doAfterEach = async () => {
const db = useDb()
return await db.teardown()
}
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
try {
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
} catch (error) {
console.log(error)
}
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
describe("Tests database constraints", () => {
let cart,
discount,
lineItemId = "line-test"
beforeEach(async () => {
try {
await cartSeeder(dbConnection)
discount = await simpleDiscountFactory(dbConnection, {
code: "MEDUSATEST",
id: "discount-test",
rule: {
value: 100,
type: "fixed",
allocation: "total",
},
regions: ["test-region"],
})
cart = await simpleCartFactory(
dbConnection,
{
customer: "test-customer",
id: "cart-test",
line_items: [
{
id: lineItemId,
variant_id: "test-variant",
cart_id: "cart-test",
unit_price: 1000,
quantity: 1,
adjustments: [
{
amount: 10,
discount_id: discount.id,
description: "discount",
item_id: lineItemId,
},
],
},
],
region: "test-region",
shipping_address: {
address_1: "test",
country_code: "us",
first_name: "chris",
last_name: "rock",
postal_code: "101",
},
shipping_methods: [
{
shipping_option: "test-option",
},
],
},
100
)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
await doAfterEach()
})
afterEach(async () => {
await doAfterEach()
})
describe("Given an existing line item, a discount, and a line item adjustment for both", () => {
describe("When creating an adjustment for another line item w. same discount", () => {
test("Then should create an adjustment", async () => {
const createLineItemWithAdjustment = async () => {
return await simpleLineItemFactory(dbConnection, {
id: "line-test-2",
variant_id: "test-variant-quantity",
cart_id: "test-cart",
unit_price: 1000,
quantity: 1,
adjustments: [
{
amount: 10,
discount_id: discount.id,
description: "discount",
item_id: "line-test-2",
},
],
})
}
expect(createLineItemWithAdjustment()).resolves.toEqual(
expect.anything()
)
})
})
describe("When creating an adjustment for another line item w. null discount", () => {
test("Then should create an adjustment", async () => {
const createAdjustmentNullDiscount = async () => {
return await dbConnection.manager.insert(LineItemAdjustment, {
id: "lia-1",
item_id: lineItemId,
amount: 35,
description: "custom discount",
discount_id: null,
})
}
expect(createAdjustmentNullDiscount()).resolves.toEqual(
expect.anything()
)
})
})
describe("When creating multiple adjustments w. a null discount_id", () => {
test("Then should create multiple adjustments", async () => {
const createAdjustmentsNullDiscount = async () => {
await dbConnection.manager.insert(LineItemAdjustment, {
id: "lia-1",
item_id: lineItemId,
amount: 35,
description: "custom discount",
discount_id: null,
})
return await dbConnection.manager.insert(LineItemAdjustment, {
id: "lia-2",
item_id: lineItemId,
amount: 100,
description: "custom discount",
discount_id: null,
})
}
expect(createAdjustmentsNullDiscount()).resolves.toEqual(
expect.anything()
)
})
})
describe("When creating an adjustment w. for same line item and different discount", () => {
test("Then should create an adjustment", async () => {
const createAdjustment = async () => {
await simpleDiscountFactory(dbConnection, {
code: "ANOTHER",
id: "discount-2",
rule: {
value: 10,
type: "percentage",
allocation: "item",
},
regions: ["test-region"],
})
return await dbConnection.manager.insert(LineItemAdjustment, {
id: "lia-1",
item_id: lineItemId,
amount: 10,
description: "discount",
discount_id: "discount-2",
})
}
expect(createAdjustment()).resolves.toEqual(expect.anything())
})
})
describe("When creating an adjustment w. existing line item and discount pair", () => {
test("Then should throw a duplicate error", async () => {
const createDuplicateAdjustment = async () =>
await dbConnection.manager.insert(LineItemAdjustment, {
id: "lia-1",
item_id: lineItemId,
amount: 20,
description: "discount",
discount_id: discount.id,
})
expect(createDuplicateAdjustment()).rejects.toEqual(
expect.objectContaining({ code: "23505" })
)
})
})
})
})
})
@@ -0,0 +1,659 @@
const path = require("path")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { useDb, initDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const promotionsSeeder = require("../../helpers/price-selection-seeder")
jest.setTimeout(30000)
describe("Promotions", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
describe("Money amount", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await promotionsSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("calculated_price contains lowest price", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product?region_id=test-region")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
const lowestPrice = variant.prices.reduce(
(prev, curr) => (curr.amount < prev ? curr.amount : prev),
Infinity
)
expect(variant.calculated_price).toEqual(lowestPrice)
expect(variant).toEqual(
expect.objectContaining({ original_price: 120, calculated_price: 110 })
)
})
it("returns no money amounts belonging to customer groups without login", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product?cart_id=test-cart")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant.prices.length).toEqual(2)
variant.prices.forEach((price) => {
if (price.price_list) {
expect(price.price_list.customer_groups).toEqual(undefined)
} else {
expect(price.price_list).toEqual(null)
}
})
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1",
region_id: "test-region",
currency_code: "usd",
amount: 120,
}),
expect.objectContaining({
id: "test-price3",
region_id: "test-region",
currency_code: "usd",
price_list_id: "pl",
amount: 110,
}),
])
)
})
it("sets default price as original price", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product?cart_id=test-cart")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
})
it("gets prices for currency if no region prices exist", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product?cart_id=test-cart-2")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
expect(variant.prices.length).toEqual(2)
variant.prices.forEach((price) => {
if (price.price_list) {
expect(price.price_list.customer_groups).toEqual(undefined)
} else {
expect(price.price_list).toEqual(null)
}
})
variant.prices.forEach((price) => {
expect(price.region_id).toEqual("test-region")
expect(price.currency_code).toEqual("usd")
})
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1",
region_id: "test-region",
currency_code: "usd",
amount: 120,
}),
expect.objectContaining({
id: "test-price3",
region_id: "test-region",
currency_code: "usd",
price_list_id: "pl",
amount: 110,
}),
])
)
})
it("gets prices for cart region for multi region product", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product-multi-region?cart_id=test-cart-1")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant).toEqual(
expect.objectContaining({ original_price: 130, calculated_price: 110 })
)
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
expect(variant.prices.length).toEqual(2)
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1-region-2",
region_id: "test-region-2",
currency_code: "dkk",
amount: 130,
}),
expect.objectContaining({
id: "test-price3-region-2",
region_id: "test-region-2",
currency_code: "dkk",
price_list_id: "pl",
amount: 110,
}),
])
)
})
it("gets prices for multi region product", async () => {
const api = useApi()
const res = await api
.get(
"/store/products/test-product-multi-region?region_id=test-region-2"
)
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant).toEqual(
expect.objectContaining({ original_price: 130, calculated_price: 110 })
)
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
expect(variant.prices.length).toEqual(2)
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1-region-2",
region_id: "test-region-2",
currency_code: "dkk",
amount: 130,
}),
expect.objectContaining({
id: "test-price3-region-2",
region_id: "test-region-2",
currency_code: "dkk",
price_list_id: "pl",
amount: 110,
}),
])
)
})
it("gets prices for multi currency product", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product-multi-region?currency_code=dkk")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant).toEqual(
expect.objectContaining({ original_price: 130, calculated_price: 110 })
)
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
expect(variant.prices.length).toEqual(2)
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1-region-2",
region_id: "test-region-2",
currency_code: "dkk",
amount: 130,
}),
expect.objectContaining({
id: "test-price3-region-2",
region_id: "test-region-2",
currency_code: "dkk",
price_list_id: "pl",
amount: 110,
}),
])
)
})
it("gets moneyamounts only with valid date interval", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product-sale?cart_id=test-cart")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
const date = new Date()
expect(variant.prices.length).toEqual(2)
variant.prices.forEach((price) => {
if (price.starts_at) {
expect(new Date(price.starts_at).getTime()).toBeLessThan(
date.getTime()
)
}
if (price.ends_at) {
expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
date.getTime()
)
}
})
})
it("gets moneyamounts with valid date intervals and finds lowest price with overlapping intervals", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product-sale-overlap?cart_id=test-cart")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
const date = new Date()
expect(variant).toEqual(
expect.objectContaining({
original_price: 150,
calculated_price: 120,
})
)
expect(variant.prices.length).toEqual(3)
variant.prices.forEach((price) => {
if (price.starts_at) {
expect(new Date(price.starts_at).getTime()).toBeLessThan(
date.getTime()
)
}
if (price.ends_at) {
expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
date.getTime()
)
}
})
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price-sale-overlap-1",
region_id: "test-region",
currency_code: "usd",
amount: 140,
price_list_id: "pl_current_1",
}),
expect.objectContaining({
id: "test-price1-sale-overlap",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl_current",
}),
expect.objectContaining({
id: "test-price2-sale-overlap-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
}),
])
)
})
it("gets all prices with varying quantity limits with no quantity", async () => {
const api = useApi()
const res = await api
.get("/store/products/test-product-quantity?cart_id=test-cart")
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant.prices.length).toEqual(5)
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl",
min_quantity: 10,
max_quantity: 100,
}),
expect.objectContaining({
id: "test-price1-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl",
min_quantity: 101,
max_quantity: 1000,
}),
expect.objectContaining({
id: "test-price2-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 130,
price_list_id: "pl",
max_quantity: 9,
}),
expect.objectContaining({
id: "test-price3-quantity-now",
region_id: "test-region",
currency_code: "usd",
amount: 140,
price_list_id: "pl_current",
min_quantity: 101,
max_quantity: 1000,
}),
expect.objectContaining({
id: "test-price3-quantity-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
}),
])
)
expect(variant.calculated_price).toEqual(130)
expect(variant.original_price).toEqual(150)
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
})
it("fetches product with groups in money amounts with login", async () => {
const api = useApi()
// customer with customer-group 5
const authResponse = await api.post("/store/auth", {
email: "test5@email.com",
password: "test",
})
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
const res = await api
.get("/store/products/test-product?cart_id=test-cart", {
headers: {
Cookie: authCookie,
},
})
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant.prices.length).toEqual(3)
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1",
region_id: "test-region",
currency_code: "usd",
amount: 120,
}),
expect.objectContaining({
id: "test-price3",
region_id: "test-region",
currency_code: "usd",
price_list_id: "pl",
amount: 110,
}),
expect.objectContaining({
id: "test-price",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list: expect.objectContaining({}),
}),
])
)
})
it("fetches product with groups and quantities in money amounts with login", async () => {
const api = useApi()
// customer with customer-group 5
const authResponse = await api.post("/store/auth", {
email: "test5@email.com",
password: "test",
})
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
const res = await api
.get(
"/store/products/test-product-quantity-customer?cart_id=test-cart",
{
headers: {
Cookie: authCookie,
},
}
)
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
expect(variant.prices.length).toEqual(6)
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price-quantity-customer",
region_id: "test-region",
currency_code: "usd",
amount: 100,
min_quantity: 10,
max_quantity: 100,
}),
expect.objectContaining({
id: "test-price1-quantity-customer",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 101,
max_quantity: 1000,
}),
expect.objectContaining({
id: "test-price2-quantity-customer",
region_id: "test-region",
currency_code: "usd",
amount: 130,
max_quantity: 9,
}),
expect.objectContaining({
id: "test-price2-quantity-customer-group",
region_id: "test-region",
currency_code: "usd",
amount: 100,
max_quantity: 9,
price_list: expect.objectContaining({}),
}),
expect.objectContaining({
id: "test-price3-quantity-customer-now",
region_id: "test-region",
currency_code: "usd",
amount: 140,
min_quantity: 101,
max_quantity: 1000,
}),
expect.objectContaining({
id: "test-price3-quantity-customer-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
price_list_id: null,
}),
])
)
expect(variant.calculated_price).toEqual(100)
expect(variant.original_price).toEqual(150)
expect(variant.original_price).toEqual(
variant.prices.find((p) => p.price_list_id === null).amount
)
})
it("gets moneyamounts only with valid date interval for customer", async () => {
const api = useApi()
// customer with customer-group 5
const authResponse = await api.post("/store/auth", {
email: "test5@email.com",
password: "test",
})
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
const res = await api
.get("/store/products/test-product-sale-customer?cart_id=test-cart", {
headers: {
Cookie: authCookie,
},
})
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
const date = new Date()
expect(variant).toEqual(
expect.objectContaining({
original_price: 150,
calculated_price: 100,
})
)
expect(variant.prices.length).toEqual(2)
variant.prices.forEach((price) => {
if (price.starts_at) {
expect(new Date(price.starts_at).getTime()).toBeLessThan(
date.getTime()
)
}
if (price.ends_at) {
expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
date.getTime()
)
}
})
})
it("gets moneyamounts only with valid date interval for customer regardless of quantity limits", async () => {
const api = useApi()
// customer with customer-group 5
const authResponse = await api.post("/store/auth", {
email: "test5@email.com",
password: "test",
})
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
const res = await api
.get(
"/store/products/test-product-sale-customer-quantity?cart_id=test-cart",
{
headers: {
Cookie: authCookie,
},
}
)
.catch((error) => console.log(error))
const variant = res.data.product.variants[0]
const date = new Date()
expect(variant).toEqual(
expect.objectContaining({
original_price: 150,
calculated_price: 100,
})
)
expect(variant.prices.length).toEqual(3)
variant.prices.forEach((price) => {
if (price.starts_at) {
expect(new Date(price.starts_at).getTime()).toBeLessThan(
date.getTime()
)
}
if (price.ends_at) {
expect(new Date(price.ends_at).getTime()).toBeGreaterThan(
date.getTime()
)
}
})
expect(variant.prices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price1-sale-customer-quantity-groups",
region_id: "test-region",
currency_code: "usd",
amount: 100,
max_quantity: 99,
price_list: expect.objectContaining({}),
}),
expect.objectContaining({
id: "test-price2-sale-customer-quantity-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
}),
expect.objectContaining({
id: "test-price1-sale-customer-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 110,
max_quantity: 99,
}),
])
)
})
})
})
@@ -259,6 +259,14 @@ const createReturnableOrder = async (dbConnection, options) => {
variant_id: "test-variant", variant_id: "test-variant",
quantity: 2, quantity: 2,
unit_price: 1000, unit_price: 1000,
adjustments: [
{
amount: 200,
discount_code: "TESTCODE",
description: "discount",
item_id: "test-item",
},
],
tax_lines: [ tax_lines: [
{ {
name: "default", name: "default",
@@ -24,7 +24,7 @@ Object {
} }
`; `;
exports[`/store/carts shipping address + region updates updates region only - single to multipe countries 1`] = ` exports[`/store/carts shipping address + region updates updates region only - single to multiple countries 1`] = `
Object { Object {
"address_1": null, "address_1": null,
"address_2": null, "address_2": null,
@@ -1,10 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/store/variants /test-variant 1`] = ` exports[`/store/variants GET /store/variants /test-variant 1`] = `
Object { Object {
"variant": Object { "variant": Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": "test-barcode", "barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean", "ean": "test-ean",
@@ -30,6 +31,7 @@ Object {
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -37,8 +39,11 @@ Object {
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": "test-price", "id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": "test-variant", "variant_id": "test-variant",
}, },
@@ -55,12 +60,247 @@ Object {
} }
`; `;
exports[`/store/variants includes default relations 1`] = ` exports[`/store/variants GET /store/variants advanced pricing /test-variant with currency code 1`] = `
Object {
"variant": Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": 80,
"calculated_price_type": "sale",
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
"height": null,
"hs_code": null,
"id": "test-variant",
"inventory_quantity": 10,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "test-variant-option",
"metadata": null,
"option_id": "test-option",
"updated_at": Any<String>,
"value": "Default variant",
"variant_id": "test-variant",
},
],
"origin_country": null,
"original_price": 100,
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price-discount",
"max_quantity": null,
"min_quantity": null,
"price_list": Object {
"created_at": Any<String>,
"deleted_at": null,
"description": "Winter sale for VIP customers.",
"ends_at": null,
"id": "pl",
"name": "VIP winter sale",
"starts_at": null,
"status": "active",
"type": "sale",
"updated_at": Any<String>,
},
"price_list_id": "pl",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
],
"product": Any<Object>,
"product_id": "test-product",
"sku": "test-sku",
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
}
`;
exports[`/store/variants GET /store/variants advanced pricing lists by title 1`] = `
Object {
"variants": Array [
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
"height": null,
"hs_code": null,
"id": Any<String>,
"inventory_quantity": 12,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": Any<String>,
"metadata": null,
"option_id": Any<String>,
"updated_at": Any<String>,
"value": "Handcrafted",
"variant_id": Any<String>,
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": Any<String>,
},
],
"product": Any<Object>,
"product_id": Any<String>,
"sku": null,
"title": "test2",
"upc": null,
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
}
`;
exports[`/store/variants GET /store/variants advanced pricing sets additional prices correctly when querying a variant 1`] = `
Object {
"variant": Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": 80,
"calculated_price_type": "sale",
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
"height": null,
"hs_code": null,
"id": "test-variant",
"inventory_quantity": 10,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"options": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"id": "test-variant-option",
"metadata": null,
"option_id": "test-option",
"updated_at": Any<String>,
"value": "Default variant",
"variant_id": "test-variant",
},
],
"origin_country": null,
"original_price": 100,
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price-discount",
"max_quantity": null,
"min_quantity": null,
"price_list": Object {
"created_at": Any<String>,
"deleted_at": null,
"description": "Winter sale for VIP customers.",
"ends_at": null,
"id": "pl",
"name": "VIP winter sale",
"starts_at": null,
"status": "active",
"type": "sale",
"updated_at": Any<String>,
},
"price_list_id": "pl",
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
],
"product": Any<Object>,
"product_id": "test-product",
"sku": "test-sku",
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
}
`;
exports[`/store/variants GET /store/variants includes default relations 1`] = `
Object { Object {
"variants": Array [ "variants": Array [
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": "test-barcode", "barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean", "ean": "test-ean",
@@ -86,6 +326,7 @@ Object {
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -93,8 +334,11 @@ Object {
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": "test-price", "id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": "test-variant", "variant_id": "test-variant",
}, },
@@ -102,7 +102,7 @@ Object {
], ],
"origin_country": null, "origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/, "profile_id": StringMatching /\\^sp_\\*/,
"status": "draft", "status": "published",
"subtitle": null, "subtitle": null,
"tags": Array [ "tags": Array [
Object { Object {
@@ -130,6 +130,7 @@ Object {
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": "test-barcode", "barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean", "ean": "test-ean",
@@ -155,6 +156,7 @@ Object {
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -162,8 +164,36 @@ Object {
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": "test-price", "id": "test-price",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price-discount",
"max_quantity": null,
"min_quantity": null,
"price_list": Object {
"created_at": Any<String>,
"deleted_at": null,
"description": "Winter sale for VIP customers.",
"ends_at": null,
"id": "pl",
"name": "VIP winter sale",
"starts_at": null,
"status": "active",
"type": "sale",
"updated_at": Any<String>,
},
"price_list_id": "pl",
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": "test-variant", "variant_id": "test-variant",
}, },
@@ -179,6 +209,7 @@ Object {
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": null, "barcode": null,
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean2", "ean": "test-ean2",
@@ -204,6 +235,7 @@ Object {
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -211,8 +243,36 @@ Object {
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": "test-price2", "id": "test-price2",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant_2",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price2-discount",
"max_quantity": null,
"min_quantity": null,
"price_list": Object {
"created_at": Any<String>,
"deleted_at": null,
"description": "Winter sale for VIP customers.",
"ends_at": null,
"id": "pl",
"name": "VIP winter sale",
"starts_at": null,
"status": "active",
"type": "sale",
"updated_at": Any<String>,
},
"price_list_id": "pl",
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": "test-variant_2", "variant_id": "test-variant_2",
}, },
@@ -228,6 +288,7 @@ Object {
Object { Object {
"allow_backorder": false, "allow_backorder": false,
"barcode": "test-barcode 1", "barcode": "test-barcode 1",
"calculated_price": null,
"created_at": Any<String>, "created_at": Any<String>,
"deleted_at": null, "deleted_at": null,
"ean": "test-ean1", "ean": "test-ean1",
@@ -253,6 +314,7 @@ Object {
}, },
], ],
"origin_country": null, "origin_country": null,
"original_price": null,
"prices": Array [ "prices": Array [
Object { Object {
"amount": 100, "amount": 100,
@@ -260,8 +322,36 @@ Object {
"currency_code": "usd", "currency_code": "usd",
"deleted_at": null, "deleted_at": null,
"id": "test-price1", "id": "test-price1",
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": "test-variant_1",
},
Object {
"amount": 80,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": "test-price1-discount",
"max_quantity": null,
"min_quantity": null,
"price_list": Object {
"created_at": Any<String>,
"deleted_at": null,
"description": "Winter sale for VIP customers.",
"ends_at": null,
"id": "pl",
"name": "VIP winter sale",
"starts_at": null,
"status": "active",
"type": "sale",
"updated_at": Any<String>,
},
"price_list_id": "pl",
"region_id": null, "region_id": null,
"sale_amount": null,
"updated_at": Any<String>, "updated_at": Any<String>,
"variant_id": "test-variant_1", "variant_id": "test-variant_1",
}, },
@@ -280,3 +370,61 @@ Object {
}, },
} }
`; `;
exports[`/store/products list params works with expand and fields 1`] = `
Object {
"count": 2,
"limit": 1,
"offset": 0,
"products": Array [
Object {
"id": Any<String>,
"title": "testprod",
"variants": Array [
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
"height": null,
"hs_code": null,
"id": Any<String>,
"inventory_quantity": 10,
"length": null,
"manage_inventory": true,
"material": null,
"metadata": null,
"mid_code": null,
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"deleted_at": null,
"id": Any<String>,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"updated_at": Any<String>,
"variant_id": Any<String>,
},
],
"product_id": Any<String>,
"sku": null,
"title": "test-variant",
"upc": null,
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
},
],
}
`;
File diff suppressed because it is too large. Load diff
@@ -58,7 +58,7 @@ describe("/store/customers", () => {
expect(response.data.customer).not.toHaveProperty("password_hash") expect(response.data.customer).not.toHaveProperty("password_hash")
}) })
it("responds 409 on duplicate", async () => { it("responds 422 on duplicate", async () => {
const api = useApi() const api = useApi()
const response = await api const response = await api
@@ -70,7 +70,7 @@ describe("/store/customers", () => {
}) })
.catch((err) => err.response) .catch((err) => err.response)
expect(response.status).toEqual(402) expect(response.status).toEqual(422)
}) })
}) })
@@ -1,9 +1,12 @@
const { ProductVariant } = require("@medusajs/medusa")
const path = require("path") const path = require("path")
const setupServer = require("../../../helpers/setup-server") const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api") const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db") const { initDb, useDb } = require("../../../helpers/use-db")
const { simpleProductFactory } = require("../../factories")
const productSeeder = require("../../helpers/product-seeder") const productSeeder = require("../../helpers/product-seeder")
const storeProductSeeder = require("../../helpers/store-product-seeder")
jest.setTimeout(30000) jest.setTimeout(30000)
describe("/store/variants", () => { describe("/store/variants", () => {
let medusaProcess let medusaProcess
@@ -21,28 +24,100 @@ describe("/store/variants", () => {
medusaProcess.kill() medusaProcess.kill()
}) })
beforeEach(async () => { describe("GET /store/variants", () => {
try { beforeEach(async () => {
await productSeeder(dbConnection) try {
} catch (err) { await productSeeder(dbConnection)
console.log(err)
throw err
}
})
afterEach(async () => { await simpleProductFactory(
const db = useDb() dbConnection,
await db.teardown() {
}) title: "prod",
variants: [
{
title: "test1",
inventory_quantity: 10,
},
{
title: "test2",
inventory_quantity: 12,
},
],
},
100
)
} catch (err) {
console.log(err)
throw err
}
})
it("includes default relations", async () => { afterEach(async () => {
const api = useApi() const db = useDb()
await db.teardown()
})
const response = await api.get("/store/variants?ids=test-variant") it("includes default relations", async () => {
const api = useApi()
expect(response.data).toMatchSnapshot({ const response = await api.get("/store/variants?ids=test-variant")
variants: [
{ expect(response.data).toMatchSnapshot({
variants: [
{
allow_backorder: false,
barcode: "test-barcode",
created_at: expect.any(String),
deleted_at: null,
ean: "test-ean",
height: null,
hs_code: null,
id: "test-variant",
inventory_quantity: 10,
length: null,
manage_inventory: true,
material: null,
metadata: null,
mid_code: null,
origin_country: null,
product_id: "test-product",
sku: "test-sku",
title: "Test variant",
upc: "test-upc",
updated_at: expect.any(String),
weight: null,
width: null,
options: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
prices: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
currency_code: "usd",
deleted_at: null,
id: "test-price",
region_id: null,
variant_id: "test-variant",
},
],
product: expect.any(Object),
},
],
})
})
it("/test-variant", async () => {
const api = useApi()
const response = await api.get("/store/variants/test-variant")
expect(response.data).toMatchSnapshot({
variant: {
allow_backorder: false, allow_backorder: false,
barcode: "test-barcode", barcode: "test-barcode",
created_at: expect.any(String), created_at: expect.any(String),
@@ -80,72 +155,237 @@ describe("/store/variants", () => {
deleted_at: null, deleted_at: null,
id: "test-price", id: "test-price",
region_id: null, region_id: null,
sale_amount: null, min_quantity: null,
max_quantity: null,
price_list_id: null,
variant_id: "test-variant", variant_id: "test-variant",
}, },
], ],
product: expect.any(Object), product: expect.any(Object),
options: [
{ created_at: expect.any(String), updated_at: expect.any(String) },
],
}, },
], })
}) })
}) })
it("/test-variant", async () => { describe("GET /store/variants advanced pricing", () => {
const api = useApi() beforeEach(async () => {
try {
await storeProductSeeder(dbConnection)
const response = await api.get("/store/variants/test-variant") await simpleProductFactory(
dbConnection,
expect(response.data).toMatchSnapshot({
variant: {
allow_backorder: false,
barcode: "test-barcode",
created_at: expect.any(String),
deleted_at: null,
ean: "test-ean",
height: null,
hs_code: null,
id: "test-variant",
inventory_quantity: 10,
length: null,
manage_inventory: true,
material: null,
metadata: null,
mid_code: null,
origin_country: null,
product_id: "test-product",
sku: "test-sku",
title: "Test variant",
upc: "test-upc",
updated_at: expect.any(String),
weight: null,
width: null,
options: [
{ {
title: "prod",
variants: [
{
title: "test1",
inventory_quantity: 10,
},
{
title: "test2",
inventory_quantity: 12,
},
],
},
100
)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("/test-variant with currency code", async () => {
const api = useApi()
const response = await api.get(
"/store/variants/test-variant?currency_code=usd"
)
expect(response.data).toMatchSnapshot({
variant: {
allow_backorder: false,
barcode: "test-barcode",
created_at: expect.any(String),
deleted_at: null,
ean: "test-ean",
height: null,
hs_code: null,
id: "test-variant",
inventory_quantity: 10,
length: null,
manage_inventory: true,
material: null,
metadata: null,
mid_code: null,
origin_country: null,
product_id: "test-product",
sku: "test-sku",
title: "Test variant",
upc: "test-upc",
updated_at: expect.any(String),
weight: null,
width: null,
options: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
prices: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
currency_code: "usd",
deleted_at: null,
id: "test-price",
region_id: null,
min_quantity: null,
max_quantity: null,
price_list_id: null,
variant_id: "test-variant",
},
{
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 80,
currency_code: "usd",
deleted_at: null,
price_list_id: "pl",
id: "test-price-discount",
region_id: null,
variant_id: "test-variant",
price_list: {
created_at: expect.any(String),
updated_at: expect.any(String),
id: "pl",
},
},
],
product: expect.any(Object),
},
})
})
it("sets additional prices correctly when querying a variant", async () => {
const api = useApi()
const response = await api
.get("/store/variants/test-variant?cart_id=test-cart")
.catch((error) => console.log(error))
expect(response.data).toMatchSnapshot({
variant: {
id: "test-variant",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku",
ean: "test-ean",
upc: "test-upc",
barcode: "test-barcode",
product_id: "test-product",
allow_backorder: false,
created_at: expect.any(String),
deleted_at: null,
height: null,
hs_code: null,
length: null,
manage_inventory: true,
material: null,
metadata: null,
mid_code: null,
origin_country: null,
updated_at: expect.any(String),
weight: null,
width: null,
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
original_price: 100,
calculated_price: 80,
prices: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
currency_code: "usd",
deleted_at: null,
id: "test-price",
region_id: null,
variant_id: "test-variant",
},
{
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 80,
currency_code: "usd",
deleted_at: null,
price_list_id: "pl",
id: "test-price-discount",
region_id: null,
variant_id: "test-variant",
price_list: {
created_at: expect.any(String),
updated_at: expect.any(String),
id: "pl",
},
},
],
product: expect.any(Object),
},
})
})
it("lists by title", async () => {
const api = useApi()
const response = await api
.get(
"/store/variants?title[]=test1&title[]=test2&inventory_quantity[gt]=10"
)
.catch((err) => console.log(err))
expect(response.data).toMatchSnapshot({
variants: [
{
id: expect.any(String),
title: "test2",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
options: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
id: expect.any(String),
option_id: expect.any(String),
variant_id: expect.any(String),
},
],
prices: [
{
id: expect.any(String),
variant_id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
product: expect.any(Object),
product_id: expect.any(String),
}, },
], ],
prices: [ })
{
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
currency_code: "usd",
deleted_at: null,
id: "test-price",
region_id: null,
sale_amount: null,
variant_id: "test-variant",
},
],
product: expect.any(Object),
options: [
{ created_at: expect.any(String), updated_at: expect.any(String) },
],
},
}) })
}) })
}) })
+247 -18
View File
@@ -1,9 +1,9 @@
const { Product } = require("@medusajs/medusa")
const path = require("path") const path = require("path")
const setupServer = require("../../../helpers/setup-server") const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api") const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db") const { initDb, useDb } = require("../../../helpers/use-db")
const { simpleProductFactory } = require("../../factories")
const productSeeder = require("../../helpers/store-product-seeder") const productSeeder = require("../../helpers/store-product-seeder")
const adminSeeder = require("../../helpers/admin-seeder") const adminSeeder = require("../../helpers/admin-seeder")
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -200,17 +200,26 @@ describe("/store/products", () => {
}) })
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.products.length).toEqual(5)
expect(response.data.products).toEqual([ expect(response.data.products).toEqual([
expect.objectContaining({ expect.objectContaining({
id: "giftcard", id: "test-product1",
collection_id: "test-collection",
}),
expect.objectContaining({
id: "test-product",
collection_id: "test-collection",
}),
expect.objectContaining({
id: "test-product_filtering_2",
collection_id: "test-collection2",
}), }),
expect.objectContaining({ expect.objectContaining({
id: "test-product_filtering_1", id: "test-product_filtering_1",
collection_id: "test-collection1", collection_id: "test-collection1",
}), }),
expect.objectContaining({ expect.objectContaining({
id: "test-product_filtering_2", id: "giftcard",
collection_id: "test-collection2",
}), }),
]) ])
@@ -222,6 +231,173 @@ describe("/store/products", () => {
}) })
}) })
describe("list params", () => {
beforeEach(async () => {
try {
await productSeeder(dbConnection)
await adminSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Includes Additional prices when queried with a cart id", async () => {
const api = useApi()
const response = await api
.get("/store/products?cart_id=test-cart")
.catch((err) => {
console.log(err)
})
expect(response.data.products).toEqual([
expect.objectContaining({
id: "test-product1",
collection_id: "test-collection",
}),
expect.objectContaining({
id: "test-product",
collection_id: "test-collection",
variants: [
expect.objectContaining({
original_price: 100,
calculated_price: 80,
prices: [
expect.objectContaining({
id: "test-price",
currency_code: "usd",
amount: 100,
}),
expect.objectContaining({
id: "test-price-discount",
currency_code: "usd",
amount: 80,
}),
],
}),
expect.objectContaining({
original_price: 100,
calculated_price: 80,
prices: [
expect.objectContaining({
id: "test-price2",
currency_code: "usd",
amount: 100,
}),
expect.objectContaining({
id: "test-price2-discount",
currency_code: "usd",
amount: 80,
}),
],
}),
expect.objectContaining({
original_price: 100,
calculated_price: 80,
prices: [
expect.objectContaining({
id: "test-price1",
currency_code: "usd",
amount: 100,
}),
expect.objectContaining({
id: "test-price1-discount",
currency_code: "usd",
amount: 80,
}),
],
}),
],
}),
expect.objectContaining({
id: "test-product_filtering_2",
collection_id: "test-collection2",
}),
expect.objectContaining({
id: "test-product_filtering_1",
collection_id: "test-collection1",
}),
expect.objectContaining({
id: "giftcard",
}),
])
})
})
describe("list params", () => {
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await simpleProductFactory(
dbConnection,
{
title: "testprod",
status: "published",
variants: [{ title: "test-variant" }],
},
11
)
await simpleProductFactory(
dbConnection,
{
title: "testprod3",
status: "published",
variants: [{ title: "test-variant1" }],
},
12
)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("works with expand and fields", async () => {
const api = useApi()
const response = await api.get(
"/store/products?expand=variants,variants.prices&fields=id,title&limit=1"
)
expect(response.data).toMatchSnapshot({
products: [
{
id: expect.any(String),
variants: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
id: expect.any(String),
product_id: expect.any(String),
prices: [
{
created_at: expect.any(String),
updated_at: expect.any(String),
id: expect.any(String),
variant_id: expect.any(String),
},
],
},
],
},
],
})
})
})
describe("/store/products/:id", () => { describe("/store/products/:id", () => {
beforeEach(async () => { beforeEach(async () => {
try { try {
@@ -263,6 +439,8 @@ describe("/store/products", () => {
height: null, height: null,
hs_code: null, hs_code: null,
origin_country: null, origin_country: null,
calculated_price: null,
original_price: null,
barcode: "test-barcode", barcode: "test-barcode",
product_id: "test-product", product_id: "test-product",
created_at: expect.any(String), created_at: expect.any(String),
@@ -275,19 +453,35 @@ describe("/store/products", () => {
], ],
prices: [ prices: [
{ {
id: "test-money-amount",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
amount: 100, amount: 100,
created_at: expect.any(String),
currency_code: "usd", currency_code: "usd",
deleted_at: null, deleted_at: null,
min_quantity: null,
max_quantity: null,
price_list_id: null,
id: "test-price", id: "test-price",
region_id: null, region_id: null,
sale_amount: null,
updated_at: expect.any(String),
variant_id: "test-variant", variant_id: "test-variant",
}, },
{
id: "test-price-discount",
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 80,
currency_code: "usd",
price_list_id: "pl",
deleted_at: null,
region_id: null,
variant_id: "test-variant",
price_list: {
id: "pl",
type: "sale",
created_at: expect.any(String),
updated_at: expect.any(String),
},
},
], ],
}, },
{ {
@@ -307,6 +501,8 @@ describe("/store/products", () => {
hs_code: null, hs_code: null,
origin_country: null, origin_country: null,
barcode: null, barcode: null,
calculated_price: null,
original_price: null,
product_id: "test-product", product_id: "test-product",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
@@ -318,18 +514,33 @@ describe("/store/products", () => {
], ],
prices: [ prices: [
{ {
id: "test-money-amount", id: "test-price2",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
amount: 100, amount: 100,
created_at: expect.any(String),
currency_code: "usd", currency_code: "usd",
price_list_id: null,
deleted_at: null, deleted_at: null,
id: "test-price2",
region_id: null, region_id: null,
sale_amount: null,
variant_id: "test-variant_2", variant_id: "test-variant_2",
}, },
{
id: "test-price2-discount",
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 80,
currency_code: "usd",
price_list_id: "pl",
deleted_at: null,
region_id: null,
variant_id: "test-variant_2",
price_list: {
id: "pl",
type: "sale",
created_at: expect.any(String),
updated_at: expect.any(String),
},
},
], ],
}, },
{ {
@@ -348,6 +559,8 @@ describe("/store/products", () => {
height: null, height: null,
hs_code: null, hs_code: null,
origin_country: null, origin_country: null,
calculated_price: null,
original_price: null,
barcode: "test-barcode 1", barcode: "test-barcode 1",
product_id: "test-product", product_id: "test-product",
created_at: expect.any(String), created_at: expect.any(String),
@@ -360,19 +573,35 @@ describe("/store/products", () => {
], ],
prices: [ prices: [
{ {
id: "test-money-amount", id: "test-price1",
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
amount: 100, amount: 100,
created_at: expect.any(String),
currency_code: "usd", currency_code: "usd",
min_quantity: null,
max_quantity: null,
price_list_id: null,
deleted_at: null, deleted_at: null,
id: "test-price1",
region_id: null, region_id: null,
sale_amount: null,
updated_at: expect.any(String),
variant_id: "test-variant_1", variant_id: "test-variant_1",
}, },
{
id: "test-price1-discount",
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 80,
currency_code: "usd",
price_list_id: "pl",
deleted_at: null,
region_id: null,
variant_id: "test-variant_1",
price_list: {
id: "pl",
type: "sale",
created_at: expect.any(String),
updated_at: expect.any(String),
},
},
], ],
}, },
], ],
@@ -473,7 +702,7 @@ describe("/store/products", () => {
it("lists all published products", async () => { it("lists all published products", async () => {
const api = useApi() const api = useApi()
//update test-product status to published // update test-product status to published
await api await api
.post( .post(
"/admin/products/test-product", "/admin/products/test-product",
@@ -12,6 +12,8 @@ const {
simpleProductFactory, simpleProductFactory,
} = require("../../factories") } = require("../../factories")
jest.setTimeout(30000)
describe("Swaps", () => { describe("Swaps", () => {
let medusaProcess let medusaProcess
let dbConnection let dbConnection
@@ -28,7 +28,7 @@ describe("/admin/tax-rates", () => {
const cwd = path.resolve(path.join(__dirname, "..", "..")) const cwd = path.resolve(path.join(__dirname, "..", ".."))
try { try {
dbConnection = await initDb({ cwd }) dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd, verbose: true }) medusaProcess = await setupServer({ cwd })
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
@@ -216,7 +216,7 @@ describe("/admin/tax-rates", () => {
) )
}) })
test.only("fails with 404 on unknown rate", async () => { test("fails with 404 on unknown rate", async () => {
await adminSeeder(dbConnection) await adminSeeder(dbConnection)
const { tax_rates } = await createTaxRates(dbConnection, 1, 1, 200) const { tax_rates } = await createTaxRates(dbConnection, 1, 1, 200)
const [rate] = tax_rates const [rate] = tax_rates
@@ -197,6 +197,111 @@ describe("Order Taxes", () => {
expect(response.data.order.total).toEqual(2300) expect(response.data.order.total).toEqual(2300)
}) })
test("completing cart with failure doesn't duplicate", async () => {
const product1 = await simpleProductFactory(
dbConnection,
{
variants: [
{
id: "test-variant",
},
],
},
100
)
const product2 = await simpleProductFactory(
dbConnection,
{
variants: [
{
id: "test-variant-2",
},
],
},
100
)
const region = await simpleRegionFactory(dbConnection, {
name: "Test region",
tax_rate: 12,
})
await simpleProductTaxRateFactory(dbConnection, {
product_id: product1.id,
rate: {
region_id: region.id,
rate: 25,
},
})
await simpleProductTaxRateFactory(dbConnection, {
product_id: product2.id,
rate: {
region_id: region.id,
rate: 20,
},
})
const cart = await simpleCartFactory(
dbConnection,
{
region: region.id,
email: "test@testson.com",
line_items: [
{
variant_id: "test-variant",
unit_price: 100,
},
{
variant_id: "test-variant-2",
unit_price: 50,
},
],
},
100
)
const api = useApi()
await api.post(`/store/carts/${cart.id}`, {
email: "test@testson.com",
})
const failedComplete = await api
.post(`/store/carts/${cart.id}/complete`)
.catch((err) => err.response)
expect(failedComplete.status).toEqual(400)
expect(failedComplete.data.message).toEqual(
"You cannot complete a cart without a payment session."
)
await api.post(`/store/carts/${cart.id}/payment-sessions`)
const response = await api.post(`/store/carts/${cart.id}/complete`)
expect(response.status).toEqual(200)
expect(response.data.type).toEqual("order")
expect(response.data.data.tax_total).toEqual(35)
expect(response.data.data.total).toEqual(185)
expect(
response.data.data.items.flatMap((li) => li.tax_lines).length
).toEqual(2)
expect(response.data.data.items[0].tax_lines).toEqual([
expect.objectContaining({
rate: 25,
}),
])
expect(response.data.data.items[1].tax_lines).toEqual([
expect.objectContaining({
rate: 20,
}),
])
})
test("completing cart creates tax lines", async () => { test("completing cart creates tax lines", async () => {
const product1 = await simpleProductFactory( const product1 = await simpleProductFactory(
dbConnection, dbConnection,
+1
View File
@@ -11,3 +11,4 @@ export * from "./simple-tax-rate-factory"
export * from "./simple-shipping-option-factory" export * from "./simple-shipping-option-factory"
export * from "./simple-shipping-method-factory" export * from "./simple-shipping-method-factory"
export * from "./simple-product-type-tax-rate-factory" export * from "./simple-product-type-tax-rate-factory"
export * from "./simple-price-list-factory"
@@ -1,16 +1,16 @@
import { Connection } from "typeorm"
import faker from "faker"
import { Cart } from "@medusajs/medusa" import { Cart } from "@medusajs/medusa"
import faker from "faker"
import { RegionFactoryData, simpleRegionFactory } from "./simple-region-factory" import { Connection } from "typeorm"
import {
LineItemFactoryData,
simpleLineItemFactory,
} from "./simple-line-item-factory"
import { import {
AddressFactoryData, AddressFactoryData,
simpleAddressFactory, simpleAddressFactory,
} from "./simple-address-factory" } from "./simple-address-factory"
import { simpleCustomerFactory } from "./simple-customer-factory"
import {
LineItemFactoryData,
simpleLineItemFactory,
} from "./simple-line-item-factory"
import { RegionFactoryData, simpleRegionFactory } from "./simple-region-factory"
import { import {
ShippingMethodFactoryData, ShippingMethodFactoryData,
simpleShippingMethodFactory, simpleShippingMethodFactory,
@@ -18,6 +18,7 @@ import {
export type CartFactoryData = { export type CartFactoryData = {
id?: string id?: string
customer?: string | { email: string }
region?: RegionFactoryData | string region?: RegionFactoryData | string
email?: string | null email?: string | null
line_items?: LineItemFactoryData[] line_items?: LineItemFactoryData[]
@@ -43,6 +44,22 @@ export const simpleCartFactory = async (
const region = await simpleRegionFactory(connection, data.region) const region = await simpleRegionFactory(connection, data.region)
regionId = region.id regionId = region.id
} }
let customerId: string
if (typeof data.customer === "string") {
customerId = data.customer
} else {
if (data?.customer?.email) {
const customer = await simpleCustomerFactory(connection, data.customer)
customerId = customer.id
} else if (data.email) {
const customer = await simpleCustomerFactory(connection, {
email: data.email,
})
customerId = customer.id
}
}
const address = await simpleAddressFactory(connection, data.shipping_address) const address = await simpleAddressFactory(connection, data.shipping_address)
const id = data.id || `simple-cart-${Math.random() * 1000}` const id = data.id || `simple-cart-${Math.random() * 1000}`
@@ -51,6 +68,7 @@ export const simpleCartFactory = async (
email: email:
typeof data.email !== "undefined" ? data.email : faker.internet.email(), typeof data.email !== "undefined" ? data.email : faker.internet.email(),
region_id: regionId, region_id: regionId,
customer_id: customerId,
shipping_address_id: address.id, shipping_address_id: address.id,
}) })
@@ -0,0 +1,46 @@
import { Customer } from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
CustomerGroupFactoryData,
simpleCustomerGroupFactory,
} from "./simple-customer-group-factory"
export type CustomerFactoryData = {
id?: string
email?: string
groups?: CustomerGroupFactoryData[]
}
export const simpleCustomerFactory = async (
connection: Connection,
data: CustomerFactoryData = {},
seed?: number
): Promise<Customer> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
const customerId = data.id || `simple-customer-${Math.random() * 1000}`
const c = manager.create(Customer, {
id: customerId,
email: data.email,
})
const customer = await manager.save(c)
if (data.groups) {
const groups = []
for (const g of data.groups) {
const created = await simpleCustomerGroupFactory(connection, g)
groups.push(created)
}
customer.groups = groups
await manager.save(customer)
}
return customer
}
@@ -0,0 +1,31 @@
import { CustomerGroup } from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
export type CustomerGroupFactoryData = {
id?: string
name?: string
}
export const simpleCustomerGroupFactory = async (
connection: Connection,
data: CustomerGroupFactoryData = {},
seed?: number
): Promise<CustomerGroup> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
const customerGroupId =
data.id || `simple-customer-group-${Math.random() * 1000}`
const c = manager.create(CustomerGroup, {
id: customerGroupId,
name: data.name,
})
const group = await manager.save(c)
return group
}
@@ -0,0 +1,116 @@
import {
DiscountCondition,
DiscountConditionOperator,
DiscountConditionType,
} from "@medusajs/medusa/dist/models/discount-condition"
import { DiscountConditionCustomerGroup } from "@medusajs/medusa/dist/models/discount-condition-customer-group"
import { DiscountConditionProduct } from "@medusajs/medusa/dist/models/discount-condition-product"
import { DiscountConditionProductCollection } from "@medusajs/medusa/dist/models/discount-condition-product-collection"
import { DiscountConditionProductTag } from "@medusajs/medusa/dist/models/discount-condition-product-tag"
import { DiscountConditionProductType } from "@medusajs/medusa/dist/models/discount-condition-product-type"
import { DiscountConditionJoinTableForeignKey } from "@medusajs/medusa/dist/repositories/discount-condition"
import faker from "faker"
import { Connection } from "typeorm"
export type DiscuntConditionFactoryData = {
rule_id: string
type: DiscountConditionType
operator: DiscountConditionOperator
products: string[]
product_collections: string[]
product_types: string[]
product_tags: string[]
customer_groups: string[]
}
const getJoinTableResourceIdentifiers = (type: string) => {
let conditionTable: any
let resourceKey
switch (type) {
case DiscountConditionType.PRODUCTS: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_ID
conditionTable = DiscountConditionProduct
break
}
case DiscountConditionType.PRODUCT_TYPES: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_TYPE_ID
conditionTable = DiscountConditionProductType
break
}
case DiscountConditionType.PRODUCT_COLLECTIONS: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_COLLECTION_ID
conditionTable = DiscountConditionProductCollection
break
}
case DiscountConditionType.PRODUCT_TAGS: {
resourceKey = DiscountConditionJoinTableForeignKey.PRODUCT_TAG_ID
conditionTable = DiscountConditionProductTag
break
}
case DiscountConditionType.CUSTOMER_GROUPS: {
resourceKey = DiscountConditionJoinTableForeignKey.CUSTOMER_GROUP_ID
conditionTable = DiscountConditionCustomerGroup
break
}
default:
break
}
return {
resourceKey,
conditionTable,
}
}
export const simpleDiscountConditionFactory = async (
connection: Connection,
data: DiscuntConditionFactoryData,
seed?: number
): Promise<void> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
let resources = []
if (data.products) {
resources = data.products
}
if (data.product_collections) {
resources = data.product_collections
}
if (data.product_types) {
resources = data.product_types
}
if (data.product_tags) {
resources = data.product_tags
}
if (data.customer_groups) {
resources = data.customer_groups
}
const condToSave = manager.create(DiscountCondition, {
type: data.type,
operator: data.operator,
discount_rule_id: data.rule_id,
})
const { conditionTable, resourceKey } = getJoinTableResourceIdentifiers(
data.type
)
const condition = await manager.save(condToSave)
for (const resourceCond of resources) {
const toSave = manager.create(conditionTable, {
[resourceKey]: resourceCond,
condition_id: condition.id,
})
await manager.save(toSave)
}
}
@@ -1,16 +1,21 @@
import { Connection } from "typeorm"
import faker from "faker"
import { import {
AllocationType,
Discount, Discount,
DiscountRule, DiscountRule,
DiscountRuleType, DiscountRuleType,
AllocationType,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
DiscuntConditionFactoryData,
simpleDiscountConditionFactory,
} from "./simple-discount-condition-factory"
export type DiscountRuleFactoryData = { export type DiscountRuleFactoryData = {
type?: DiscountRuleType type?: DiscountRuleType
value?: number value?: number
allocation?: AllocationType allocation?: AllocationType
conditions: DiscuntConditionFactoryData[]
} }
export type DiscountFactoryData = { export type DiscountFactoryData = {
@@ -41,6 +46,16 @@ export const simpleDiscountFactory = async (
const dRule = await manager.save(ruleToSave) const dRule = await manager.save(ruleToSave)
if (data?.rule?.conditions) {
for (const condition of data.rule.conditions) {
await simpleDiscountConditionFactory(
connection,
{ ...condition, rule_id: dRule.id },
1
)
}
}
const toSave = manager.create(Discount, { const toSave = manager.create(Discount, {
id: data.id, id: data.id,
is_dynamic: data.is_dynamic ?? false, is_dynamic: data.is_dynamic ?? false,
@@ -1,6 +1,6 @@
import { Connection } from "typeorm" import { Connection } from "typeorm"
import faker from "faker" import faker from "faker"
import { LineItem, LineItemTaxLine } from "@medusajs/medusa" import { LineItem, LineItemAdjustment, LineItemTaxLine } from "@medusajs/medusa"
type TaxLineFactoryData = { type TaxLineFactoryData = {
rate: number rate: number
@@ -8,6 +8,11 @@ type TaxLineFactoryData = {
name: string name: string
} }
type LineItemAdjustmentFactoryData = Omit<LineItemAdjustment, "discount_id"> & {
discount_id: string
discount_code: string
}
export type LineItemFactoryData = { export type LineItemFactoryData = {
id?: string id?: string
cart_id?: string cart_id?: string
@@ -24,6 +29,7 @@ export type LineItemFactoryData = {
shipped_quantity?: boolean shipped_quantity?: boolean
returned_quantity?: boolean returned_quantity?: boolean
tax_lines?: TaxLineFactoryData[] tax_lines?: TaxLineFactoryData[]
adjustments: LineItemAdjustmentFactoryData[]
} }
export const simpleLineItemFactory = async ( export const simpleLineItemFactory = async (
@@ -63,6 +69,7 @@ export const simpleLineItemFactory = async (
fulfilled_quantity: data.fulfilled_quantity || null, fulfilled_quantity: data.fulfilled_quantity || null,
shipped_quantity: data.shipped_quantity || null, shipped_quantity: data.shipped_quantity || null,
returned_quantity: data.returned_quantity || null, returned_quantity: data.returned_quantity || null,
adjustments: data.adjustments,
}) })
const line = await manager.save(toSave) const line = await manager.save(toSave)
@@ -101,7 +101,17 @@ export const simpleOrderFactory = async (
await simpleShippingMethodFactory(connection, { ...sm, order_id: order.id }) await simpleShippingMethodFactory(connection, { ...sm, order_id: order.id })
} }
const items = data.line_items const items = data.line_items.map((item) => {
let adjustments = item?.adjustments || []
return {
...item,
adjustments: adjustments.map((adj) => ({
...adj,
discount_id: discounts.find((d) => d.code === adj?.discount_code),
})),
}
})
for (const item of items) { for (const item of items) {
await simpleLineItemFactory(connection, { ...item, order_id: id }) await simpleLineItemFactory(connection, { ...item, order_id: id })
} }
@@ -0,0 +1,66 @@
import {
PriceList,
MoneyAmount,
PriceListType,
PriceListStatus,
} from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
type ProductListPrice = {
variant_id: string
currency_code: string
region_id: string
amount: number
}
export type PriceListFactoryData = {
id?: string
name?: string
description?: string
type?: PriceListType
status?: PriceListStatus
starts_at?: Date
ends_at?: Date
customer_groups?: string[]
prices?: ProductListPrice[]
}
export const simplePriceListFactory = async (
connection: Connection,
data: PriceListFactoryData = {},
seed?: number
): Promise<PriceList> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
const manager = connection.manager
const listId = data.id || `simple-price-list-${Math.random() * 1000}`
const toCreate = {
id: listId,
name: data.name || faker.commerce.productName(),
description: data.description || "Some text",
status: data.status || PriceListStatus.ACTIVE,
type: data.type || PriceListType.OVERRIDE,
starts_at: data.starts_at || null,
ends_at: data.ends_at || null,
}
const toSave = manager.create(PriceList, toCreate)
const toReturn = await manager.save(toSave)
if (typeof data.prices !== "undefined") {
for (const ma of data.prices) {
const factoryData = {
...ma,
price_list_id: listId,
}
const toSave = manager.create(MoneyAmount, factoryData)
await manager.save(toSave)
}
}
return toReturn
}
@@ -1,23 +1,25 @@
import { Connection } from "typeorm"
import faker from "faker"
import { import {
ShippingProfileType,
ShippingProfile,
Product, Product,
ProductType,
ProductOption, ProductOption,
ProductTag,
ProductType,
ShippingProfile,
ShippingProfileType,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import { import {
simpleProductVariantFactory,
ProductVariantFactoryData, ProductVariantFactoryData,
simpleProductVariantFactory,
} from "./simple-product-variant-factory" } from "./simple-product-variant-factory"
export type ProductFactoryData = { export type ProductFactoryData = {
id?: string id?: string
is_giftcard?: boolean is_giftcard?: boolean
status?: string
title?: string title?: string
type?: string type?: string
tags?: string[]
options?: { id: string; title: string }[] options?: { id: string; title: string }[]
variants?: ProductVariantFactoryData[] variants?: ProductVariantFactoryData[]
} }
@@ -26,7 +28,7 @@ export const simpleProductFactory = async (
connection: Connection, connection: Connection,
data: ProductFactoryData = {}, data: ProductFactoryData = {},
seed?: number seed?: number
): Promise<Product> => { ): Promise<Product | undefined> => {
if (typeof seed !== "undefined") { if (typeof seed !== "undefined") {
faker.seed(seed) faker.seed(seed)
} }
@@ -41,26 +43,41 @@ export const simpleProductFactory = async (
type: ShippingProfileType.GIFT_CARD, type: ShippingProfileType.GIFT_CARD,
}) })
let typeId: string const prodId = data.id || `simple-product-${Math.random() * 1000}`
const productToCreate = {
id: prodId,
title: data.title || faker.commerce.productName(),
status: data.status,
is_giftcard: data.is_giftcard || false,
discountable: !data.is_giftcard,
tags: [] as ProductTag[],
profile_id: data.is_giftcard ? gcProfile?.id : defaultProfile?.id,
} as Product
if (typeof data.tags !== "undefined") {
for (let i = 0; i < data.tags.length; i++) {
const createdTag = manager.create(ProductTag, {
id: `tag-${Math.random() * 1000}`,
value: data.tags[i],
})
const tagRes = await manager.save(createdTag)
productToCreate.tags.push(tagRes)
}
}
if (typeof data.type !== "undefined") { if (typeof data.type !== "undefined") {
const toSave = manager.create(ProductType, { const toSave = manager.create(ProductType, {
value: data.type, value: data.type,
}) })
const res = await manager.save(toSave) const res = await manager.save(toSave)
typeId = res.id productToCreate["type_id"] = res.id
} }
const prodId = data.id || `simple-product-${Math.random() * 1000}` const toSave = manager.create(Product, productToCreate)
const toSave = manager.create(Product, {
id: prodId,
type_id: typeId,
title: data.title || faker.commerce.productName(),
is_giftcard: data.is_giftcard || false,
discountable: !data.is_giftcard,
profile_id: data.is_giftcard ? gcProfile.id : defaultProfile.id,
})
const product = await manager.save(toSave) await manager.save(toSave)
const optionId = `${prodId}-option` const optionId = `${prodId}-option`
const options = data.options || [{ id: optionId, title: "Size" }] const options = data.options || [{ id: optionId, title: "Size" }]
@@ -95,5 +112,5 @@ export const simpleProductFactory = async (
await simpleProductVariantFactory(connection, factoryData) await simpleProductVariantFactory(connection, factoryData)
} }
return product return manager.findOne(Product, { id: prodId }, { relations: ["tags"] })
} }
+373 -3
View File
@@ -14,6 +14,8 @@ const {
LineItem, LineItem,
Payment, Payment,
PaymentSession, PaymentSession,
CustomerGroup,
PriceList,
} = require("@medusajs/medusa") } = require("@medusajs/medusa")
module.exports = async (connection, data = {}) => { module.exports = async (connection, data = {}) => {
@@ -52,6 +54,8 @@ module.exports = async (connection, data = {}) => {
tax_rate: 0, tax_rate: 0,
}) })
await manager.save(r)
// Region with multiple countries // Region with multiple countries
const regionWithMultipleCoutries = manager.create(Region, { const regionWithMultipleCoutries = manager.create(Region, {
id: "test-region-multiple", id: "test-region-multiple",
@@ -68,6 +72,38 @@ module.exports = async (connection, data = {}) => {
`UPDATE "country" SET region_id='test-region-multiple' WHERE iso_2 = 'dk'` `UPDATE "country" SET region_id='test-region-multiple' WHERE iso_2 = 'dk'`
) )
const customer5 = await manager.create(Customer, {
id: "test-customer-5",
email: "test5@email.com",
first_name: "John",
last_name: "Deere",
password_hash:
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN", // password matching "test"
has_account: true,
})
await manager.save(customer5)
const c_group_5 = await manager.create(CustomerGroup, {
id: "test-group-5",
name: "test-group-5",
})
await manager.save(c_group_5)
customer5.groups = [c_group_5]
await manager.save(customer5)
const priceList_customer = await manager.create(PriceList, {
id: "pl_customer",
name: "VIP winter sale",
description: "Winter sale for VIP customers.",
type: "sale",
status: "active",
})
priceList_customer.customer_groups = [c_group_5]
await manager.save(priceList_customer)
const freeRule = manager.create(DiscountRule, { const freeRule = manager.create(DiscountRule, {
id: "free-shipping-rule", id: "free-shipping-rule",
description: "Free shipping rule", description: "Free shipping rule",
@@ -108,6 +144,69 @@ module.exports = async (connection, data = {}) => {
tenPercent.rule = tenPercentRule tenPercent.rule = tenPercentRule
await manager.save(tenPercent) await manager.save(tenPercent)
const totalFixed100Rule = manager.create(DiscountRule, {
id: "total-fixed-100-rule",
description: "Fixed 100 total",
type: "fixed",
value: 100,
allocation: "total",
})
const totalFixed100 = manager.create(Discount, {
id: "total-fixed-100",
code: "FIXED100",
is_dynamic: false,
is_disabled: false,
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
})
totalFixed100.regions = [r]
totalFixed100.rule = totalFixed100Rule
await manager.save(totalFixed100)
const itemFixed200Rule = manager.create(DiscountRule, {
id: "item-fixed-200-rule",
description: "Item 200 fixed",
type: "fixed",
value: 200,
allocation: "item",
})
const itemFixed200 = manager.create(Discount, {
id: "item-fixed-200",
code: "FIXED200",
is_dynamic: false,
is_disabled: false,
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
})
itemFixed200.regions = [r]
itemFixed200.rule = itemFixed200Rule
await manager.save(itemFixed200)
const itemPerc15Rule = manager.create(DiscountRule, {
id: "item-percentage-15-rule",
description: "Item 15 percentage",
type: "percentage",
value: 15,
allocation: "item",
})
const itemPerc15 = manager.create(Discount, {
id: "item-percentage-15",
code: "15PERCENT",
is_dynamic: false,
is_disabled: false,
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
})
itemPerc15.regions = [r]
itemPerc15.rule = itemPerc15Rule
await manager.save(itemPerc15)
const dUsageLimit = await manager.create(Discount, { const dUsageLimit = await manager.create(Discount, {
id: "test-discount-usage-limit", id: "test-discount-usage-limit",
code: "SPENT", code: "SPENT",
@@ -240,7 +339,7 @@ module.exports = async (connection, data = {}) => {
is_disabled: false, is_disabled: false,
starts_at: tenDaysAgo, starts_at: tenDaysAgo,
ends_at: tenDaysFromToday, ends_at: tenDaysFromToday,
valid_duration: "P1M", //one month valid_duration: "P1M", // one month
}) })
DynamicDiscount.regions = [r] DynamicDiscount.regions = [r]
@@ -256,11 +355,21 @@ module.exports = async (connection, data = {}) => {
email: "test@email.com", email: "test@email.com",
}) })
await manager.insert(Customer, { const c2 = await manager.create(Customer, {
id: "test-customer-2", id: "test-customer-2",
email: "test-2@email.com", email: "test-2@email.com",
}) })
const cg = await manager.create(CustomerGroup, {
id: "cgroup",
name: "customer group",
})
await manager.save(cg)
c2.groups = [cg]
await manager.save(c2)
await manager.insert(Customer, { await manager.insert(Customer, {
id: "some-customer", id: "some-customer",
email: "some-customer@email.com", email: "some-customer@email.com",
@@ -299,6 +408,28 @@ module.exports = async (connection, data = {}) => {
data: {}, data: {},
}) })
const priceList = await manager.create(PriceList, {
id: "pl",
name: "VIP winter sale",
description: "Winter sale for VIP customers.",
type: "sale",
status: "active",
})
await manager.save(priceList)
const priceList1 = await manager.create(PriceList, {
id: "pl_current",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
})
await manager.save(priceList1)
await manager.insert(Product, { await manager.insert(Product, {
id: "giftcard-product", id: "giftcard-product",
title: "Giftcard", title: "Giftcard",
@@ -328,6 +459,45 @@ module.exports = async (connection, data = {}) => {
options: [{ id: "test-option", title: "Size" }], options: [{ id: "test-option", title: "Size" }],
}) })
await manager.insert(ProductVariant, {
id: "test-variant-quantity",
title: "test variant",
product_id: "test-product",
inventory_quantity: 1000,
options: [
{
option_id: "test-option",
value: "Size",
},
],
})
await manager.insert(ProductVariant, {
id: "test-variant-sale",
title: "test variant",
product_id: "test-product",
inventory_quantity: 1000,
options: [
{
option_id: "test-option",
value: "Size",
},
],
})
await manager.insert(ProductVariant, {
id: "test-variant-sale-customer",
title: "test variant",
product_id: "test-product",
inventory_quantity: 1000,
options: [
{
option_id: "test-option",
value: "Size",
},
],
})
await manager.insert(ProductVariant, { await manager.insert(ProductVariant, {
id: "test-variant", id: "test-variant",
title: "test variant", title: "test variant",
@@ -357,14 +527,82 @@ module.exports = async (connection, data = {}) => {
const ma = manager.create(MoneyAmount, { const ma = manager.create(MoneyAmount, {
variant_id: "test-variant", variant_id: "test-variant",
currency_code: "usd", currency_code: "usd",
type: "default",
amount: 1000, amount: 1000,
}) })
await manager.save(ma) await manager.save(ma)
const ma_sale = manager.create(MoneyAmount, {
variant_id: "test-variant-sale",
currency_code: "usd",
amount: 1000,
})
await manager.save(ma_sale)
const ma_sale_1 = manager.create(MoneyAmount, {
variant_id: "test-variant-sale",
currency_code: "usd",
amount: 800,
price_list_id: "pl_current",
})
await manager.save(ma_sale_1)
const ma_sale_customer = manager.create(MoneyAmount, {
variant_id: "test-variant-sale-customer",
currency_code: "usd",
amount: 1000,
})
await manager.save(ma_sale_customer)
const ma_sale_customer_1 = manager.create(MoneyAmount, {
variant_id: "test-variant-sale-customer",
currency_code: "usd",
amount: 700,
price_list_id: "pl_customer",
})
await manager.save(ma_sale_customer_1)
const ma_sale_customer_2 = manager.create(MoneyAmount, {
variant_id: "test-variant-sale-customer",
currency_code: "usd",
amount: 800,
price_list_id: "pl_current",
})
await manager.save(ma_sale_customer_2)
const ma_quantity = manager.create(MoneyAmount, {
variant_id: "test-variant-quantity",
currency_code: "usd",
type: "default",
amount: 1000,
})
await manager.save(ma_quantity)
const ma_quantity_1 = manager.create(MoneyAmount, {
variant_id: "test-variant-quantity",
currency_code: "usd",
type: "sale",
min_quantity: 10,
max_quantity: 100,
amount: 800,
})
await manager.save(ma_quantity_1)
const ma_quantity_2 = manager.create(MoneyAmount, {
variant_id: "test-variant-quantity",
currency_code: "usd",
type: "sale",
min_quantity: 100,
amount: 700,
})
await manager.save(ma_quantity_2)
const ma2 = manager.create(MoneyAmount, { const ma2 = manager.create(MoneyAmount, {
variant_id: "test-variant-2", variant_id: "test-variant-2",
currency_code: "usd", currency_code: "usd",
type: "default",
amount: 8000, amount: 8000,
}) })
@@ -373,6 +611,7 @@ module.exports = async (connection, data = {}) => {
const ma3 = manager.create(MoneyAmount, { const ma3 = manager.create(MoneyAmount, {
variant_id: "giftcard-denom", variant_id: "giftcard-denom",
currency_code: "usd", currency_code: "usd",
type: "default",
amount: 1000, amount: 1000,
}) })
@@ -394,6 +633,74 @@ module.exports = async (connection, data = {}) => {
await manager.save(cart) await manager.save(cart)
const cartWithTotalFixedDiscount = manager.create(Cart, {
id: "test-cart-w-total-fixed-discount",
customer_id: "some-customer",
email: "some-customer@email.com",
discounts: [totalFixed100],
shipping_address: {
id: "test-shipping-address",
first_name: "lebron",
country_code: "us",
},
region_id: "test-region",
currency_code: "usd",
items: [],
})
await manager.save(cartWithTotalFixedDiscount)
const cartWithItemFixedDiscount = manager.create(Cart, {
id: "test-cart-w-item-fixed-discount",
customer_id: "some-customer",
email: "some-customer@email.com",
discounts: [itemFixed200],
shipping_address: {
id: "test-shipping-address",
first_name: "lebron",
country_code: "us",
},
region_id: "test-region",
currency_code: "usd",
items: [],
})
await manager.save(cartWithItemFixedDiscount)
const cartWithTotalPercDiscount = manager.create(Cart, {
id: "test-cart-w-total-percentage-discount",
customer_id: "some-customer",
email: "some-customer@email.com",
discounts: [tenPercent],
shipping_address: {
id: "test-shipping-address",
first_name: "lebron",
country_code: "us",
},
region_id: "test-region",
currency_code: "usd",
items: [],
})
await manager.save(cartWithTotalPercDiscount)
const cartWithItemPercDiscount = manager.create(Cart, {
id: "test-cart-w-item-percentage-discount",
customer_id: "some-customer",
email: "some-customer@email.com",
discounts: [itemPerc15],
shipping_address: {
id: "test-shipping-address",
first_name: "lebron",
country_code: "us",
},
region_id: "test-region",
currency_code: "usd",
items: [],
})
await manager.save(cartWithItemPercDiscount)
const cart2 = manager.create(Cart, { const cart2 = manager.create(Cart, {
id: "test-cart-2", id: "test-cart-2",
customer_id: "some-customer", customer_id: "some-customer",
@@ -533,4 +840,67 @@ module.exports = async (connection, data = {}) => {
cart_id: "test-cart-3", cart_id: "test-cart-3",
}) })
await manager.save(li2) await manager.save(li2)
const cart4 = manager.create(Cart, {
id: "test-cart-3",
email: "some-customer@email.com",
shipping_address: {
id: "test-shipping-address",
first_name: "lebron",
country_code: "us",
},
region_id: "test-region",
currency_code: "usd",
completed_at: null,
items: [],
})
await manager.save(cart4)
await manager.insert(ProductVariant, {
id: "test-variant-sale-cg",
title: "test variant",
product_id: "test-product",
inventory_quantity: 1000,
options: [
{
option_id: "test-option",
value: "Size",
},
],
})
const ma_cg = manager.create(MoneyAmount, {
variant_id: "test-variant-sale-cg",
currency_code: "usd",
amount: 1000,
})
await manager.save(ma_cg)
const ma_sale_cg = manager.create(MoneyAmount, {
variant_id: "test-variant-sale-cg",
currency_code: "usd",
price_list_id: "pl",
amount: 500,
})
await manager.save(ma_sale_cg)
const ma_sale_cg_new_region = manager.create(MoneyAmount, {
variant_id: "test-variant-sale-cg",
region_id: "test-region-multiple",
currency_code: "eur",
amount: 700,
})
await manager.save(ma_sale_cg_new_region)
const li3 = await manager.create(LineItem, {
id: "test-item3",
title: "Line Item",
description: "Line Item Desc",
thumbnail: "https://test.js/1234",
unit_price: 8000,
quantity: 1,
variant_id: "test-variant-sale-cg",
cart_id: "test-cart-3",
})
await manager.save(li3)
} }
@@ -36,10 +36,29 @@ module.exports = async (connection, data = {}) => {
has_account: true, has_account: true,
}) })
const customer5 = await manager.create(Customer, {
id: "test-customer-5",
email: "test5@email.com",
})
await manager.save(customer5)
const customer6 = await manager.create(Customer, {
id: "test-customer-6",
email: "test6@email.com",
})
await manager.save(customer6)
const customer7 = await manager.create(Customer, {
id: "test-customer-7",
email: "test7@email.com",
})
await manager.save(customer7)
const deletionCustomer = await manager.create(Customer, { const deletionCustomer = await manager.create(Customer, {
id: "test-customer-delete-cg", id: "test-customer-delete-cg",
email: "test-deletetion-cg@email.com", email: "test-deletetion-cg@email.com",
}) })
await manager.save(deletionCustomer)
await manager.insert(CustomerGroup, { await manager.insert(CustomerGroup, {
id: "customer-group-1", id: "customer-group-1",
@@ -54,7 +73,7 @@ module.exports = async (connection, data = {}) => {
await manager.insert(CustomerGroup, { await manager.insert(CustomerGroup, {
id: "customer-group-3", id: "customer-group-3",
name: "vest-group-3", name: "test-group-3",
}) })
await manager.insert(CustomerGroup, { await manager.insert(CustomerGroup, {
@@ -62,30 +81,17 @@ module.exports = async (connection, data = {}) => {
name: "test-group-4", name: "test-group-4",
}) })
const customer5 = manager.create(Customer, { const c_group_5 = await manager.create(CustomerGroup, {
id: "test-customer-5",
email: "test5@email.com",
})
const customer6 = manager.create(Customer, {
id: "test-customer-6",
email: "test6@email.com",
})
const customer7 = manager.create(Customer, {
id: "test-customer-7",
email: "test7@email.com",
})
const c_group_5 = manager.create(CustomerGroup, {
id: "test-group-5", id: "test-group-5",
name: "test-group-5", name: "test-group-5",
}) })
await manager.save(c_group_5)
const c_group_6 = manager.create(CustomerGroup, { const c_group_6 = await manager.create(CustomerGroup, {
id: "test-group-6", id: "test-group-6",
name: "test-group-6", name: "test-group-6",
}) })
await manager.save(c_group_6)
customer5.groups = [c_group_5] customer5.groups = [c_group_5]
await manager.save(customer5) await manager.save(customer5)
@@ -96,10 +102,11 @@ module.exports = async (connection, data = {}) => {
customer7.groups = [c_group_5, c_group_6] customer7.groups = [c_group_5, c_group_6]
await manager.save(customer7) await manager.save(customer7)
const c_group_delete = manager.create(CustomerGroup, { const c_group_delete = await manager.create(CustomerGroup, {
id: "test-group-delete", id: "test-group-delete",
name: "test-group-delete", name: "test-group-delete",
}) })
await manager.save(c_group_delete)
deletionCustomer.groups = [c_group_delete] deletionCustomer.groups = [c_group_delete]
await manager.save(deletionCustomer) await manager.save(deletionCustomer)
@@ -173,6 +173,14 @@ module.exports = async (connection, data = {}) => {
quantity: 1, quantity: 1,
variant_id: "test-variant", variant_id: "test-variant",
order_id: "test-order", order_id: "test-order",
adjustments: [
{
amount: 800,
discount_id: "test-discount",
description: "discount",
item_id: "test-item",
},
],
}) })
await manager.save(li) await manager.save(li)
@@ -0,0 +1,53 @@
const { PriceList, MoneyAmount } = require("@medusajs/medusa")
module.exports = async (connection, data = {}) => {
const manager = connection.manager
const priceListNoCustomerGroups = await manager.create(PriceList, {
id: "pl_no_customer_groups",
name: "VIP winter sale",
description: "Winter sale for VIP customers. 25% off selected items.",
type: "sale",
status: "active",
starts_at: "2022-07-01T00:00:00.000Z",
ends_at: "2022-07-31T00:00:00.000Z",
})
await manager.save(priceListNoCustomerGroups)
const moneyAmount1 = await manager.create(MoneyAmount, {
id: "ma_test_1",
amount: 100,
currency_code: "usd",
min_quantity: 1,
max_quantity: 100,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
})
await manager.save(moneyAmount1)
const moneyAmount2 = await manager.create(MoneyAmount, {
id: "ma_test_2",
amount: 80,
currency_code: "usd",
min_quantity: 101,
max_quantity: 500,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
})
await manager.save(moneyAmount2)
const moneyAmount3 = await manager.create(MoneyAmount, {
id: "ma_test_3",
amount: 50,
currency_code: "usd",
min_quantity: 501,
max_quantity: 1000,
variant_id: "test-variant",
price_list_id: "pl_no_customer_groups",
})
await manager.save(moneyAmount3)
}
@@ -0,0 +1,883 @@
const {
Customer,
CustomerGroup,
ShippingProfile,
Product,
ProductVariant,
ProductCollection,
ProductOption,
Region,
Cart,
PriceList,
} = require("@medusajs/medusa")
module.exports = async (connection, data = {}) => {
const yesterday = ((today) => new Date(today.setDate(today.getDate() - 1)))(
new Date()
)
const tomorrow = ((today) => new Date(today.setDate(today.getDate() + 1)))(
new Date()
)
const tenDaysAgo = ((today) => new Date(today.setDate(today.getDate() - 10)))(
new Date()
)
const tenDaysFromToday = ((today) =>
new Date(today.setDate(today.getDate() + 10)))(new Date())
const manager = connection.manager
const defaultProfile = await manager.findOne(ShippingProfile, {
type: "default",
})
await manager.insert(Region, {
id: "test-region",
name: "Test Region",
currency_code: "usd",
tax_rate: 0,
})
await manager.insert(Region, {
id: "test-region-1",
name: "Test Region no prices",
currency_code: "usd",
tax_rate: 0,
})
await manager.insert(Region, {
id: "test-region-2",
name: "Test Region no prices",
currency_code: "dkk",
tax_rate: 0,
})
const coll = manager.create(ProductCollection, {
id: "test-collection",
handle: "test-collection",
title: "Test collection",
})
await manager.save(coll)
const customer5 = await manager.create(Customer, {
id: "test-customer-5",
email: "test5@email.com",
first_name: "John",
last_name: "Deere",
password_hash:
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN", // password matching "test"
has_account: true,
})
await manager.save(customer5)
const customer6 = await manager.create(Customer, {
id: "test-customer-6",
password_hash:
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN", // password matching "test"
email: "test6@email.com",
has_account: true,
})
await manager.save(customer6)
const customer7 = await manager.create(Customer, {
id: "test-customer-7",
password_hash:
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN", // password matching "test"
email: "test7@email.com",
has_account: true,
})
await manager.save(customer7)
const c_group_5 = await manager.create(CustomerGroup, {
id: "test-group-5",
name: "test-group-5",
})
await manager.save(c_group_5)
const c_group_6 = await manager.create(CustomerGroup, {
id: "test-group-6",
name: "test-group-6",
})
await manager.save(c_group_6)
customer5.groups = [c_group_5]
await manager.save(customer5)
customer6.groups = [c_group_6]
await manager.save(customer6)
customer7.groups = [c_group_5, c_group_6]
await manager.save(customer7)
const priceList = await manager.create(PriceList, {
id: "pl",
name: "VIP winter sale",
description: "Winter sale for VIP customers.",
type: "sale",
status: "active",
})
await manager.save(priceList)
const priceList1 = await manager.create(PriceList, {
id: "pl_1",
name: "VIP winter sale",
description: "Winter sale for VIP customers.",
type: "sale",
status: "active",
})
priceList1.customer_groups = [c_group_5]
await manager.save(priceList1)
const priceList2 = await manager.create(PriceList, {
id: "pl_2",
name: "VVIP winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
})
priceList2.customer_groups = [c_group_6]
await manager.save(priceList2)
const priceList3 = await manager.create(PriceList, {
id: "pl_expired",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: yesterday,
})
await manager.save(priceList3)
const priceList4 = await manager.create(PriceList, {
id: "pl_upcoming",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tomorrow,
ends_at: tenDaysFromToday,
})
await manager.save(priceList4)
const priceList5 = await manager.create(PriceList, {
id: "pl_current",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
})
await manager.save(priceList5)
const priceList6 = await manager.create(PriceList, {
id: "pl_current_1",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: tomorrow,
})
await manager.save(priceList6)
const priceList7 = await manager.create(PriceList, {
id: "pl_upcoming-customer",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tomorrow,
ends_at: tenDaysFromToday,
})
priceList7.customer_groups = [c_group_5]
await manager.save(priceList7)
const priceList8 = await manager.create(PriceList, {
id: "pl_current-customer",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
})
priceList8.customer_groups = [c_group_5]
await manager.save(priceList8)
const priceList9 = await manager.create(PriceList, {
id: "pl_expired-customer",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: yesterday,
})
priceList9.customer_groups = [c_group_5]
await manager.save(priceList9)
const p1 = await manager.create(Product, {
id: "test-product",
handle: "test-product",
title: "Test product",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p1)
await manager.save(ProductOption, {
id: "test-option",
title: "test-option",
product_id: "test-product",
})
const variant4 = await manager.create(ProductVariant, {
id: "test-variant",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku",
ean: "test-ean",
status: "published",
upc: "test-upc",
product_id: "test-product",
prices: [
{
id: "test-price",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl_1",
},
{
id: "test-price1",
region_id: "test-region",
currency_code: "usd",
amount: 120,
},
{
id: "test-price2",
region_id: "test-region",
currency_code: "usd",
amount: 130,
price_list_id: "pl_2",
},
{
id: "test-price3",
region_id: "test-region",
currency_code: "usd",
amount: 110,
price_list_id: "pl",
},
],
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option",
},
],
})
await manager.save(variant4)
const p2 = await manager.create(Product, {
id: "test-product-quantity",
handle: "test-product-quantity",
title: "Test product",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p2)
await manager.save(ProductOption, {
id: "test-option",
title: "test-option",
product_id: "test-product-quantity",
})
const variant_quantity = await manager.create(ProductVariant, {
id: "test-variant-quantity",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-quantity",
ean: "test-ean-quantity",
status: "published",
upc: "test-upc-quantity",
product_id: "test-product-quantity",
prices: [
{
id: "test-price-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl",
min_quantity: 10,
max_quantity: 100,
},
{
id: "test-price1-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl",
min_quantity: 101,
max_quantity: 1000,
},
{
id: "test-price1-quantity-group",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_1",
},
{
id: "test-price2-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 130,
price_list_id: "pl",
max_quantity: 9,
},
{
id: "test-price3-quantity-expired",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_expired",
},
{
id: "test-price3-quantity-future",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_upcoming",
},
{
id: "test-price3-quantity-now",
region_id: "test-region",
currency_code: "usd",
amount: 140,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_current",
},
{
id: "test-price3-quantity-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
},
],
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option",
},
],
})
await manager.save(variant_quantity)
const p3 = await manager.create(Product, {
id: "test-product-sale",
handle: "test-product-sale",
title: "Test product sale",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p3)
await manager.save(ProductOption, {
id: "test-option-sale",
title: "test-option sale",
product_id: "test-product-sale",
})
const variant_sale = await manager.create(ProductVariant, {
id: "test-variant-sale",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-sale",
ean: "test-ean-sale",
status: "published",
upc: "test-upc-sale",
product_id: "test-product-sale",
prices: [
{
id: "test-price-sale",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl_expired",
},
{
id: "test-price1-sale",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl_current",
},
{
id: "test-price2-sale",
region_id: "test-region",
currency_code: "usd",
amount: 130,
price_list_id: "pl_upcoming",
},
{
id: "test-price2-sale-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
},
],
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option",
},
],
})
await manager.save(variant_sale)
const p4 = await manager.create(Product, {
id: "test-product-sale-overlap",
handle: "test-product-sale-overlap",
title: "Test product sale",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p4)
await manager.save(ProductOption, {
id: "test-option-sale-overlap",
title: "test-option sale",
product_id: "test-product-sale-overlap",
})
const variant_sale_overlap = await manager.create(ProductVariant, {
id: "test-variant-sale-overlap",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-sale-overlap",
ean: "test-ean-sale-overlap",
status: "published",
upc: "test-upc-sale-overlap",
product_id: "test-product-sale-overlap",
prices: [
{
id: "test-price-sale-overlap-1",
region_id: "test-region",
currency_code: "usd",
amount: 140,
price_list_id: "pl_current_1",
},
{
id: "test-price1-sale-overlap",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl_current",
},
{
id: "test-price2-sale-overlap-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
},
],
options: [
{
id: "test-variant-option-overlap",
value: "Default variant",
option_id: "test-option-sale-overlap",
},
],
})
await manager.save(variant_sale_overlap)
const multiRegionProduct = await manager.create(Product, {
id: "test-product-multi-region",
handle: "test-product-multi-region",
title: "Test product",
profile_id: defaultProfile.id,
description: "test-product-multi-region-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(multiRegionProduct)
await manager.save(ProductOption, {
id: "test-option-multi-region",
title: "test-option",
product_id: "test-product-multi-region",
})
const variant_multi_region = await manager.create(ProductVariant, {
id: "test-variant-multi-region",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-multi-region",
ean: "test-ean-multi-region",
status: "published",
upc: "test-upc-multi-region",
product_id: "test-product-multi-region",
prices: [
{
id: "test-price-region-1",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl",
},
{
id: "test-price1-region-1",
region_id: "test-region",
currency_code: "usd",
amount: 120,
},
{
id: "test-price1-region-2",
region_id: "test-region-2",
currency_code: "dkk",
amount: 130,
},
{
id: "test-price3-region-2",
region_id: "test-region-2",
currency_code: "dkk",
price_list_id: "pl",
amount: 110,
},
],
options: [
{
id: "test-variant-option-multi-region",
value: "Default variant",
option_id: "test-option-multi-region",
},
],
})
await manager.save(variant_multi_region)
const p5 = await manager.create(Product, {
id: "test-product-quantity-customer",
handle: "test-product-quantity-customer",
title: "Test product",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p5)
await manager.save(ProductOption, {
id: "test-option-quantity-customer",
title: "test-option",
product_id: "test-product-quantity-customer",
})
const variant_quantity_customer = await manager.create(ProductVariant, {
id: "test-variant-quantity-customer",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-quantity-customer",
ean: "test-ean-quantity-customer",
status: "published",
upc: "test-upc-quantity-customer",
product_id: "test-product-quantity-customer",
prices: [
{
id: "test-price-quantity-customer",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl",
min_quantity: 10,
max_quantity: 100,
},
{
id: "test-price1-quantity-customer",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl",
min_quantity: 101,
max_quantity: 1000,
},
{
id: "test-price2-quantity-customer",
region_id: "test-region",
currency_code: "usd",
amount: 130,
price_list_id: "pl",
max_quantity: 9,
},
{
id: "test-price2-quantity-customer-group",
region_id: "test-region",
currency_code: "usd",
amount: 100,
max_quantity: 9,
price_list_id: "pl_1",
},
{
id: "test-price3-quantity-customer-expired",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_expired",
},
{
id: "test-price3-quantity-customer-future",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_upcoming",
},
{
id: "test-price3-quantity-customer-now",
region_id: "test-region",
currency_code: "usd",
amount: 140,
min_quantity: 101,
max_quantity: 1000,
price_list_id: "pl_current",
},
{
id: "test-price3-quantity-customer-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
},
],
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option-quantity-customer",
},
],
})
await manager.save(variant_quantity_customer)
const p6 = await manager.create(Product, {
id: "test-product-sale-customer",
handle: "test-product-sale-customer",
title: "Test product sale",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p6)
await manager.save(ProductOption, {
id: "test-option-sale",
title: "test-option sale",
product_id: "test-product-sale-customer",
})
const variant_sale_customer = await manager.create(ProductVariant, {
id: "test-variant-sale-customer",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-sale-customer",
ean: "test-ean-sale-customer",
status: "published",
upc: "test-upc-sale-customer",
product_id: "test-product-sale-customer",
prices: [
{
id: "test-price-sale-customer",
region_id: "test-region",
currency_code: "usd",
amount: 120,
price_list_id: "pl_expired-customer",
},
{
id: "test-price1-sale-customer",
region_id: "test-region",
currency_code: "usd",
amount: 100,
price_list_id: "pl_current-customer",
},
{
id: "test-price2-sale-customer",
region_id: "test-region",
currency_code: "usd",
amount: 130,
price_list_id: "pl_upcoming-customer",
},
{
id: "test-price2-sale-customer-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
type: "default",
},
],
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option",
},
],
})
await manager.save(variant_sale_customer)
const p7 = await manager.create(Product, {
id: "test-product-sale-customer-quantity",
handle: "test-product-sale-customer-quantity",
title: "Test product sale",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
tags: [],
})
await manager.save(p7)
await manager.save(ProductOption, {
id: "test-option-sale",
title: "test-option sale",
product_id: "test-product-sale-customer-quantity",
})
const variant_sale_customer_quantity = await manager.create(ProductVariant, {
id: "test-variant-sale-customer-quantity",
inventory_quantity: 10,
title: "Test variant",
sku: "test-sku-sale-customer-quantity",
ean: "test-ean-sale-customer-quantity",
status: "published",
upc: "test-upc-sale-customer-quantity",
product_id: "test-product-sale-customer-quantity",
prices: [
{
id: "test-price-sale-customer-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 120,
min_quantity: 100,
max_quantity: 1000,
price_list_id: "pl_expired-customer",
},
{
id: "test-price1-sale-customer-quantity-groups",
region_id: "test-region",
currency_code: "usd",
amount: 100,
max_quantity: 99,
price_list_id: "pl_current-customer",
},
{
id: "test-price2-sale-customer-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 130,
min_quantity: 500,
max_quantity: 900,
price_list_id: "pl_upcoming-customer",
},
{
id: "test-price2-sale-customer-quantity-default",
region_id: "test-region",
currency_code: "usd",
amount: 150,
},
{
id: "test-price1-sale-customer-quantity",
region_id: "test-region",
currency_code: "usd",
amount: 110,
max_quantity: 99,
},
],
options: [
{
id: "test-variant-option",
value: "Default variant",
option_id: "test-option",
},
],
})
await manager.save(variant_sale_customer_quantity)
const cart = await manager.create(Cart, {
id: "test-cart",
region_id: "test-region",
currency_code: "usd",
items: [],
})
await manager.save(cart)
const cart_region2 = await manager.create(Cart, {
id: "test-cart-1",
region_id: "test-region-2",
currency_code: "dkk",
items: [],
})
await manager.save(cart_region2)
const cart_region1 = await manager.create(Cart, {
id: "test-cart-2",
region_id: "test-region-1",
currency_code: "usd",
items: [],
})
await manager.save(cart_region1)
}
+45 -14
View File
@@ -17,7 +17,7 @@ module.exports = async (connection, data = {}) => {
type: "default", type: "default",
}) })
const coll = manager.create(ProductCollection, { const coll = await manager.create(ProductCollection, {
id: "test-collection", id: "test-collection",
handle: "test-collection", handle: "test-collection",
title: "Test collection", title: "Test collection",
@@ -25,7 +25,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(coll) await manager.save(coll)
const coll1 = manager.create(ProductCollection, { const coll1 = await manager.create(ProductCollection, {
id: "test-collection1", id: "test-collection1",
handle: "test-collection1", handle: "test-collection1",
title: "Test collection 1", title: "Test collection 1",
@@ -33,7 +33,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(coll1) await manager.save(coll1)
const coll2 = manager.create(ProductCollection, { const coll2 = await manager.create(ProductCollection, {
id: "test-collection2", id: "test-collection2",
handle: "test-collection2", handle: "test-collection2",
title: "Test collection 2", title: "Test collection 2",
@@ -41,28 +41,28 @@ module.exports = async (connection, data = {}) => {
await manager.save(coll2) await manager.save(coll2)
const tag = manager.create(ProductTag, { const tag = await manager.create(ProductTag, {
id: "tag1", id: "tag1",
value: "123", value: "123",
}) })
await manager.save(tag) await manager.save(tag)
const tag3 = manager.create(ProductTag, { const tag3 = await manager.create(ProductTag, {
id: "tag3", id: "tag3",
value: "123", value: "123",
}) })
await manager.save(tag3) await manager.save(tag3)
const tag4 = manager.create(ProductTag, { const tag4 = await manager.create(ProductTag, {
id: "tag4", id: "tag4",
value: "123", value: "123",
}) })
await manager.save(tag4) await manager.save(tag4)
const type = manager.create(ProductType, { const type = await manager.create(ProductType, {
id: "test-type", id: "test-type",
value: "test-type", value: "test-type",
}) })
@@ -83,7 +83,7 @@ module.exports = async (connection, data = {}) => {
tax_rate: 0, tax_rate: 0,
}) })
const p = manager.create(Product, { const p = await manager.create(Product, {
id: "test-product", id: "test-product",
handle: "test-product", handle: "test-product",
title: "Test product", title: "Test product",
@@ -129,6 +129,34 @@ module.exports = async (connection, data = {}) => {
await manager.save(variant1) await manager.save(variant1)
const sale = await manager.create(ProductVariant, {
id: "test-variant-sale",
inventory_quantity: 10,
title: "Test variant",
variant_rank: 3,
sku: "test-sku-sale",
ean: "test-ean-sale",
upc: "test-upc-sale",
barcode: "test-barcode-sale",
product_id: "test-product",
prices: [
{
id: "test-price-sale",
currency_code: "usd",
amount: 1000,
},
],
options: [
{
id: "test-variant-option-sale",
value: "Default variant",
option_id: "test-option",
},
],
})
await manager.save(sale)
const variant2 = await manager.create(ProductVariant, { const variant2 = await manager.create(ProductVariant, {
id: "test-variant_1", id: "test-variant_1",
inventory_quantity: 10, inventory_quantity: 10,
@@ -172,7 +200,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(variant3) await manager.save(variant3)
const p1 = manager.create(Product, { const p1 = await manager.create(Product, {
id: "test-product1", id: "test-product1",
handle: "test-product1", handle: "test-product1",
title: "Test product1", title: "Test product1",
@@ -203,6 +231,7 @@ module.exports = async (connection, data = {}) => {
region_id: "test-region", region_id: "test-region",
currency_code: "usd", currency_code: "usd",
amount: 100, amount: 100,
type: "default",
}, },
], ],
options: [ options: [
@@ -225,7 +254,9 @@ module.exports = async (connection, data = {}) => {
ean: "test-ean4", ean: "test-ean4",
upc: "test-upc4", upc: "test-upc4",
product_id: "test-product1", product_id: "test-product1",
prices: [{ id: "test-price4", currency_code: "usd", amount: 100 }], prices: [
{ id: "test-price4", currency_code: "usd", amount: 100, type: "default" },
],
options: [ options: [
{ {
id: "test-variant-option-4", id: "test-variant-option-4",
@@ -237,7 +268,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(variant5) await manager.save(variant5)
const product1 = manager.create(Product, { const product1 = await manager.create(Product, {
id: "test-product_filtering_1", id: "test-product_filtering_1",
handle: "test-product_filtering_1", handle: "test-product_filtering_1",
title: "Test product filtering 1", title: "Test product filtering 1",
@@ -251,7 +282,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(product1) await manager.save(product1)
const product2 = manager.create(Product, { const product2 = await manager.create(Product, {
id: "test-product_filtering_2", id: "test-product_filtering_2",
handle: "test-product_filtering_2", handle: "test-product_filtering_2",
title: "Test product filtering 2", title: "Test product filtering 2",
@@ -265,7 +296,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(product2) await manager.save(product2)
const product3 = manager.create(Product, { const product3 = await manager.create(Product, {
id: "test-product_filtering_3", id: "test-product_filtering_3",
handle: "test-product_filtering_3", handle: "test-product_filtering_3",
title: "Test product filtering 3", title: "Test product filtering 3",
@@ -279,7 +310,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(product3) await manager.save(product3)
const product4 = manager.create(Product, { const product4 = await manager.create(Product, {
id: "test-product_filtering_4", id: "test-product_filtering_4",
handle: "test-product_filtering_4", handle: "test-product_filtering_4",
title: "Test product filtering 4", title: "Test product filtering 4",
@@ -8,11 +8,45 @@ const {
ShippingProfile, ShippingProfile,
ProductVariant, ProductVariant,
Image, Image,
Cart,
PriceList,
} = require("@medusajs/medusa") } = require("@medusajs/medusa")
module.exports = async (connection, data = {}) => { module.exports = async (connection, data = {}) => {
const manager = connection.manager const manager = connection.manager
const yesterday = ((today) => new Date(today.setDate(today.getDate() - 1)))(
new Date()
)
const tomorrow = ((today) => new Date(today.setDate(today.getDate() + 1)))(
new Date()
)
const tenDaysAgo = ((today) => new Date(today.setDate(today.getDate() - 10)))(
new Date()
)
const priceList = await manager.create(PriceList, {
id: "pl",
name: "VIP winter sale",
description: "Winter sale for VIP customers.",
type: "sale",
status: "active",
})
await manager.save(priceList)
const priceList1 = await manager.create(PriceList, {
id: "pl_expired",
name: "Past winter sale",
description: "Winter sale for key accounts.",
type: "sale",
status: "active",
starts_at: tenDaysAgo,
ends_at: yesterday,
})
await manager.save(priceList1)
const defaultProfile = await manager.findOne(ShippingProfile, { const defaultProfile = await manager.findOne(ShippingProfile, {
type: "default", type: "default",
}) })
@@ -83,12 +117,20 @@ module.exports = async (connection, data = {}) => {
tax_rate: 0, tax_rate: 0,
}) })
await manager.insert(Cart, {
id: "test-cart",
region_id: "test-region",
currency_code: "usd",
items: [],
})
const p = manager.create(Product, { const p = manager.create(Product, {
id: "test-product", id: "test-product",
handle: "test-product", handle: "test-product",
title: "Test product", title: "Test product",
profile_id: defaultProfile.id, profile_id: defaultProfile.id,
description: "test-product-description", description: "test-product-description",
status: "published",
collection_id: "test-collection", collection_id: "test-collection",
type: { id: "test-type", value: "test-type" }, type: { id: "test-type", value: "test-type" },
tags: [ tags: [
@@ -117,7 +159,21 @@ module.exports = async (connection, data = {}) => {
upc: "test-upc", upc: "test-upc",
barcode: "test-barcode", barcode: "test-barcode",
product_id: "test-product", product_id: "test-product",
prices: [{ id: "test-price", currency_code: "usd", amount: 100 }], prices: [
{ id: "test-price", currency_code: "usd", type: "default", amount: 100 },
{
id: "test-price-discount",
currency_code: "usd",
amount: 80,
price_list_id: "pl",
},
{
id: "test-price-discount-expired",
currency_code: "usd",
amount: 70,
price_list_id: "pl_expired",
},
],
options: [ options: [
{ {
id: "test-variant-option", id: "test-variant-option",
@@ -139,7 +195,21 @@ module.exports = async (connection, data = {}) => {
upc: "test-upc1", upc: "test-upc1",
barcode: "test-barcode 1", barcode: "test-barcode 1",
product_id: "test-product", product_id: "test-product",
prices: [{ id: "test-price1", currency_code: "usd", amount: 100 }], prices: [
{ id: "test-price1", currency_code: "usd", type: "default", amount: 100 },
{
id: "test-price1-discount",
currency_code: "usd",
amount: 80,
price_list_id: "pl",
},
{
id: "test-price1-discount-expired",
currency_code: "usd",
amount: 70,
price_list_id: "pl_expired",
},
],
options: [ options: [
{ {
id: "test-variant-option-1", id: "test-variant-option-1",
@@ -160,7 +230,21 @@ module.exports = async (connection, data = {}) => {
ean: "test-ean2", ean: "test-ean2",
upc: "test-upc2", upc: "test-upc2",
product_id: "test-product", product_id: "test-product",
prices: [{ id: "test-price2", currency_code: "usd", amount: 100 }], prices: [
{ id: "test-price2", currency_code: "usd", type: "default", amount: 100 },
{
id: "test-price2-discount",
currency_code: "usd",
amount: 80,
price_list_id: "pl",
},
{
id: "test-price2-discount-expired",
currency_code: "usd",
amount: 70,
price_list_id: "pl_expired",
},
],
options: [ options: [
{ {
id: "test-variant-option-2", id: "test-variant-option-2",
@@ -176,6 +260,7 @@ module.exports = async (connection, data = {}) => {
id: "test-product1", id: "test-product1",
handle: "test-product1", handle: "test-product1",
title: "Test product1", title: "Test product1",
status: "published",
profile_id: defaultProfile.id, profile_id: defaultProfile.id,
description: "test-product-description1", description: "test-product-description1",
collection_id: "test-collection", collection_id: "test-collection",
+10 -1
View File
@@ -349,7 +349,7 @@ const createSwap = async (options, manager) => {
is_disabled: false, is_disabled: false,
rule: dRule, rule: dRule,
}) })
await manager.save(discount) let discountDb = await manager.save(discount)
const cart = manager.create(Cart, { const cart = manager.create(Cart, {
id: `${swapId}-cart`, id: `${swapId}-cart`,
@@ -396,6 +396,14 @@ const createSwap = async (options, manager) => {
thumbnail: "https://test.js/1234", thumbnail: "https://test.js/1234",
unit_price: 8000, unit_price: 8000,
quantity: 1, quantity: 1,
adjustments: [
{
amount: -800,
description: "discount",
discount_id: discountDb.id,
item_id: `${swapId}-return-item-1`,
},
],
variant_id: "test-variant", variant_id: "test-variant",
order_id: "order-with-swap", order_id: "order-with-swap",
cart_id: cart.id, cart_id: cart.id,
@@ -419,6 +427,7 @@ const createSwap = async (options, manager) => {
const return_item1 = manager.create(LineItem, { const return_item1 = manager.create(LineItem, {
...li, ...li,
is_return: true,
unit_price: -1 * li.unit_price, unit_price: -1 * li.unit_price,
}) })
+1
View File
@@ -1,4 +1,5 @@
// API // API
process.chdir(__dirname)
module.exports = { module.exports = {
name: "api", name: "api",
+2
View File
@@ -8,5 +8,7 @@ module.exports = {
// redis_url: REDIS_URL, // redis_url: REDIS_URL,
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`, database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`,
database_type: "postgres", database_type: "postgres",
jwt_secret: 'test',
cookie_secret: 'test'
}, },
} }
+3 -3
View File
@@ -8,16 +8,16 @@
"build": "babel src -d dist --extensions \".ts,.js\"" "build": "babel src -d dist --extensions \".ts,.js\""
}, },
"dependencies": { "dependencies": {
"@medusajs/medusa": "1.1.64-dev-1645628651544", "@medusajs/medusa": "1.2.1-dev-1649181615374",
"faker": "^5.5.3", "faker": "^5.5.3",
"medusa-interfaces": "1.1.34-dev-1645628651544", "medusa-interfaces": "1.2.1-dev-1649181615374",
"typeorm": "^0.2.31" "typeorm": "^0.2.31"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.10", "@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10", "@babel/core": "^7.12.10",
"@babel/node": "^7.12.10", "@babel/node": "^7.12.10",
"babel-preset-medusa-package": "1.1.19-dev-1645441522984", "babel-preset-medusa-package": "1.1.19-dev-1649181615374",
"jest": "^26.6.3" "jest": "^26.6.3"
} }
} }
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -1,5 +1,6 @@
const dbFactory = require("./helpers/use-template-db") const dbFactory = require("./helpers/use-template-db")
const path = require("path")
module.exports = async () => { module.exports = async () => {
await dbFactory.createTemplateDb_() await dbFactory.createTemplateDb_({ cwd: path.resolve(".") })
} }
+2 -3
View File
@@ -20,14 +20,13 @@ class DatabaseFactory {
this.templateDbName = "medusa-integration-template" this.templateDbName = "medusa-integration-template"
} }
async createTemplateDb_() { async createTemplateDb_({ cwd }) {
try { try {
const cwd = path.resolve(path.join(__dirname, "..")) // const cwd = path.resolve(path.join(__dirname, ".."))
const connection = await this.getMasterConnection() const connection = await this.getMasterConnection()
const migrationDir = path.resolve( const migrationDir = path.resolve(
path.join( path.join(
cwd, cwd,
"api",
`node_modules`, `node_modules`,
`@medusajs`, `@medusajs`,
`medusa`, `medusa`,
+1
View File
@@ -6,6 +6,7 @@ const pkgs = glob
module.exports = { module.exports = {
testEnvironment: `node`, testEnvironment: `node`,
testTimeout: 10000,
globalSetup: "<rootDir>/integration-tests/globalSetup.js", globalSetup: "<rootDir>/integration-tests/globalSetup.js",
globalTeardown: "<rootDir>/integration-tests/globalTeardown.js", globalTeardown: "<rootDir>/integration-tests/globalTeardown.js",
rootDir: `../`, rootDir: `../`,
@@ -427,6 +427,7 @@ Object {
"idempotency_key": null, "idempotency_key": null,
"items": Array [ "items": Array [
Object { Object {
"adjustments": Array [],
"allow_discounts": true, "allow_discounts": true,
"cart_id": null, "cart_id": null,
"claim_order_id": null, "claim_order_id": null,
@@ -761,6 +762,7 @@ Object {
"idempotency_key": null, "idempotency_key": null,
"items": Array [ "items": Array [
Object { Object {
"adjustments": Array [],
"allow_discounts": true, "allow_discounts": true,
"cart_id": null, "cart_id": null,
"claim_order_id": null, "claim_order_id": null,
@@ -982,6 +984,7 @@ Object {
"idempotency_key": null, "idempotency_key": null,
"items": Array [ "items": Array [
Object { Object {
"adjustments": Array [],
"allow_discounts": true, "allow_discounts": true,
"cart_id": null, "cart_id": null,
"claim_order_id": null, "claim_order_id": null,
@@ -1248,6 +1251,7 @@ Object {
"idempotency_key": null, "idempotency_key": null,
"items": Array [ "items": Array [
Object { Object {
"adjustments": Array [],
"allow_discounts": true, "allow_discounts": true,
"cart_id": null, "cart_id": null,
"claim_order_id": null, "claim_order_id": null,
@@ -1569,6 +1573,7 @@ Object {
"idempotency_key": null, "idempotency_key": null,
"items": Array [ "items": Array [
Object { Object {
"adjustments": Array [],
"allow_discounts": true, "allow_discounts": true,
"cart_id": null, "cart_id": null,
"claim_order_id": null, "claim_order_id": null,
@@ -107,6 +107,7 @@ describe("medusa-plugin-sendgrid", () => {
}, },
items: [ items: [
{ {
adjustments: [],
created_at: expect.any(Date), created_at: expect.any(Date),
updated_at: expect.any(Date), updated_at: expect.any(Date),
order_id: expect.any(String), order_id: expect.any(String),
@@ -227,6 +228,7 @@ describe("medusa-plugin-sendgrid", () => {
}, },
items: [ items: [
{ {
adjustments: [],
created_at: expect.any(Date), created_at: expect.any(Date),
updated_at: expect.any(Date), updated_at: expect.any(Date),
order_id: expect.any(String), order_id: expect.any(String),
@@ -285,6 +287,7 @@ describe("medusa-plugin-sendgrid", () => {
}, },
items: [ items: [
{ {
adjustments: [],
created_at: expect.any(Date), created_at: expect.any(Date),
updated_at: expect.any(Date), updated_at: expect.any(Date),
order_id: expect.any(String), order_id: expect.any(String),
@@ -24,5 +24,7 @@ module.exports = {
// redis_url: REDIS_URL, // redis_url: REDIS_URL,
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`, database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`,
database_type: "postgres", database_type: "postgres",
jwt_secret: 'test',
cookie_secret: 'test'
}, },
} }
+5 -5
View File
@@ -8,18 +8,18 @@
"build": "babel src -d dist --extensions \".ts,.js\"" "build": "babel src -d dist --extensions \".ts,.js\""
}, },
"dependencies": { "dependencies": {
"@medusajs/medusa": "1.1.60-dev-1642337635041", "@medusajs/medusa": "1.2.1-dev-1649009241281",
"faker": "^5.5.3", "faker": "^5.5.3",
"medusa-fulfillment-webshipper": "1.1.35-dev-1642337635041", "medusa-fulfillment-webshipper": "1.2.1-dev-1649009241281",
"medusa-interfaces": "1.1.32-dev-1642337635041", "medusa-interfaces": "1.2.1-dev-1649009241281",
"medusa-plugin-sendgrid": "1.1.36-dev-1642337635041", "medusa-plugin-sendgrid": "1.2.1-dev-1649009241281",
"typeorm": "^0.2.31" "typeorm": "^0.2.31"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.10", "@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10", "@babel/core": "^7.12.10",
"@babel/node": "^7.12.10", "@babel/node": "^7.12.10",
"babel-preset-medusa-package": "1.1.19-dev-1642337635041", "babel-preset-medusa-package": "1.1.19-dev-1649009241281",
"jest": "^26.6.3" "jest": "^26.6.3"
} }
} }
+46 -46
View File
@@ -1268,10 +1268,10 @@
"@types/yargs" "^15.0.0" "@types/yargs" "^15.0.0"
chalk "^4.0.0" chalk "^4.0.0"
"@medusajs/medusa-cli@1.1.25-dev-1642337635041": "@medusajs/medusa-cli@1.2.1-dev-1649009241281":
version "1.1.25-dev-1642337635041" version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.1.25-dev-1642337635041.tgz#e3ca33f453446dddc4922f42b4254995de9874b5" resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.2.1-dev-1649009241281.tgz#e0cc327224af6067013e0eba1b882b3f9f0fd0e3"
integrity sha512-8pA5iBMglgrdPRSEZLvjovJczib5zk9qlQJsewlSrw/aolwnusukEE7vBfjAiUXAZKngMx6BnYoORlWSV88wdA== integrity sha512-3949nJfrBIguXrKDwiKjQ10XxZ06ccTShbcOUsXkIuGIMOHBZ+AuhjErCAnXS3H/JSIO93lnQKVJ8DWSkf9BGQ==
dependencies: dependencies:
"@babel/polyfill" "^7.8.7" "@babel/polyfill" "^7.8.7"
"@babel/runtime" "^7.9.6" "@babel/runtime" "^7.9.6"
@@ -1289,8 +1289,8 @@
is-valid-path "^0.1.1" is-valid-path "^0.1.1"
joi-objectid "^3.0.1" joi-objectid "^3.0.1"
meant "^1.0.1" meant "^1.0.1"
medusa-core-utils "1.1.31-dev-1642337635041" medusa-core-utils "1.1.31-dev-1649009241281"
medusa-telemetry "0.0.11-dev-1642337635041" medusa-telemetry "0.0.11-dev-1649009241281"
netrc-parser "^3.1.6" netrc-parser "^3.1.6"
open "^8.0.6" open "^8.0.6"
ora "^5.4.1" ora "^5.4.1"
@@ -1304,13 +1304,13 @@
winston "^3.3.3" winston "^3.3.3"
yargs "^15.3.1" yargs "^15.3.1"
"@medusajs/medusa@1.1.60-dev-1642337635041": "@medusajs/medusa@1.2.1-dev-1649009241281":
version "1.1.60-dev-1642337635041" version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.60-dev-1642337635041.tgz#011d64b9b85a3f66e01f3755788370aa444a2e27" resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.2.1-dev-1649009241281.tgz#ed063a9abee468cd71b5d0201ea4f32dd2a59775"
integrity sha512-MBef46mJZMSyE3+dcijMqjFZbNo7AL60mP/PwkOy4W1d/t2MZ+GOBIufLAVxVw7SaiSjAGpQDmCrUOqQrIhIdg== integrity sha512-uNVPZq4rVOLRRP9yTeYWPn9xsLfhh7mgRexMjRL/7QmKTQmspBL/pbjdRk0WCBSSf3o5SFiOV0/n69Oxg6Oaig==
dependencies: dependencies:
"@hapi/joi" "^16.1.8" "@hapi/joi" "^16.1.8"
"@medusajs/medusa-cli" "1.1.25-dev-1642337635041" "@medusajs/medusa-cli" "1.2.1-dev-1649009241281"
"@types/lodash" "^4.14.168" "@types/lodash" "^4.14.168"
awilix "^4.2.3" awilix "^4.2.3"
body-parser "^1.19.0" body-parser "^1.19.0"
@@ -1334,8 +1334,8 @@
joi "^17.3.0" joi "^17.3.0"
joi-objectid "^3.0.1" joi-objectid "^3.0.1"
jsonwebtoken "^8.5.1" jsonwebtoken "^8.5.1"
medusa-core-utils "1.1.31-dev-1642337635041" medusa-core-utils "1.1.31-dev-1649009241281"
medusa-test-utils "1.1.35-dev-1642337635041" medusa-test-utils "1.1.37-dev-1649009241281"
morgan "^1.9.1" morgan "^1.9.1"
multer "^1.4.2" multer "^1.4.2"
passport "^0.4.0" passport "^0.4.0"
@@ -1995,10 +1995,10 @@ babel-preset-jest@^26.6.2:
babel-plugin-jest-hoist "^26.6.2" babel-plugin-jest-hoist "^26.6.2"
babel-preset-current-node-syntax "^1.0.0" babel-preset-current-node-syntax "^1.0.0"
babel-preset-medusa-package@1.1.19-dev-1642337635041: babel-preset-medusa-package@1.1.19-dev-1649009241281:
version "1.1.19-dev-1642337635041" version "1.1.19-dev-1649009241281"
resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1642337635041.tgz#24c6f2db887f35c358270d85cc0f6b6c19f459fe" resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1649009241281.tgz#253cb95840d3e122a45e2f72e15f623afa57ae61"
integrity sha512-HtrHI6oWUEMyTK9MHKzCg7hzZHgi8+uCIQyTQIY5SYFRA6kZiNl54Wzai/BY26F1j5g0sVNfqmFXdBd53GEz7g== integrity sha512-UFH2A+rYUc33d2f+xEVCQOOmcm0RSSPlnTeSVV5bM8SoS4MXgekPdBO+sMjKi/bLbHJncKaapCm/HoYZ19ZJHg==
dependencies: dependencies:
"@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-class-properties" "^7.12.1"
"@babel/plugin-proposal-decorators" "^7.12.1" "@babel/plugin-proposal-decorators" "^7.12.1"
@@ -5138,48 +5138,48 @@ media-typer@0.3.0:
resolved "http://localhost:4873/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "http://localhost:4873/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
medusa-core-utils@1.1.31-dev-1642337635041: medusa-core-utils@1.1.31-dev-1649009241281:
version "1.1.31-dev-1642337635041" version "1.1.31-dev-1649009241281"
resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1642337635041.tgz#d6928f377320ab851eff1750e13e44e2e366c9cb" resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1649009241281.tgz#1ff37cdd9057c5716124f250c0b4b261ef8c7cde"
integrity sha512-m0VXTM3QwKi0oRk2Q4mqn6sK07xJgiwyz3rs/0gHTjl88B8k75XugbriFceLPEYqHs6AjP6nSNiRDqdRJqUsMg== integrity sha512-nYcgpTtUSGAFG00m1rKcWUgnlmL2zu5aOYl5Fas/re+hhc1fhi+jwHUMigK0c28t71RwgtaeJFNKgQNgxli+2A==
dependencies: dependencies:
joi "^17.3.0" joi "^17.3.0"
joi-objectid "^3.0.1" joi-objectid "^3.0.1"
medusa-fulfillment-webshipper@1.1.35-dev-1642337635041: medusa-fulfillment-webshipper@1.2.1-dev-1649009241281:
version "1.1.35-dev-1642337635041" version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/medusa-fulfillment-webshipper/-/medusa-fulfillment-webshipper-1.1.35-dev-1642337635041.tgz#ab4b0a23488094b39582d1c1d1c69e29713b464d" resolved "http://localhost:4873/medusa-fulfillment-webshipper/-/medusa-fulfillment-webshipper-1.2.1-dev-1649009241281.tgz#d199209df0d3683a53829565713b857bac284282"
integrity sha512-P1gRvnJcs5Svgmvb44Qp1Oph9TqtUQsEeawhX7u+3dSz9qDnJ1lo1XB1tBttpCP9qepPe3nc0Sk7mEwlq8eM6Q== integrity sha512-dtMBJ3mObQS2uGKClga66qDOGTKjOenzLOdjScurCwheQqi5a+EOyUrZSssE4oQEMMRsuY+L/MTkcLyKr4f+HA==
dependencies: dependencies:
axios "^0.20.0" axios "^0.20.0"
body-parser "^1.19.0" body-parser "^1.19.0"
cors "^2.8.5" cors "^2.8.5"
express "^4.17.1" express "^4.17.1"
medusa-core-utils "1.1.31-dev-1642337635041" medusa-core-utils "1.1.31-dev-1649009241281"
medusa-interfaces@1.1.32-dev-1642337635041: medusa-interfaces@1.2.1-dev-1649009241281:
version "1.1.32-dev-1642337635041" version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.1.32-dev-1642337635041.tgz#8179a344a41e1163502b4ee8af5b294443067f99" resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.2.1-dev-1649009241281.tgz#77b6c906b805e6b1fb092fb7632a7ad8ecbafec5"
integrity sha512-I7Wp+BEgzJ2wLkGBoDe6VhAlB8lJRMAHJ7wGzWfCMiG3Dj0PX2KE2HZDT+PA5S7IIXHWh9c0n37/cHZDcIF5Hw== integrity sha512-tgDptu8k7aWCKBo1m8RxcxcffsVjy2e2M80rfvTd81gjC3EtGBGxDmiwNGJoaWkFq+Matq2wve2l92xVA2t8uQ==
dependencies: dependencies:
medusa-core-utils "1.1.31-dev-1642337635041" medusa-core-utils "1.1.31-dev-1649009241281"
medusa-plugin-sendgrid@1.1.36-dev-1642337635041: medusa-plugin-sendgrid@1.2.1-dev-1649009241281:
version "1.1.36-dev-1642337635041" version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/medusa-plugin-sendgrid/-/medusa-plugin-sendgrid-1.1.36-dev-1642337635041.tgz#ae4d1d23167daf3c45ced085f3079ed21d5e876d" resolved "http://localhost:4873/medusa-plugin-sendgrid/-/medusa-plugin-sendgrid-1.2.1-dev-1649009241281.tgz#362fafa54658c641fb482346c0ba6f28007bbbf3"
integrity sha512-nJBq0x9h9fMsPfW4BnGzvk8yWpuk1v90zeXP85cDhFeAAWJ50o2zF7HsNcJawPfhKooAF+3kRDApR8706gUyqQ== integrity sha512-2C7mfwR+HZSjm/Y3TGHHr7dJAoNhwUvEVZs2dpJCEfmpbsR2f6TG3J5e+kidubQ4NeCmHdrXGPmjS4lbH1tnhQ==
dependencies: dependencies:
"@babel/plugin-transform-classes" "^7.9.5" "@babel/plugin-transform-classes" "^7.9.5"
"@sendgrid/mail" "^7.1.1" "@sendgrid/mail" "^7.1.1"
body-parser "^1.19.0" body-parser "^1.19.0"
express "^4.17.1" express "^4.17.1"
medusa-core-utils "1.1.31-dev-1642337635041" medusa-core-utils "1.1.31-dev-1649009241281"
medusa-test-utils "1.1.35-dev-1642337635041" medusa-test-utils "1.1.37-dev-1649009241281"
medusa-telemetry@0.0.11-dev-1642337635041: medusa-telemetry@0.0.11-dev-1649009241281:
version "0.0.11-dev-1642337635041" version "0.0.11-dev-1649009241281"
resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1642337635041.tgz#290bb20c1712adf99ec5d2f6871b200106203bc0" resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1649009241281.tgz#76077de2a5b53f754f1a55701d892716be67876d"
integrity sha512-LlJVgdKAQFrp6ULW8D+Qvb/VSEgbQbKjEeoMnUwDaMMswGehsFUXsqBzs1LRo5nKmpLaPvSorw4OMvktR+RxTQ== integrity sha512-ZEWmHZI9RByg1Tm8sOOxVCs+dkr2LCvElXqFsJfgIoTdjvGS96tfG1pbod2LHD5rcBY2aXDSXBRf0pA1xRtvsQ==
dependencies: dependencies:
axios "^0.21.1" axios "^0.21.1"
axios-retry "^3.1.9" axios-retry "^3.1.9"
@@ -5191,13 +5191,13 @@ medusa-telemetry@0.0.11-dev-1642337635041:
remove-trailing-slash "^0.1.1" remove-trailing-slash "^0.1.1"
uuid "^8.3.2" uuid "^8.3.2"
medusa-test-utils@1.1.35-dev-1642337635041: medusa-test-utils@1.1.37-dev-1649009241281:
version "1.1.35-dev-1642337635041" version "1.1.37-dev-1649009241281"
resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.35-dev-1642337635041.tgz#1daf7f6a671e2fffe43ab349d795581870104a4a" resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.37-dev-1649009241281.tgz#59c9ac8fddf31a410347dac5ef147758a61f13da"
integrity sha512-pCpMjmo0VyYbSRu/Ga/GdirBscJtS4HK4HtfXepMrut7Yg/kV+GpW/fFtBFkiqGDBpCWYLopzZAdRw+qpdpusA== integrity sha512-+3c+2DB3UpJxyIt6LvWCR9aBtXwrABXV4GhUYW6toGl19JE2lZ9PBL3GSVmGi7etZ7Xv7tR9bwZXZURvKMr8Hg==
dependencies: dependencies:
"@babel/plugin-transform-classes" "^7.9.5" "@babel/plugin-transform-classes" "^7.9.5"
medusa-core-utils "1.1.31-dev-1642337635041" medusa-core-utils "1.1.31-dev-1649009241281"
randomatic "^3.1.1" randomatic "^3.1.1"
merge-descriptors@1.0.1: merge-descriptors@1.0.1:
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.1.0](https://github.com/medusajs/medusa/compare/gatsby-source-medusa@0.0.49...gatsby-source-medusa@0.1.0) (2022-05-01)
### Bug Fixes
* **gatsby-source-medusa:** adds support for incremental sourcing of medusa data ([#1217](https://github.com/medusajs/medusa/issues/1217)) ([23f8399](https://github.com/medusajs/medusa/commit/23f8399c16e3f3c7a01a6846b2b96750dde55a60))
## [0.0.49](https://github.com/medusajs/medusa/compare/gatsby-source-medusa@0.0.48...gatsby-source-medusa@0.0.49) (2022-02-06) ## [0.0.49](https://github.com/medusajs/medusa/compare/gatsby-source-medusa@0.0.48...gatsby-source-medusa@0.0.49) (2022-02-06)
### Bug Fixes ### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "gatsby-source-medusa", "name": "gatsby-source-medusa",
"version": "0.0.49", "version": "0.1.0",
"description": "Gatsby source plugin for building websites using Medusa Commerce as a data source", "description": "Gatsby source plugin for building websites using Medusa Commerce as a data source",
"scripts": { "scripts": {
"test": "jest --watchAll", "test": "jest --watchAll",
+33 -45
View File
@@ -1,5 +1,4 @@
import axios, { AxiosPromise, AxiosRequestConfig } from "axios" import axios, { AxiosPromise, AxiosRequestConfig } from "axios"
import { Reporter } from "gatsby-cli/lib/reporter/reporter"
function medusaRequest( function medusaRequest(
storeURL: string, storeURL: string,
@@ -18,34 +17,28 @@ function medusaRequest(
return client(options) return client(options)
} }
export const createClient = ( export const createClient = (options: MedusaPluginOptions): any => {
options: MedusaPluginOptions,
reporter: Reporter
): any => {
const { storeUrl, authToken } = options as any const { storeUrl, authToken } = options as any
/** /**
*
* @param {string} _date used fetch products updated since the specified date * @param {string} _date used fetch products updated since the specified date
* @return {Promise<any[]>} * @return {Promise<any[]>} products to create nodes from
*/ */
async function products(_date?: string): Promise<any[]> { async function products(_date?: string): Promise<any[]> {
let products: any[] = [] let products: any[] = []
let offset = 0 let offset = 0
let count = 1 let count = 1
do { do {
await medusaRequest(storeUrl, `/store/products?offset=${offset}`) let path = `/store/products?offset=${offset}`
.then(({ data }) => { if (_date) {
products = [...products, ...data.products] path += `&updated_at[gt]=${_date}`
count = data.count }
offset = data.products.length
}) await medusaRequest(storeUrl, path).then(({ data }) => {
.catch((error) => { products = [...products, ...data.products]
reporter.error( count = data.count
`"The following error status was produced while attempting to fetch products: ${error}` offset = data.products.length
) })
return []
})
} while (products.length < count) } while (products.length < count)
return products return products
@@ -53,27 +46,25 @@ export const createClient = (
/** /**
* *
* @param {string} date used fetch regions updated since the specified date * @param {string} _date used fetch regions updated since the specified date
* @return {Promise<any[]>} * @return {Promise<any[]>} regions to create nodes from
*/ */
async function regions(_date?: string): Promise<any[]> { async function regions(_date?: string): Promise<any[]> {
const regions = await medusaRequest(storeUrl, `/store/regions`) let path = `/store/regions`
.then(({ data }) => { if (_date) {
return data.regions path += `?updated_at[gt]=${_date}`
}) }
.catch((error) => {
console.warn(` const regions = await medusaRequest(storeUrl, path).then(({ data }) => {
"The following error status was produced while attempting to fetch regions: ${error} return data.regions
`) })
return []
})
return regions return regions
} }
/** /**
* *
* @param {string} _date used fetch regions updated since the specified date * @param {string} _date used fetch regions updated since the specified date
* @return {Promise<any[]>} * @return {Promise<any[]>} orders to create nodes from
*/ */
async function orders(_date?: string): Promise<any[]> { async function orders(_date?: string): Promise<any[]> {
const orders = await medusaRequest(storeUrl, `/admin/orders`, { const orders = await medusaRequest(storeUrl, `/admin/orders`, {
@@ -95,25 +86,22 @@ export const createClient = (
/** /**
* *
* @param {string} _date used fetch regions updated since the specified date * @param {string} _date used fetch regions updated since the specified date
* @return {Promise<any[]>} * @return {Promise<any[]>} collections to create nodes from
*/ */
async function collections(_date?: string): Promise<any[]> { async function collections(_date?: string): Promise<any[]> {
let collections: any[] = [] let collections: any[] = []
let offset = 0 let offset = 0
let count = 1 let count = 1
do { do {
await medusaRequest(storeUrl, `/store/collections?offset=${offset}`) let path = `/store/collections?offset=${offset}`
.then(({ data }) => { if (_date) {
collections = [...collections, ...data.collections] path += `&updated_at[gt]=${_date}`
count = data.count }
offset = data.collections.length await medusaRequest(storeUrl, path).then(({ data }) => {
}) collections = [...collections, ...data.collections]
.catch((error) => { count = data.count
reporter.error( offset = data.collections.length
`"The following error status was produced while attempting to fetch products: ${error}` })
)
return []
})
} while (collections.length < count) } while (collections.length < count)
return collections return collections
@@ -27,7 +27,7 @@ async function sourceAllNodes(
createRegionsOperation, createRegionsOperation,
createOrdersOperation, createOrdersOperation,
createCollectionsOperation, createCollectionsOperation,
} = createOperations(pluginOptions, gatsbyApi) } = createOperations(pluginOptions)
const operations = [ const operations = [
createProductsOperation, createProductsOperation,
@@ -54,22 +54,17 @@ const medusaNodeTypes = [
"MedusaCollections", "MedusaCollections",
] ]
export async function sourceUpdatedNodes( async function sourceUpdatedNodes(
gatsbyApi: SourceNodesArgs, gatsbyApi: SourceNodesArgs,
pluginOptions: MedusaPluginOptions pluginOptions: MedusaPluginOptions,
lastBuildTime: string
): Promise<void> { ): Promise<void> {
const { const {
incrementalProductsOperation, incrementalProductsOperation,
incrementalRegionsOperation, incrementalRegionsOperation,
incrementalOrdersOperation, incrementalOrdersOperation,
incrementalCollectionsOperation, incrementalCollectionsOperation,
} = createOperations(pluginOptions, gatsbyApi) } = createOperations(pluginOptions)
const lastBuildTime = new Date(
gatsbyApi.store.getState().status.plugins?.[`gatsby-source-medusa`]?.[
`lastBuildTime`
]
)
for (const nodeType of medusaNodeTypes) { for (const nodeType of medusaNodeTypes) {
gatsbyApi gatsbyApi
@@ -94,36 +89,25 @@ export async function sourceUpdatedNodes(
} }
} }
export async function onPostBuild({ cache }: { cache: any }): Promise<void> {
await cache.set("timestamp", Date.now())
}
export async function sourceNodes( export async function sourceNodes(
gatsbyApi: SourceNodesArgs, gatsbyApi: SourceNodesArgs,
pluginOptions: MedusaPluginOptions pluginOptions: MedusaPluginOptions
): Promise<void> { ): Promise<void> {
const pluginStatus = const { cache } = gatsbyApi
gatsbyApi.store.getState().status.plugins?.[`gatsby-source-medusa`] const lastBuildTime = await cache.get("timestamp")
const lastBuildTime = pluginStatus?.[`lastBuildTime`]
if (lastBuildTime !== undefined) { if (lastBuildTime !== undefined) {
gatsbyApi.reporter.info( await sourceUpdatedNodes(gatsbyApi, pluginOptions, lastBuildTime)
`Cache is warm, but incremental builds are currently not supported. Running a clean build.`
)
await sourceAllNodes(gatsbyApi, pluginOptions)
} else { } else {
gatsbyApi.reporter.info(`Cache is cold, running a clean build.`) gatsbyApi.reporter.info(`Cache is cold, running a clean build.`)
await sourceAllNodes(gatsbyApi, pluginOptions) await sourceAllNodes(gatsbyApi, pluginOptions)
} }
gatsbyApi.reporter.info(`Finished sourcing nodes, caching last build time`) gatsbyApi.reporter.info(`Finished sourcing nodes`)
gatsbyApi.actions.setPluginStatus(
pluginStatus !== undefined
? {
...pluginStatus,
[`lastBuildTime`]: Date.now(),
}
: {
[`lastBuildTime`]: Date.now(),
}
)
} }
export function createResolvers({ createResolvers }: CreateResolversArgs): any { export function createResolvers({ createResolvers }: CreateResolversArgs): any {
+10 -14
View File
@@ -1,11 +1,7 @@
import { SourceNodesArgs } from "gatsby"
import { createClient } from "./client" import { createClient } from "./client"
export function createOperations( export function createOperations(options: MedusaPluginOptions): IOperations {
options: MedusaPluginOptions, const client = createClient(options)
{ reporter }: SourceNodesArgs
): IOperations {
const client = createClient(options, reporter)
function createOperation( function createOperation(
name: "products" | "collections" | "regions" | "orders", name: "products" | "collections" | "regions" | "orders",
@@ -22,13 +18,13 @@ export function createOperations(
createCollectionsOperation: createOperation("collections"), createCollectionsOperation: createOperation("collections"),
createRegionsOperation: createOperation("regions"), createRegionsOperation: createOperation("regions"),
createOrdersOperation: createOperation("orders"), createOrdersOperation: createOperation("orders"),
incrementalProductsOperation: (date: Date): any => incrementalProductsOperation: (date: string): any =>
createOperation("products", date.toISOString()), createOperation("products", date),
incrementalCollectionsOperation: (date: Date): any => incrementalCollectionsOperation: (date: string): any =>
createOperation("collections", date.toISOString()), createOperation("collections", date),
incrementalRegionsOperation: (date: Date): any => incrementalRegionsOperation: (date: string): any =>
createOperation("regions", date.toISOString()), createOperation("regions", date),
incrementalOrdersOperation: (date: Date): any => incrementalOrdersOperation: (date: string): any =>
createOperation("orders", date.toISOString()), createOperation("orders", date),
} }
} }
+4 -4
View File
@@ -22,8 +22,8 @@ interface IOperations {
createCollectionsOperation: IMedusaOperation createCollectionsOperation: IMedusaOperation
createRegionsOperation: IMedusaOperation createRegionsOperation: IMedusaOperation
createOrdersOperation: IMedusaOperation createOrdersOperation: IMedusaOperation
incrementalProductsOperation: (date: Date) => IMedusaOperation incrementalProductsOperation: (date: string) => IMedusaOperation
incrementalCollectionsOperation: (date: Date) => IMedusaOperation incrementalCollectionsOperation: (date: string) => IMedusaOperation
incrementalRegionsOperation: (date: Date) => IMedusaOperation incrementalRegionsOperation: (date: string) => IMedusaOperation
incrementalOrdersOperation: (date: Date) => IMedusaOperation incrementalOrdersOperation: (date: string) => IMedusaOperation
} }
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.3.0](https://github.com/medusajs/medusa/compare/@medusajs/medusa-cli@1.2.1...@medusajs/medusa-cli@1.3.0) (2022-05-01)
**Note:** Version bump only for package @medusajs/medusa-cli
## [1.2.1](https://github.com/medusajs/medusa/compare/@medusajs/medusa-cli@1.1.27...@medusajs/medusa-cli@1.2.1) (2022-02-28) ## [1.2.1](https://github.com/medusajs/medusa/compare/@medusajs/medusa-cli@1.1.27...@medusajs/medusa-cli@1.2.1) (2022-02-28)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@medusajs/medusa-cli", "name": "@medusajs/medusa-cli",
"version": "1.2.1", "version": "1.3.0",
"description": "Command Line interface for Medusa Commerce", "description": "Command Line interface for Medusa Commerce",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.1.0](https://github.com/medusajs/medusa/compare/medusa-file-s3@1.0.12...medusa-file-s3@1.1.0) (2022-05-01)
**Note:** Version bump only for package medusa-file-s3
## [1.0.12](https://github.com/medusajs/medusa/compare/medusa-file-s3@1.0.11...medusa-file-s3@1.0.12) (2022-01-11) ## [1.0.12](https://github.com/medusajs/medusa/compare/medusa-file-s3@1.0.11...medusa-file-s3@1.0.12) (2022-01-11)
**Note:** Version bump only for package medusa-file-s3 **Note:** Version bump only for package medusa-file-s3
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "medusa-file-s3", "name": "medusa-file-s3",
"version": "1.0.12", "version": "1.1.0",
"description": "AWS s3 file connector for Medusa", "description": "AWS s3 file connector for Medusa",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
@@ -24,7 +24,7 @@
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"jest": "^25.5.2", "jest": "^25.5.2",
"medusa-test-utils": "^0.3.0" "medusa-test-utils": "^1.1.37"
}, },
"scripts": { "scripts": {
"build": "babel src -d .", "build": "babel src -d .",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.2.0](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.1.37...medusa-file-spaces@1.2.0) (2022-05-01)
**Note:** Version bump only for package medusa-file-spaces
## [1.1.37](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.1.36...medusa-file-spaces@1.1.37) (2022-01-11) ## [1.1.37](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.1.36...medusa-file-spaces@1.1.37) (2022-01-11)
**Note:** Version bump only for package medusa-file-spaces **Note:** Version bump only for package medusa-file-spaces
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "medusa-file-spaces", "name": "medusa-file-spaces",
"version": "1.1.37", "version": "1.2.0",
"description": "Digital Ocean Spaces file connector for Medusa", "description": "Digital Ocean Spaces file connector for Medusa",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
@@ -24,7 +24,7 @@
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"jest": "^25.5.2", "jest": "^25.5.2",
"medusa-test-utils": "^0.3.0" "medusa-test-utils": "^1.1.37"
}, },
"scripts": { "scripts": {
"build": "babel src -d .", "build": "babel src -d .",
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.3.0](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.2.1...medusa-fulfillment-webshipper@1.3.0) (2022-05-01)
### Features
* Add DiscountConditions ([#1230](https://github.com/medusajs/medusa/issues/1230)) ([a610805](https://github.com/medusajs/medusa/commit/a610805917ee930d4cebde74905e541a468aa83b)), closes [#1146](https://github.com/medusajs/medusa/issues/1146) [#1149](https://github.com/medusajs/medusa/issues/1149) [#1156](https://github.com/medusajs/medusa/issues/1156) [#1170](https://github.com/medusajs/medusa/issues/1170) [#1172](https://github.com/medusajs/medusa/issues/1172) [#1212](https://github.com/medusajs/medusa/issues/1212) [#1224](https://github.com/medusajs/medusa/issues/1224) [#1228](https://github.com/medusajs/medusa/issues/1228)
## [1.2.1](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.35...medusa-fulfillment-webshipper@1.2.1) (2022-02-28) ## [1.2.1](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.35...medusa-fulfillment-webshipper@1.2.1) (2022-02-28)
@@ -1,6 +1,6 @@
{ {
"name": "medusa-fulfillment-webshipper", "name": "medusa-fulfillment-webshipper",
"version": "1.2.1", "version": "1.3.0",
"description": "Webshipper Fulfillment provider for Medusa", "description": "Webshipper Fulfillment provider for Medusa",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
@@ -18,7 +18,7 @@
"@babel/plugin-transform-runtime": "^7.7.6", "@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5", "@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6", "@babel/runtime": "^7.9.6",
"@medusajs/medusa": "^1.2.1", "@medusajs/medusa": "^1.3.0",
"client-sessions": "^0.8.0", "client-sessions": "^0.8.0",
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"eslint": "^6.8.0", "eslint": "^6.8.0",
@@ -1,4 +1,3 @@
import { TotalsService } from "@medusajs/medusa"
import { humanizeAmount } from "medusa-core-utils" import { humanizeAmount } from "medusa-core-utils"
import { FulfillmentService } from "medusa-interfaces" import { FulfillmentService } from "medusa-interfaces"
import Webshipper from "../utils/webshipper" import Webshipper from "../utils/webshipper"
@@ -114,13 +113,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
const fromOrder = await this.orderService_.retrieve(orderId, { const fromOrder = await this.orderService_.retrieve(orderId, {
select: ["total"], select: ["total"],
relations: [ relations: ["discounts", "discounts.rule", "shipping_address", "returns"],
"discounts",
"discounts.rule",
"discounts.rule.valid_for",
"shipping_address",
"returns",
],
}) })
const methodData = returnOrder.shipping_method.data const methodData = returnOrder.shipping_method.data
@@ -145,7 +138,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
} }
} }
let docs = [] const docs = []
if (this.invoiceGenerator_) { if (this.invoiceGenerator_) {
const base64Invoice = await this.invoiceGenerator_.createReturnInvoice( const base64Invoice = await this.invoiceGenerator_.createReturnInvoice(
fromOrder, fromOrder,
@@ -338,7 +331,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
} }
} }
let id = fulfillment.id const id = fulfillment.id
let visible_ref = `${fromOrder.display_id}-${id.substr(id.length - 4)}` let visible_ref = `${fromOrder.display_id}-${id.substr(id.length - 4)}`
let ext_ref = `${fromOrder.id}.${fulfillment.id}` let ext_ref = `${fromOrder.id}.${fulfillment.id}`
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.3.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.2.1...medusa-interfaces@1.3.0) (2022-05-01)
### Bug Fixes
* ensures no duplicate tax lines when completing cart ([#1262](https://github.com/medusajs/medusa/issues/1262)) ([607a382](https://github.com/medusajs/medusa/commit/607a382b4ee190c25eafa345674b55b74a7d6349))
## [1.2.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.34...medusa-interfaces@1.2.1) (2022-02-28) ## [1.2.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.34...medusa-interfaces@1.2.1) (2022-02-28)
+8 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "medusa-interfaces", "name": "medusa-interfaces",
"version": "1.2.1", "version": "1.3.0",
"description": "Core interfaces for Medusa", "description": "Core interfaces for Medusa",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
@@ -9,7 +9,7 @@
"directory": "packages/medusa-interfaces" "directory": "packages/medusa-interfaces"
}, },
"scripts": { "scripts": {
"build": "babel src --out-dir dist/ --ignore **/__tests__", "build": "tsc --build",
"prepare": "cross-env NODE_ENV=production npm run build", "prepare": "cross-env NODE_ENV=production npm run build",
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__", "watch": "babel -w src --out-dir dist/ --ignore **/__tests__",
"test": "jest" "test": "jest"
@@ -24,18 +24,20 @@
"@babel/plugin-transform-instanceof": "^7.8.3", "@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6", "@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5", "@babel/preset-env": "^7.7.5",
"@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.9.6", "@babel/runtime": "^7.9.6",
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"jest": "^25.5.2", "jest": "^25.5.2",
"medusa-core-utils": "^1.1.31",
"medusa-test-utils": "^1.1.37",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"typeorm": "^0.2.29" "typeorm": "^0.2.29",
"typescript": "^4.4.4"
}, },
"peerDependencies": { "peerDependencies": {
"medusa-core-utils": "^1.1.31",
"typeorm": "0.x" "typeorm": "0.x"
}, },
"dependencies": {
"medusa-core-utils": "^1.1.31"
},
"gitHead": "cd1f5afa5aa8c0b15ea957008ee19f1d695cbd2e" "gitHead": "cd1f5afa5aa8c0b15ea957008ee19f1d695cbd2e"
} }
+14 -5
View File
@@ -158,12 +158,18 @@ class BaseService {
* @param {string} isolation - the isolation level to be used for the work. * @param {string} isolation - the isolation level to be used for the work.
* @return {any} the result of the transactional work * @return {any} the result of the transactional work
*/ */
async atomicPhase_(work, isolationOrErrorHandler, maybeErrorHandler) { async atomicPhase_(
let errorHandler = maybeErrorHandler work,
isolationOrErrorHandler,
maybeErrorHandlerOrDontFail
) {
let errorHandler = maybeErrorHandlerOrDontFail
let isolation = isolationOrErrorHandler let isolation = isolationOrErrorHandler
let dontFail = false
if (typeof isolationOrErrorHandler === "function") { if (typeof isolationOrErrorHandler === "function") {
isolation = null isolation = null
errorHandler = isolationOrErrorHandler errorHandler = isolationOrErrorHandler
dontFail = !!maybeErrorHandlerOrDontFail
} }
if (this.transactionManager_) { if (this.transactionManager_) {
@@ -222,12 +228,15 @@ class BaseService {
} }
try { try {
const result = await this.manager_.transaction((m) => doWork(m)) return await this.manager_.transaction((m) => doWork(m))
return result
} catch (error) { } catch (error) {
if (errorHandler) { if (errorHandler) {
await errorHandler(error) const result = await errorHandler(error)
if (dontFail) {
return result
}
} }
throw error throw error
} }
} }
+28
View File
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"lib": ["es5", "es6"],
"target": "es5",
"outDir": "./dist",
"esModuleInterop": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true // to use ES5 specific tooling
},
"include": ["./src/**/*", "index.d.ts"],
"exclude": [
"./dist/**/*",
"./src/**/__tests__",
"./src/**/__mocks__",
"node_modules"
]
}
+23
View File
@@ -3,6 +3,29 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.2.0](https://github.com/medusajs/medusa-js/compare/@medusajs/medusa-js@1.1.1...@medusajs/medusa-js@1.2.0) (2022-05-01)
### Bug Fixes
* **medusa:** Remove unsupported Discount endpoints ([#1367](https://github.com/medusajs/medusa-js/issues/1367)) ([9acee27](https://github.com/medusajs/medusa-js/commit/9acee2799ead683575edd0f7172f336878569dfe))
* `CustomerGroups` missing features in the clients ([#1159](https://github.com/medusajs/medusa-js/issues/1159)) ([218b20b](https://github.com/medusajs/medusa-js/commit/218b20b26db46f0a91736ece2530a83fa94aed97))
* default `POST` request payloads to an empty object ([#1141](https://github.com/medusajs/medusa-js/issues/1141)) ([4e7435e](https://github.com/medusajs/medusa-js/commit/4e7435e4d706e9237e08e1bef1818c4d564a5f9c))
* merge conflicts ([562a1b4](https://github.com/medusajs/medusa-js/commit/562a1b427a6aeb634fbc8b1a6d023c451ca2cd62))
* use /admin/returns/:id/receive for swap returns ([#1041](https://github.com/medusajs/medusa-js/issues/1041)) ([7ae754b](https://github.com/medusajs/medusa-js/commit/7ae754bb6187db17c45b2cfadc625df8f997f5ab))
### Features
* customer group customers client endpoints ([#1221](https://github.com/medusajs/medusa-js/issues/1221)) ([b7f6996](https://github.com/medusajs/medusa-js/commit/b7f699654bd8c5b08919667d4e29c835901e1af9))
* customer groups client endpoints ([#1147](https://github.com/medusajs/medusa-js/issues/1147)) ([93426bf](https://github.com/medusajs/medusa-js/commit/93426bfc0263b3a19e6d47e19cc498fea441fb30))
* customer groups react hooks ([#1153](https://github.com/medusajs/medusa-js/issues/1153)) ([daf49bc](https://github.com/medusajs/medusa-js/commit/daf49bcaf31e6e86cfd13a24efd5b3de626617a4))
* new tax api ([#979](https://github.com/medusajs/medusa-js/issues/979)) ([c56660f](https://github.com/medusajs/medusa-js/commit/c56660fca9921a3f3637bc137d9794781c5b090f)), closes [#885](https://github.com/medusajs/medusa-js/issues/885) [#896](https://github.com/medusajs/medusa-js/issues/896) [#911](https://github.com/medusajs/medusa-js/issues/911) [#945](https://github.com/medusajs/medusa-js/issues/945) [#950](https://github.com/medusajs/medusa-js/issues/950) [#951](https://github.com/medusajs/medusa-js/issues/951) [#954](https://github.com/medusajs/medusa-js/issues/954) [#969](https://github.com/medusajs/medusa-js/issues/969) [#998](https://github.com/medusajs/medusa-js/issues/998) [#1017](https://github.com/medusajs/medusa-js/issues/1017) [#1110](https://github.com/medusajs/medusa-js/issues/1110)
## [1.1.1](https://github.com/medusajs/medusa-js/compare/@medusajs/medusa-js@1.0.12...@medusajs/medusa-js@1.1.1) (2022-02-28) ## [1.1.1](https://github.com/medusajs/medusa-js/compare/@medusajs/medusa-js@1.0.12...@medusajs/medusa-js@1.1.1) (2022-02-28)
+5 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@medusajs/medusa-js", "name": "@medusajs/medusa-js",
"version": "1.1.1", "version": "1.2.0",
"description": "Client for Medusa Commerce Rest API", "description": "Client for Medusa Commerce Rest API",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -15,7 +15,7 @@
"author": "Oliver Juhl", "author": "Oliver Juhl",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@medusajs/medusa": "^1.2.1", "@medusajs/medusa": "^1.3.0",
"axios": "^0.24.0", "axios": "^0.24.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"qs": "^6.10.3", "qs": "^6.10.3",
@@ -23,10 +23,11 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/medusajs/medusa-js.git" "url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-js"
}, },
"bugs": { "bugs": {
"url": "https://github.com/medusajs/medusa-js/issues" "url": "https://github.com/medusajs/medusa/issues"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^26.0.19", "@types/jest": "^26.0.19",
+4
View File
@@ -187,6 +187,10 @@ class Client {
options: RequestOptions = {}, options: RequestOptions = {},
customHeaders: Record<string, any> = {} customHeaders: Record<string, any> = {}
): Promise<any> { ): Promise<any> {
if (method === "POST" && !payload) {
payload = {}
}
const reqOpts = { const reqOpts = {
method, method,
withCredentials: true, withCredentials: true,
@@ -0,0 +1,160 @@
import {
AdminPostCustomerGroupsReq,
AdminCustomerGroupsRes,
AdminGetCustomerGroupsParams,
AdminCustomerGroupsListRes,
AdminPostCustomerGroupsGroupReq,
AdminCustomerGroupsDeleteRes,
AdminPostCustomerGroupsGroupCustomersBatchReq,
AdminDeleteCustomerGroupsGroupCustomerBatchReq,
AdminGetCustomerGroupsGroupParams,
AdminCustomersListRes,
AdminGetCustomersParams,
} from "@medusajs/medusa"
import qs from "qs"
import BaseResource from "../base"
import { ResponsePromise } from "../.."
class AdminCustomerGroupsResource extends BaseResource {
/**
* Create a customer group.
*
* @param payload - customer group info
* @param customHeaders
*/
create(
payload: AdminPostCustomerGroupsReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsRes> {
const path = `/admin/customer-groups`
return this.client.request("POST", path, payload, {}, customHeaders)
}
/**
* Retrieves a customer group.
*
* @param id - customer group id
* @param query - pass query options such as "expand", "fields" etc.
* @param customHeaders
*/
retrieve(
id: string,
query?: AdminGetCustomerGroupsGroupParams,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsRes> {
let path = `/admin/customer-groups/${id}`
if (query) {
const queryString = qs.stringify(query)
path += `?${queryString}`
}
return this.client.request("GET", path, {}, {}, customHeaders)
}
/**
* Updates a customer group
*
* @param id - customer group id
* @param payload - data to update customer group with
* @param customHeaders
*/
update(
id: string,
payload: AdminPostCustomerGroupsGroupReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsRes> {
const path = `/admin/customer-groups/${id}`
return this.client.request("POST", path, payload, {}, customHeaders)
}
/**
* Deletes a customer group.
*
* @param id - id of the customer group
* @param customHeaders
*/
delete(
id: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsDeleteRes> {
const path = `/admin/customer-groups/${id}`
return this.client.request("DELETE", path, {}, {}, customHeaders)
}
/**
* Lists customer groups.
*
* @param query optional
* @param customHeaders
*/
list(
query?: AdminGetCustomerGroupsParams,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsListRes> {
let path = `/admin/customer-groups`
if (query) {
const queryString = qs.stringify(query)
path = `/admin/customer-groups?${queryString}`
}
return this.client.request("GET", path, {}, {}, customHeaders)
}
/**
* Add multiple customers to a customer group.
*
* @param id - customer group id
* @param payload - an object which contains an array of customer ids which will be added to the group
* @param customHeaders
*/
addCustomers(
id: string,
payload: AdminPostCustomerGroupsGroupCustomersBatchReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsRes> {
const path = `/admin/customer-groups/${id}/customers/batch`
return this.client.request("POST", path, payload, {}, customHeaders)
}
/**
* Remove multiple customers from a customer group.
*
* @param id - customer group id
* @param payload - an object which contains an array of customers ids which will be removed from the group
* @param customHeaders
*/
removeCustomers(
id: string,
payload: AdminDeleteCustomerGroupsGroupCustomerBatchReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomerGroupsRes> {
const path = `/admin/customer-groups/${id}/customers/batch`
return this.client.request("DELETE", path, payload, {}, customHeaders)
}
/**
* List and count customers that belong to provided customer groups.
*
* @param id - customer group id
* @param query - params for filtering customers
* @param customHeaders
*/
listCustomers(
id: string,
query?: AdminGetCustomersParams,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomersListRes> {
let path = `/admin/customer-groups/${id}/customers`
if (query) {
const queryString = qs.stringify(query)
path += `?${queryString}`
}
return this.client.request("GET", path, {}, {}, customHeaders)
}
}
export default AdminCustomerGroupsResource
@@ -15,7 +15,10 @@ class AdminCustomersResource extends BaseResource {
* @param payload information of customer * @param payload information of customer
* @param customHeaders * @param customHeaders
*/ */
create(payload: AdminPostCustomersReq, customHeaders: Record<string, any> = {}): ResponsePromise<AdminCustomersRes> { create(
payload: AdminPostCustomersReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomersRes> {
const path = `/admin/customers` const path = `/admin/customers`
return this.client.request("POST", path, payload, {}, customHeaders) return this.client.request("POST", path, payload, {}, customHeaders)
} }
@@ -40,7 +43,10 @@ class AdminCustomersResource extends BaseResource {
* @param id customer id * @param id customer id
* @param customHeaders * @param customHeaders
*/ */
retrieve(id: string, customHeaders: Record<string, any> = {}): ResponsePromise<AdminCustomersRes> { retrieve(
id: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomersRes> {
const path = `/admin/customers/${id}` const path = `/admin/customers/${id}`
return this.client.request("GET", path, {}, {}, customHeaders) return this.client.request("GET", path, {}, {}, customHeaders)
} }
@@ -2,6 +2,7 @@ import {
AdminDiscountsDeleteRes, AdminDiscountsDeleteRes,
AdminDiscountsListRes, AdminDiscountsListRes,
AdminDiscountsRes, AdminDiscountsRes,
AdminGetDiscountParams,
AdminGetDiscountsParams, AdminGetDiscountsParams,
AdminPostDiscountsDiscountDynamicCodesReq, AdminPostDiscountsDiscountDynamicCodesReq,
AdminPostDiscountsDiscountReq, AdminPostDiscountsDiscountReq,
@@ -15,26 +16,22 @@ class AdminDiscountsResource extends BaseResource {
/** /**
* @description Adds region to discount * @description Adds region to discount
*/ */
addRegion(id: string, regionId: string, customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { addRegion(
id: string,
regionId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/regions/${regionId}` const path = `/admin/discounts/${id}/regions/${regionId}`
return this.client.request("POST", path, {}, {}, customHeaders) return this.client.request("POST", path, {}, {}, customHeaders)
} }
/**
* @description Add valid product
*/
addValidProduct(
id: string,
productId: string,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/products/${productId}`
return this.client.request("POST", path, {})
}
/** /**
* @description Creates discounts * @description Creates discounts
*/ */
create(payload: AdminPostDiscountsReq, customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { create(
payload: AdminPostDiscountsReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts` const path = `/admin/discounts`
return this.client.request("POST", path, payload, {}, customHeaders) return this.client.request("POST", path, payload, {}, customHeaders)
} }
@@ -45,7 +42,8 @@ class AdminDiscountsResource extends BaseResource {
update( update(
id: string, id: string,
payload: AdminPostDiscountsDiscountReq, payload: AdminPostDiscountsDiscountReq,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}` const path = `/admin/discounts/${id}`
return this.client.request("POST", path, payload, {}, customHeaders) return this.client.request("POST", path, payload, {}, customHeaders)
} }
@@ -56,7 +54,8 @@ class AdminDiscountsResource extends BaseResource {
createDynamicCode( createDynamicCode(
id: string, id: string,
payload: AdminPostDiscountsDiscountDynamicCodesReq, payload: AdminPostDiscountsDiscountDynamicCodesReq,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/dynamic-codes` const path = `/admin/discounts/${id}/dynamic-codes`
return this.client.request("POST", path, payload, {}, customHeaders) return this.client.request("POST", path, payload, {}, customHeaders)
} }
@@ -64,7 +63,10 @@ class AdminDiscountsResource extends BaseResource {
/** /**
* @description Deletes a discount * @description Deletes a discount
*/ */
delete(id: string, customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsDeleteRes> { delete(
id: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsDeleteRes> {
const path = `/admin/discounts/${id}` const path = `/admin/discounts/${id}`
return this.client.request("DELETE", path, {}, {}, customHeaders) return this.client.request("DELETE", path, {}, {}, customHeaders)
} }
@@ -75,7 +77,8 @@ class AdminDiscountsResource extends BaseResource {
deleteDynamicCode( deleteDynamicCode(
id: string, id: string,
code: string, code: string,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/dynamic-codes/${code}` const path = `/admin/discounts/${id}/dynamic-codes/${code}`
return this.client.request("DELETE", path, {}, {}, customHeaders) return this.client.request("DELETE", path, {}, {}, customHeaders)
} }
@@ -83,7 +86,10 @@ class AdminDiscountsResource extends BaseResource {
/** /**
* @description Retrieves a discount * @description Retrieves a discount
*/ */
retrieve(id: string, customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { retrieve(
id: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}` const path = `/admin/discounts/${id}`
return this.client.request("GET", path, {}, {}, customHeaders) return this.client.request("GET", path, {}, {}, customHeaders)
} }
@@ -91,7 +97,10 @@ class AdminDiscountsResource extends BaseResource {
/** /**
* @description Retrieves a discount by code * @description Retrieves a discount by code
*/ */
retrieveByCode(code: string, customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { retrieveByCode(
code: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/code/${code}` const path = `/admin/discounts/code/${code}`
return this.client.request("GET", path, {}, {}, customHeaders) return this.client.request("GET", path, {}, {}, customHeaders)
} }
@@ -101,7 +110,8 @@ class AdminDiscountsResource extends BaseResource {
*/ */
list( list(
query?: AdminGetDiscountsParams, query?: AdminGetDiscountsParams,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsListRes> { customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsListRes> {
let path = `/admin/discounts` let path = `/admin/discounts`
if (query) { if (query) {
@@ -118,21 +128,11 @@ class AdminDiscountsResource extends BaseResource {
removeRegion( removeRegion(
id: string, id: string,
regionId: string, regionId: string,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> { customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/regions/${regionId}` const path = `/admin/discounts/${id}/regions/${regionId}`
return this.client.request("DELETE", path, {}, {}, customHeaders) return this.client.request("DELETE", path, {}, {}, customHeaders)
} }
/**
* @description Removes a valid product from a discount
*/
removeValidProduct(
id: string,
productId: string,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/products/${productId}`
return this.client.request("DELETE", path, {}, {}, customHeaders)
}
} }
export default AdminDiscountsResource export default AdminDiscountsResource
@@ -1,6 +1,7 @@
import BaseResource from "../base" import BaseResource from "../base"
import AdminAuthResource from "./auth" import AdminAuthResource from "./auth"
import AdminCustomersResource from "./customers" import AdminCustomersResource from "./customers"
import AdminCustomerGroupsResource from "./customer-groups"
import AdminDiscountsResource from "./discounts" import AdminDiscountsResource from "./discounts"
import CollectionsResource from "./collections" import CollectionsResource from "./collections"
import AdminDraftOrdersResource from "./draft-orders" import AdminDraftOrdersResource from "./draft-orders"
@@ -23,16 +24,19 @@ import AdminRegionsResource from "./regions"
import AdminNotificationsResource from "./notifications" import AdminNotificationsResource from "./notifications"
import AdminUploadsResource from "./uploads" import AdminUploadsResource from "./uploads"
import AdminProductTagsResource from "./product-tags" import AdminProductTagsResource from "./product-tags"
import AdminPriceListResource from "./price-lists"
class Admin extends BaseResource { class Admin extends BaseResource {
public auth = new AdminAuthResource(this.client) public auth = new AdminAuthResource(this.client)
public customers = new AdminCustomersResource(this.client) public customers = new AdminCustomersResource(this.client)
public customerGroups = new AdminCustomerGroupsResource(this.client)
public discounts = new AdminDiscountsResource(this.client) public discounts = new AdminDiscountsResource(this.client)
public collections = new CollectionsResource(this.client) public collections = new CollectionsResource(this.client)
public draftOrders = new AdminDraftOrdersResource(this.client) public draftOrders = new AdminDraftOrdersResource(this.client)
public giftCards = new AdminGiftCardsResource(this.client) public giftCards = new AdminGiftCardsResource(this.client)
public invites = new AdminInvitesResource(this.client) public invites = new AdminInvitesResource(this.client)
public notes = new AdminNotesResource(this.client) public notes = new AdminNotesResource(this.client)
public priceLists = new AdminPriceListResource(this.client)
public products = new AdminProductsResource(this.client) public products = new AdminProductsResource(this.client)
public productTags = new AdminProductTagsResource(this.client) public productTags = new AdminProductTagsResource(this.client)
public productTypes = new AdminProductTypesResource(this.client) public productTypes = new AdminProductTypesResource(this.client)
Loaded 100 of 414 files, more files were not shown because too many files have changed in this diff. Show more