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
415 files changed
+28842 -10089

No files matched your search

+13 -13
View File
@@ -5,19 +5,19 @@
/packages/medusa/src/subscribers/notification.js
/packages/medusa/src/subscribers/order.js
/packages/medusa/src/subscribers/product.js
/packages/medusa/src/loaders/api.js
/packages/medusa/src/loaders/database.js
/packages/medusa/src/loaders/defaults.js
/packages/medusa/src/loaders/express.js
/packages/medusa/src/loaders/index.js
/packages/medusa/src/loaders/logger.js
/packages/medusa/src/loaders/models.js
/packages/medusa/src/loaders/passport.js
/packages/medusa/src/loaders/plugins.js
/packages/medusa/src/loaders/redis.js
/packages/medusa/src/loaders/repositories.js
/packages/medusa/src/loaders/services.js
/packages/medusa/src/loaders/subscribers.js
/packages/medusa/src/loaders/api.ts
/packages/medusa/src/loaders/database.ts
/packages/medusa/src/loaders/defaults.ts
/packages/medusa/src/loaders/express.ts
/packages/medusa/src/loaders/index.ts
/packages/medusa/src/loaders/logger.ts
/packages/medusa/src/loaders/models.ts
/packages/medusa/src/loaders/passport.ts
/packages/medusa/src/loaders/plugins.ts
/packages/medusa/src/loaders/redis.ts
/packages/medusa/src/loaders/repositories.ts
/packages/medusa/src/loaders/services.ts
/packages/medusa/src/loaders/subscribers.ts
/packages/medusa/src/api/routes/admin/auth
/packages/medusa/src/api/routes/admin/collections
/packages/medusa/src/api/routes/store/carts
+8 -4
View File
@@ -10,7 +10,7 @@ Thank you for considering contributing to Medusa! This document will outline how
## Issues before PRs
1. Before you start working on a change please make sure that there is an issue for what you will be working on. You can either find and [existing issue](https://github.com/medusajs/medusa/issues) or [open a new issue](https://github.com/medusajs/medusa/issues/new) if none exists. Doing this makes sure that others can contribute with thoughts or suggest alternatives, ultimately making sure that we only add changes that make
1. Before you start working on a change please make sure that there is an issue for what you will be working on. You can either find and [existing issue](https://github.com/medusajs/medusa/issues) or [open a new issue](https://github.com/medusajs/medusa/issues/new) if none exists. Doing this makes sure that others can contribute with thoughts or suggest alternatives, ultimately making sure that we only add changes that make
2. When you are ready to start working on a change you should first [fork the Medusa repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) and [branch out](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-and-deleting-branches-within-your-repository) from the `develop` branch.
3. Make your changes.
@@ -21,17 +21,21 @@ Thank you for considering contributing to Medusa! This document will outline how
### Branches
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
- `feat/` for features
- `docs/` for documentation changes
### 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.
### 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:
- **What** - what changes are in this PR
- **Why** - why are these changes relevant
- **How** - how have the changes been implemented
@@ -52,8 +56,8 @@ All PRs should include tests for the changes that are included. We have two type
### Documentation
- We generally encourage to document your changes through comments in your code.
- If you alter user-facing behaviour you must provide documentation for such changes.
- We generally encourage to document your changes through comments in your code.
- If you alter user-facing behaviour you must provide documentation for such changes.
- All methods and endpoints should be documented using [JSDoc](https://jsdoc.app/) and [`swagger-inline`](https://www.npmjs.com/package/swagger-inline)
### Release
+1 -1
View File
@@ -81,7 +81,7 @@ Write-ups for all features will be made available in [Github discussions](https:
- [ ] Sales Channel API
- [ ] Multi-warehouse support
- [ ] GraphQL API
## Setting up a storefront for your Medusa project
Medusa is a headless commerce engine which means that it can be used for any type of digital commerce experience - you may use it as the backend for an app, a voice application, social commerce experiences or a traditional e-commerce website, you may even want to integrate Medusa into your own software to enable commerce functionality. All of these are use cases that Medusa supports - to learn more read the documentation or reach out.
+217 -217
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**
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.
+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>
### Order Cancelled
### Order Canceled
**Key in plugin options:** `order_canceled_template`
@@ -24,7 +24,7 @@ export default () => {
router.get("/admin/hello", (req, res) => {
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
const corsOptions = {
origin: projectConfig.admin_cors.split(","),
credentials: true,
origin: projectConfig.admin_cors.split(","),
credentials: true,
}
```
@@ -72,7 +72,7 @@ Finally, for each route you add, create an `OPTIONS` request:
router.options("/admin/hello", cors(corsOptions))
router.get("/admin/hello", (req, res) => {
//...
});
})
```
## Multiple Endpoints
@@ -84,13 +84,13 @@ You can add more than one endpoints in `src/api/index.js`:
```js
router.get("/admin/hello", (req, res) => {
res.json({
message: "Welcome to Your Store!"
message: "Welcome to Your Store!",
})
})
router.get("/admin/bye", (req, res) => {
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) => {
router.get("/admin/hello", (req, res) => {
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) => {
router.get("/admin/bye", (req, res) => {
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:
```js
router.get('/admin/products/count', (req, res) => {
const productService = req.scope.resolve('productService')
router.get("/admin/products/count", (req, res) => {
const productService = req.scope.resolve("productService")
productService
.count()
.then((count) => {
res.json({
count
})
})
productService.count().then((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.
@@ -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:
```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:
```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) => {
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
router.get("/store/hello", (req, res) => {
res.json({
message: "Welcome to My Store!"
message: "Welcome to My Store!",
})
})
router.get("/store/bye", (req, res) => {
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) => {
router.get("/store/hello", (req, res) => {
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) => {
router.get("/store/bye", (req, res) => {
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:
```js
router.get('/store/products/count', (req, res) => {
const productService = req.scope.resolve('productService')
router.get("/store/products/count", (req, res) => {
const productService = req.scope.resolve("productService")
productService
.count()
.then((count) => {
res.json({
count
})
})
productService.count().then((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.
@@ -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:
```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:
```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.
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`.
@@ -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)
## 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
import { PaymentService } from "medusa-interfaces"
@@ -1,7 +1,5 @@
---
title: 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:
```js
import { BaseService } from "medusa-interfaces";
import { BaseService } from "medusa-interfaces"
class HelloService extends BaseService {
getMessage() {
@@ -39,7 +37,7 @@ class HelloService extends BaseService {
}
}
export default HelloService;
export default HelloService
```
## 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:
```js
const helloService = req.scope.resolve('helloService');
const helloService = req.scope.resolve("helloService")
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
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
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...
```
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
@@ -87,7 +87,7 @@ Inside the root folder which was specified at the beginning of the installation
/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
⠴ Installing packages...
@@ -112,10 +112,10 @@ Create initial git commit in my-medusa-store/admin
## **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)
- [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)
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
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>
<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
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
heroku create medusa-test-app
@@ -70,13 +70,13 @@ Medusa requires a Postgres database and a Redis instance to work. These are adde
:::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
Add a Postgres addon to your Heroku app
Add a Postgres add-on to your Heroku app
```bash
heroku addons:create heroku-postgresql:hobby-dev
@@ -90,7 +90,7 @@ Add a Redis instance to your Heroku app
:::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
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
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
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
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.
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
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 })
```
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
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
/*
+3 -5
View File
@@ -1,7 +1,5 @@
---
title: Introduction
---
## Architecture overview
@@ -18,11 +16,11 @@ Your Medusa server will include all functionalities related to your store’s ch
### Admin Dashboard
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
+22 -20
View File
@@ -1,6 +1,12 @@
# 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
@@ -29,33 +35,29 @@ You can install Node from the [official website](https://nodejs.org/en/).
3. **Start your Medusa engine**
```bash
cd my-medusa-store
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
curl localhost:9000/store/products | python -m json.tool
```
```bash
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.
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)
:::note
## 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
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`.
```bash
curl localhost:9000/store/products
```
:::
<!-- ### 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
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
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
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
- 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
```
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.
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
@@ -64,7 +64,7 @@ cd my-medusa-server
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
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
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)
- [Gatsby Starter](https://github.com/medusajs/gatsby-starter-medusa)
### 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)
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
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",
"height": null,
"hs_code": null,
"id": StringMatching /\\^test-\\*/,
"id": "test-product",
"images": Array [
Object {
"created_at": Any<String>,
@@ -78,6 +78,7 @@ Array [
Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -103,15 +104,19 @@ Array [
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
"created_at": Any<String>,
"currency_code": "usd",
"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,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/,
},
@@ -127,6 +132,7 @@ Array [
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean2",
@@ -152,6 +158,7 @@ Array [
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -159,10 +166,13 @@ Array [
"currency_code": "usd",
"deleted_at": null,
"id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/,
"variant_id": "test-variant_2",
},
],
"product_id": StringMatching /\\^test-\\*/,
@@ -176,6 +186,7 @@ Array [
Object {
"allow_backorder": false,
"barcode": "test-barcode 1",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean1",
@@ -201,6 +212,7 @@ Array [
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -208,8 +220,11 @@ Array [
"currency_code": "usd",
"deleted_at": null,
"id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/,
},
@@ -222,6 +237,60 @@ Array [
"weight": 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,
"width": null,
@@ -245,7 +314,7 @@ Array [
"handle": "test-product1",
"height": null,
"hs_code": null,
"id": StringMatching /\\^test-\\*/,
"id": "test-product1",
"images": Array [],
"is_giftcard": false,
"length": null,
@@ -283,6 +352,7 @@ Array [
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean4",
@@ -308,6 +378,7 @@ Array [
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -315,8 +386,11 @@ Array [
"currency_code": "usd",
"deleted_at": null,
"id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": null,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/,
},
@@ -332,6 +406,7 @@ Array [
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean3",
@@ -357,6 +432,7 @@ Array [
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -364,8 +440,11 @@ Array [
"currency_code": "usd",
"deleted_at": null,
"id": StringMatching /\\^test-price\\*/,
"max_quantity": null,
"min_quantity": null,
"price_list": null,
"price_list_id": null,
"region_id": "test-region",
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": StringMatching /\\^test-variant\\*/,
},
@@ -382,39 +461,6 @@ Array [
"weight": 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 {
"collection": Any<Object>,
"collection_id": "test-collection1",
@@ -426,7 +472,7 @@ Array [
"handle": "test-product_filtering_1",
"height": null,
"hs_code": null,
"id": StringMatching /\\^test-\\*/,
"id": "test-product_filtering_1",
"images": Array [],
"is_giftcard": false,
"length": null,
@@ -459,7 +505,7 @@ Array [
"handle": "test-product_filtering_2",
"height": null,
"hs_code": null,
"id": StringMatching /\\^test-\\*/,
"id": "test-product_filtering_2",
"images": Array [],
"is_giftcard": false,
"length": null,
@@ -481,6 +527,39 @@ Array [
"weight": 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 {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
@@ -554,6 +634,7 @@ Array [
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -561,8 +642,11 @@ Array [
"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,
"sale_amount": null,
"updated_at": Any<String>,
"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) => {
expect(err.response.status).toEqual(402)
expect(err.response.status).toEqual(422)
expect(err.response.data.type).toEqual("duplicate_error")
expect(err.response.data.message).toEqual(
"Key (name)=(vip-customers) already exists."
@@ -130,7 +130,7 @@ describe("/admin/customer-groups", () => {
.catch((error) => {
expect(error.response.data.type).toEqual("not_found")
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", () => {
beforeEach(async () => {
try {
@@ -290,6 +513,66 @@ describe("/admin/customer-groups", () => {
const db = useDb()
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 () => {
const api = useApi()
@@ -336,8 +619,6 @@ describe("/admin/customer-groups", () => {
})
it("throws error when a customer group doesn't exist", async () => {
expect.assertions(3)
const api = useApi()
const id = "test-group-000"
@@ -352,7 +633,7 @@ describe("/admin/customer-groups", () => {
expect(err.response.status).toEqual(404)
expect(err.response.data.type).toEqual("not_found")
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 () => {
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 () => {
const api = useApi()
let response = await api
@@ -250,33 +306,10 @@ describe("/admin/customers", () => {
expect(response.status).toEqual(200)
expect(response.data.customer.groups).toEqual(
expect.arrayContaining([
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" }),
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).toEqual(
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-4",
name: "test-group-4",
}),
])
)
})
+594 -18
View File
@@ -1,5 +1,11 @@
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 { useApi } = require("../../../helpers/use-api")
@@ -7,6 +13,10 @@ const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const discountSeeder = require("../../helpers/discount-seeder")
const { exportAllDeclaration } = require("@babel/types")
const { simpleProductFactory } = require("../../factories")
const {
simpleDiscountFactory,
} = require("../../factories/simple-discount-factory")
jest.setTimeout(30000)
@@ -26,6 +36,205 @@ describe("/admin/discounts", () => {
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", () => {
beforeEach(async () => {
const manager = dbConnection.manager
@@ -267,27 +476,394 @@ describe("/admin/discounts", () => {
usage_limit: 10,
})
)
})
const test = await api.get(
`/admin/discounts/${response.data.discount.id}`,
{ headers: { Authorization: "Bearer test_token" } }
)
it("creates a discount with conditions", async () => {
const api = useApi()
expect(test.status).toEqual(200)
expect(test.data.discount).toEqual(
expect.objectContaining({
code: "HELLOWORLD",
usage_limit: 10,
rule: expect.objectContaining({
value: 10,
type: "percentage",
description: "test",
allocation: "total",
}),
const product = await simpleProductFactory(dbConnection, {
type: "pants",
tags: ["ss22"],
})
const anotherProduct = await simpleProductFactory(dbConnection, {
type: "blouses",
tags: ["ss23"],
})
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 () => {
const api = useApi()
@@ -882,8 +1458,8 @@ describe("/admin/discounts", () => {
}
)
} catch (error) {
expect(error.response.data.message).toMatch(
/duplicate key value violates unique constraint/i
expect(error.response.data.message).toEqual(
"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 () => {
const api = useApi()
@@ -1555,6 +1555,116 @@ describe("/admin/orders", () => {
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 () => {
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 productSeeder = require("../../helpers/product-seeder")
const { ProductVariant } = require("@medusajs/medusa")
const priceListSeeder = require("../../helpers/price-list-seeder")
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 () => {
const api = useApi()
@@ -521,10 +566,13 @@ describe("/admin/products", () => {
console.log(err)
})
response.data.products.sort((a, b) =>
a.created_at > b.created_at ? 1 : -1
)
expect(response.data.products).toMatchSnapshot([
{
id: expect.stringMatching(/^test-*/),
created_at: expect.any(String),
id: "test-product",
options: [
{
id: expect.stringMatching(/^test-*/),
@@ -542,7 +590,53 @@ describe("/admin/products", () => {
],
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),
updated_at: expect.any(String),
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),
updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/),
prices: [
{
id: expect.stringMatching(/^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_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*/),
id: "test-price-sale",
variant_id: expect.stringMatching(/^test-variant*/),
created_at: expect.any(String),
updated_at: expect.any(String),
@@ -633,12 +704,12 @@ describe("/admin/products", () => {
updated_at: expect.any(String),
},
{
id: expect.stringMatching(/^test-*/),
id: "test-product1",
created_at: expect.any(String),
options: [],
variants: [
{
id: "test-variant_4", // expect.stringMatching(/^test-variant*/),
id: "test-variant_4",
created_at: expect.any(String),
updated_at: expect.any(String),
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),
updated_at: expect.any(String),
product_id: expect.stringMatching(/^test-*/),
@@ -702,11 +773,10 @@ describe("/admin/products", () => {
updated_at: expect.any(String),
},
profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.stringMatching(/^test-*/),
id: "test-product_filtering_1",
profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String),
type: expect.any(Object),
@@ -717,7 +787,7 @@ describe("/admin/products", () => {
updated_at: expect.any(String),
},
{
id: expect.stringMatching(/^test-*/),
id: "test-product_filtering_2",
profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String),
type: expect.any(Object),
@@ -728,7 +798,7 @@ describe("/admin/products", () => {
updated_at: expect.any(String),
},
{
id: expect.stringMatching(/^test-*/),
id: "test-product_filtering_3",
profile_id: expect.stringMatching(/^sp_*/),
created_at: expect.any(String),
type: expect.any(Object),
@@ -773,7 +843,20 @@ describe("/admin/products", () => {
{
title: "Test variant",
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" }],
},
],
@@ -790,66 +873,126 @@ describe("/admin/products", () => {
})
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(
expect.objectContaining({
title: "Test",
discountable: true,
is_giftcard: false,
handle: "test",
status: "draft",
images: expect.arrayContaining([
expect.objectContaining({
url: "test-image.png",
}),
expect.objectContaining({
url: "test-image-2.png",
}),
]),
thumbnail: "test-image.png",
tags: [
expect.objectContaining({
value: "123",
}),
expect.objectContaining({
value: "456",
}),
],
type: expect.objectContaining({
value: "test-type",
}),
collection: expect.objectContaining({
id: "test-collection",
title: "Test collection",
}),
options: [
expect.objectContaining({
title: "size",
}),
expect.objectContaining({
title: "color",
}),
],
variants: [
expect.objectContaining({
title: "Test variant",
prices: [
expect.objectContaining({
currency_code: "usd",
amount: 100,
}),
],
options: [
expect.objectContaining({
value: "large",
}),
expect.objectContaining({
value: "green",
}),
],
}),
],
})
)
expect(response.data.product).toMatchSnapshot({
id: expect.stringMatching(/^prod_*/),
title: "Test",
discountable: true,
is_giftcard: false,
handle: "test",
status: "draft",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
images: [
{
id: expect.any(String),
url: "test-image.png",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
url: "test-image-2.png",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
thumbnail: "test-image.png",
tags: [
{
id: expect.any(String),
value: "123",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: expect.any(String),
value: "456",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
type: {
value: "test-type",
created_at: expect.any(String),
updated_at: expect.any(String),
},
collection: {
id: "test-collection",
title: "Test collection",
created_at: expect.any(String),
updated_at: expect.any(String),
},
options: [
{
id: expect.stringMatching(/^opt_*/),
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 () => {
@@ -1006,8 +1149,7 @@ describe("/admin/products", () => {
prices: [
{
currency_code: "usd",
amount: 100,
sale_amount: 75,
amount: 75,
},
],
},
@@ -1030,36 +1172,92 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200)
expect(response.data.product).toEqual(
expect.objectContaining({
images: expect.arrayContaining([
expect.objectContaining({
url: "test-image-2.png",
}),
]),
thumbnail: "test-image-2.png",
tags: [
expect.objectContaining({
value: "123",
}),
],
variants: [
expect.objectContaining({
prices: [
expect.objectContaining({
sale_amount: 75,
amount: 100,
}),
],
}),
],
status: "published",
collection: null,
type: expect.objectContaining({
value: "test-type-2",
}),
})
)
expect(response.data.product).toMatchSnapshot({
id: "test-product",
created_at: expect.any(String),
description: "test-product-description",
discountable: true,
handle: "test-product",
images: [
{
created_at: expect.any(String),
deleted_at: null,
id: expect.stringMatching(/^img_*/),
metadata: null,
updated_at: expect.any(String),
url: "test-image-2.png",
},
],
is_giftcard: false,
options: [
{
created_at: expect.any(String),
id: "test-option",
product_id: "test-product",
title: "test-option",
updated_at: expect.any(String),
},
],
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 () => {
@@ -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 () => {
try {
await productSeeder(dbConnection)
await adminSeeder(dbConnection)
await priceListSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
@@ -1205,7 +1404,7 @@ describe("/admin/products", () => {
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 data = {
prices: [
@@ -1227,7 +1426,6 @@ describe("/admin/products", () => {
})
expect(response.status).toEqual(200)
expect(response.data).toEqual({
product: expect.objectContaining({
id: "test-product",
@@ -1294,8 +1492,9 @@ describe("/admin/products", () => {
prices: [
// usd price coming from the product seeder
{
currency_code: "usd",
id: "test-price",
amount: 100,
currency_code: "usd",
},
{
currency_code: "eur",
@@ -1316,26 +1515,28 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200)
expect(response.data).toEqual({
product: expect.objectContaining({
id: "test-product",
variants: expect.arrayContaining([
expect.objectContaining({
id: "test-variant",
prices: [
expect.objectContaining({
amount: 100,
currency_code: "usd",
}),
expect.objectContaining({
amount: 4500,
currency_code: "eur",
}),
],
}),
]),
}),
})
expect(response.data).toEqual(
expect.objectContaining({
product: expect.objectContaining({
id: "test-product",
variants: expect.arrayContaining([
expect.objectContaining({
id: "test-variant",
prices: expect.arrayContaining([
expect.objectContaining({
amount: 100,
currency_code: "usd",
}),
expect.objectContaining({
amount: 4500,
currency_code: "eur",
}),
]),
}),
]),
}),
})
)
})
it("successfully updates a variant's prices by replacing a price", async () => {
@@ -1343,7 +1544,7 @@ describe("/admin/products", () => {
const data = {
prices: [
{
currency_code: "eur",
currency_code: "usd",
amount: 4500,
},
],
@@ -1361,15 +1562,15 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200)
expect(response.data.product.variants[0].prices.length).toEqual(
data.prices.length
expect(response.data.product.variants[0].prices.length).toEqual(1)
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 () => {
@@ -1399,19 +1600,20 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200)
expect(response.data.product.variants[0].prices.length).toEqual(
data.prices.length
expect(response.data.product.variants[0].prices.length).toEqual(2)
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 () => {
@@ -1627,7 +1829,7 @@ describe("/admin/products", () => {
})
} catch (error) {
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) {
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 () => {
const api = useApi()
const product = await api
await api
.get("/admin/products/test-product", {
headers: {
Authorization: "bearer test_token",
+135 -11
View File
@@ -11,9 +11,9 @@ jest.setTimeout(30000)
describe("/admin/regions", () => {
let medusaProcess
let dbConnection
const cwd = path.resolve(path.join(__dirname, "..", ".."))
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
@@ -21,11 +21,98 @@ describe("/admin/regions", () => {
afterAll(async () => {
const db = useDb()
await db.shutdown()
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", () => {
beforeAll(async () => {
beforeEach(async () => {
const manager = dbConnection.manager
await adminSeeder(dbConnection)
await manager.insert(Region, {
@@ -57,7 +144,7 @@ describe("/admin/regions", () => {
})
})
afterAll(async () => {
afterEach(async () => {
const db = useDb()
await db.teardown()
})
@@ -116,14 +203,23 @@ describe("/admin/regions", () => {
describe("DELETE /admin/regions/:id", () => {
beforeEach(async () => {
const manager = dbConnection.manager
await adminSeeder(dbConnection)
await manager.insert(Region, {
id: "test-region",
name: "Test Region",
currency_code: "usd",
tax_rate: 0,
})
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 () => {
@@ -164,5 +260,33 @@ describe("/admin/regions", () => {
})
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 () => {
const db = useDb()
db.teardown()
medusaProcess.kill()
await db.teardown({ forceDelete: ["store"] })
await medusaProcess.kill()
})
it("has created store with default currency", async () => {
@@ -35,6 +35,7 @@ exports[`Claims creates a replace claim 1`] = `
Object {
"additional_items": Array [
Object {
"adjustments": Array [],
"allow_discounts": true,
"cart_id": null,
"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",
quantity: 2,
unit_price: 1000,
adjustments: [
{
amount: 200,
discount_code: "TESTCODE",
description: "discount",
item_id: "test-item",
},
],
tax_lines: [
{
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 {
"address_1": null,
"address_2": null,
@@ -1,10 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/store/variants /test-variant 1`] = `
exports[`/store/variants GET /store/variants /test-variant 1`] = `
Object {
"variant": Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -30,6 +31,7 @@ Object {
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -37,8 +39,11 @@ Object {
"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,
"sale_amount": null,
"updated_at": Any<String>,
"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 {
"variants": Array [
Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -86,6 +326,7 @@ Object {
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -93,8 +334,11 @@ Object {
"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,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
@@ -102,7 +102,7 @@ Object {
],
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"status": "published",
"subtitle": null,
"tags": Array [
Object {
@@ -130,6 +130,7 @@ Object {
Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -155,6 +156,7 @@ Object {
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -162,8 +164,36 @@ Object {
"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,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": "test-variant",
},
@@ -179,6 +209,7 @@ Object {
Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean2",
@@ -204,6 +235,7 @@ Object {
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -211,8 +243,36 @@ Object {
"currency_code": "usd",
"deleted_at": null,
"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,
"sale_amount": null,
"updated_at": Any<String>,
"variant_id": "test-variant_2",
},
@@ -228,6 +288,7 @@ Object {
Object {
"allow_backorder": false,
"barcode": "test-barcode 1",
"calculated_price": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean1",
@@ -253,6 +314,7 @@ Object {
},
],
"origin_country": null,
"original_price": null,
"prices": Array [
Object {
"amount": 100,
@@ -260,8 +322,36 @@ Object {
"currency_code": "usd",
"deleted_at": null,
"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,
"sale_amount": null,
"updated_at": Any<String>,
"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")
})
it("responds 409 on duplicate", async () => {
it("responds 422 on duplicate", async () => {
const api = useApi()
const response = await api
@@ -70,7 +70,7 @@ describe("/store/customers", () => {
})
.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 setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const { simpleProductFactory } = require("../../factories")
const productSeeder = require("../../helpers/product-seeder")
const storeProductSeeder = require("../../helpers/store-product-seeder")
jest.setTimeout(30000)
describe("/store/variants", () => {
let medusaProcess
@@ -21,28 +24,100 @@ describe("/store/variants", () => {
medusaProcess.kill()
})
beforeEach(async () => {
try {
await productSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
describe("GET /store/variants", () => {
beforeEach(async () => {
try {
await productSeeder(dbConnection)
afterEach(async () => {
const db = useDb()
await db.teardown()
})
await simpleProductFactory(
dbConnection,
{
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 () => {
const api = useApi()
afterEach(async () => {
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({
variants: [
{
const response = await api.get("/store/variants?ids=test-variant")
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,
barcode: "test-barcode",
created_at: expect.any(String),
@@ -80,72 +155,237 @@ describe("/store/variants", () => {
deleted_at: null,
id: "test-price",
region_id: null,
sale_amount: null,
min_quantity: null,
max_quantity: null,
price_list_id: null,
variant_id: "test-variant",
},
],
product: expect.any(Object),
options: [
{ created_at: expect.any(String), updated_at: expect.any(String) },
],
},
],
})
})
})
it("/test-variant", async () => {
const api = useApi()
describe("GET /store/variants advanced pricing", () => {
beforeEach(async () => {
try {
await storeProductSeeder(dbConnection)
const response = await api.get("/store/variants/test-variant")
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: [
await simpleProductFactory(
dbConnection,
{
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),
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 setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const { simpleProductFactory } = require("../../factories")
const productSeeder = require("../../helpers/store-product-seeder")
const adminSeeder = require("../../helpers/admin-seeder")
jest.setTimeout(30000)
@@ -200,17 +200,26 @@ describe("/store/products", () => {
})
expect(response.status).toEqual(200)
expect(response.data.products.length).toEqual(5)
expect(response.data.products).toEqual([
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({
id: "test-product_filtering_1",
collection_id: "test-collection1",
}),
expect.objectContaining({
id: "test-product_filtering_2",
collection_id: "test-collection2",
id: "giftcard",
}),
])
@@ -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", () => {
beforeEach(async () => {
try {
@@ -263,6 +439,8 @@ describe("/store/products", () => {
height: null,
hs_code: null,
origin_country: null,
calculated_price: null,
original_price: null,
barcode: "test-barcode",
product_id: "test-product",
created_at: expect.any(String),
@@ -275,19 +453,35 @@ describe("/store/products", () => {
],
prices: [
{
id: "test-money-amount",
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
created_at: expect.any(String),
currency_code: "usd",
deleted_at: null,
min_quantity: null,
max_quantity: null,
price_list_id: null,
id: "test-price",
region_id: null,
sale_amount: null,
updated_at: expect.any(String),
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,
origin_country: null,
barcode: null,
calculated_price: null,
original_price: null,
product_id: "test-product",
created_at: expect.any(String),
updated_at: expect.any(String),
@@ -318,18 +514,33 @@ describe("/store/products", () => {
],
prices: [
{
id: "test-money-amount",
id: "test-price2",
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
created_at: expect.any(String),
currency_code: "usd",
price_list_id: null,
deleted_at: null,
id: "test-price2",
region_id: null,
sale_amount: null,
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,
hs_code: null,
origin_country: null,
calculated_price: null,
original_price: null,
barcode: "test-barcode 1",
product_id: "test-product",
created_at: expect.any(String),
@@ -360,19 +573,35 @@ describe("/store/products", () => {
],
prices: [
{
id: "test-money-amount",
id: "test-price1",
created_at: expect.any(String),
updated_at: expect.any(String),
amount: 100,
created_at: expect.any(String),
currency_code: "usd",
min_quantity: null,
max_quantity: null,
price_list_id: null,
deleted_at: null,
id: "test-price1",
region_id: null,
sale_amount: null,
updated_at: expect.any(String),
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 () => {
const api = useApi()
//update test-product status to published
// update test-product status to published
await api
.post(
"/admin/products/test-product",
@@ -12,6 +12,8 @@ const {
simpleProductFactory,
} = require("../../factories")
jest.setTimeout(30000)
describe("Swaps", () => {
let medusaProcess
let dbConnection
@@ -28,7 +28,7 @@ describe("/admin/tax-rates", () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
try {
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd, verbose: true })
medusaProcess = await setupServer({ cwd })
} catch (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)
const { tax_rates } = await createTaxRates(dbConnection, 1, 1, 200)
const [rate] = tax_rates
@@ -197,6 +197,111 @@ describe("Order Taxes", () => {
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 () => {
const product1 = await simpleProductFactory(
dbConnection,
+1
View File
@@ -11,3 +11,4 @@ export * from "./simple-tax-rate-factory"
export * from "./simple-shipping-option-factory"
export * from "./simple-shipping-method-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 { RegionFactoryData, simpleRegionFactory } from "./simple-region-factory"
import {
LineItemFactoryData,
simpleLineItemFactory,
} from "./simple-line-item-factory"
import faker from "faker"
import { Connection } from "typeorm"
import {
AddressFactoryData,
simpleAddressFactory,
} 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 {
ShippingMethodFactoryData,
simpleShippingMethodFactory,
@@ -18,6 +18,7 @@ import {
export type CartFactoryData = {
id?: string
customer?: string | { email: string }
region?: RegionFactoryData | string
email?: string | null
line_items?: LineItemFactoryData[]
@@ -43,6 +44,22 @@ export const simpleCartFactory = async (
const region = await simpleRegionFactory(connection, data.region)
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 id = data.id || `simple-cart-${Math.random() * 1000}`
@@ -51,6 +68,7 @@ export const simpleCartFactory = async (
email:
typeof data.email !== "undefined" ? data.email : faker.internet.email(),
region_id: regionId,
customer_id: customerId,
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 {
AllocationType,
Discount,
DiscountRule,
DiscountRuleType,
AllocationType,
} from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
DiscuntConditionFactoryData,
simpleDiscountConditionFactory,
} from "./simple-discount-condition-factory"
export type DiscountRuleFactoryData = {
type?: DiscountRuleType
value?: number
allocation?: AllocationType
conditions: DiscuntConditionFactoryData[]
}
export type DiscountFactoryData = {
@@ -41,6 +46,16 @@ export const simpleDiscountFactory = async (
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, {
id: data.id,
is_dynamic: data.is_dynamic ?? false,
@@ -1,6 +1,6 @@
import { Connection } from "typeorm"
import faker from "faker"
import { LineItem, LineItemTaxLine } from "@medusajs/medusa"
import { LineItem, LineItemAdjustment, LineItemTaxLine } from "@medusajs/medusa"
type TaxLineFactoryData = {
rate: number
@@ -8,6 +8,11 @@ type TaxLineFactoryData = {
name: string
}
type LineItemAdjustmentFactoryData = Omit<LineItemAdjustment, "discount_id"> & {
discount_id: string
discount_code: string
}
export type LineItemFactoryData = {
id?: string
cart_id?: string
@@ -24,6 +29,7 @@ export type LineItemFactoryData = {
shipped_quantity?: boolean
returned_quantity?: boolean
tax_lines?: TaxLineFactoryData[]
adjustments: LineItemAdjustmentFactoryData[]
}
export const simpleLineItemFactory = async (
@@ -63,6 +69,7 @@ export const simpleLineItemFactory = async (
fulfilled_quantity: data.fulfilled_quantity || null,
shipped_quantity: data.shipped_quantity || null,
returned_quantity: data.returned_quantity || null,
adjustments: data.adjustments,
})
const line = await manager.save(toSave)
@@ -101,7 +101,17 @@ export const simpleOrderFactory = async (
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) {
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 {
ShippingProfileType,
ShippingProfile,
Product,
ProductType,
ProductOption,
ProductTag,
ProductType,
ShippingProfile,
ShippingProfileType,
} from "@medusajs/medusa"
import faker from "faker"
import { Connection } from "typeorm"
import {
simpleProductVariantFactory,
ProductVariantFactoryData,
simpleProductVariantFactory,
} from "./simple-product-variant-factory"
export type ProductFactoryData = {
id?: string
is_giftcard?: boolean
status?: string
title?: string
type?: string
tags?: string[]
options?: { id: string; title: string }[]
variants?: ProductVariantFactoryData[]
}
@@ -26,7 +28,7 @@ export const simpleProductFactory = async (
connection: Connection,
data: ProductFactoryData = {},
seed?: number
): Promise<Product> => {
): Promise<Product | undefined> => {
if (typeof seed !== "undefined") {
faker.seed(seed)
}
@@ -41,26 +43,41 @@ export const simpleProductFactory = async (
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") {
const toSave = manager.create(ProductType, {
value: data.type,
})
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, {
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 toSave = manager.create(Product, productToCreate)
const product = await manager.save(toSave)
await manager.save(toSave)
const optionId = `${prodId}-option`
const options = data.options || [{ id: optionId, title: "Size" }]
@@ -95,5 +112,5 @@ export const simpleProductFactory = async (
await simpleProductVariantFactory(connection, factoryData)
}
return product
return manager.findOne(Product, { id: prodId }, { relations: ["tags"] })
}
+373 -3
View File
@@ -14,6 +14,8 @@ const {
LineItem,
Payment,
PaymentSession,
CustomerGroup,
PriceList,
} = require("@medusajs/medusa")
module.exports = async (connection, data = {}) => {
@@ -52,6 +54,8 @@ module.exports = async (connection, data = {}) => {
tax_rate: 0,
})
await manager.save(r)
// Region with multiple countries
const regionWithMultipleCoutries = manager.create(Region, {
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'`
)
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, {
id: "free-shipping-rule",
description: "Free shipping rule",
@@ -108,6 +144,69 @@ module.exports = async (connection, data = {}) => {
tenPercent.rule = tenPercentRule
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, {
id: "test-discount-usage-limit",
code: "SPENT",
@@ -240,7 +339,7 @@ module.exports = async (connection, data = {}) => {
is_disabled: false,
starts_at: tenDaysAgo,
ends_at: tenDaysFromToday,
valid_duration: "P1M", //one month
valid_duration: "P1M", // one month
})
DynamicDiscount.regions = [r]
@@ -256,11 +355,21 @@ module.exports = async (connection, data = {}) => {
email: "test@email.com",
})
await manager.insert(Customer, {
const c2 = await manager.create(Customer, {
id: "test-customer-2",
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, {
id: "some-customer",
email: "some-customer@email.com",
@@ -299,6 +408,28 @@ module.exports = async (connection, 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, {
id: "giftcard-product",
title: "Giftcard",
@@ -328,6 +459,45 @@ module.exports = async (connection, data = {}) => {
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, {
id: "test-variant",
title: "test variant",
@@ -357,14 +527,82 @@ module.exports = async (connection, data = {}) => {
const ma = manager.create(MoneyAmount, {
variant_id: "test-variant",
currency_code: "usd",
type: "default",
amount: 1000,
})
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, {
variant_id: "test-variant-2",
currency_code: "usd",
type: "default",
amount: 8000,
})
@@ -373,6 +611,7 @@ module.exports = async (connection, data = {}) => {
const ma3 = manager.create(MoneyAmount, {
variant_id: "giftcard-denom",
currency_code: "usd",
type: "default",
amount: 1000,
})
@@ -394,6 +633,74 @@ module.exports = async (connection, data = {}) => {
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, {
id: "test-cart-2",
customer_id: "some-customer",
@@ -533,4 +840,67 @@ module.exports = async (connection, data = {}) => {
cart_id: "test-cart-3",
})
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,
})
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, {
id: "test-customer-delete-cg",
email: "test-deletetion-cg@email.com",
})
await manager.save(deletionCustomer)
await manager.insert(CustomerGroup, {
id: "customer-group-1",
@@ -54,7 +73,7 @@ module.exports = async (connection, data = {}) => {
await manager.insert(CustomerGroup, {
id: "customer-group-3",
name: "vest-group-3",
name: "test-group-3",
})
await manager.insert(CustomerGroup, {
@@ -62,30 +81,17 @@ module.exports = async (connection, data = {}) => {
name: "test-group-4",
})
const customer5 = manager.create(Customer, {
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, {
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 = manager.create(CustomerGroup, {
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)
@@ -96,10 +102,11 @@ module.exports = async (connection, data = {}) => {
customer7.groups = [c_group_5, c_group_6]
await manager.save(customer7)
const c_group_delete = manager.create(CustomerGroup, {
const c_group_delete = await manager.create(CustomerGroup, {
id: "test-group-delete",
name: "test-group-delete",
})
await manager.save(c_group_delete)
deletionCustomer.groups = [c_group_delete]
await manager.save(deletionCustomer)
@@ -173,6 +173,14 @@ module.exports = async (connection, data = {}) => {
quantity: 1,
variant_id: "test-variant",
order_id: "test-order",
adjustments: [
{
amount: 800,
discount_id: "test-discount",
description: "discount",
item_id: "test-item",
},
],
})
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",
})
const coll = manager.create(ProductCollection, {
const coll = await manager.create(ProductCollection, {
id: "test-collection",
handle: "test-collection",
title: "Test collection",
@@ -25,7 +25,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(coll)
const coll1 = manager.create(ProductCollection, {
const coll1 = await manager.create(ProductCollection, {
id: "test-collection1",
handle: "test-collection1",
title: "Test collection 1",
@@ -33,7 +33,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(coll1)
const coll2 = manager.create(ProductCollection, {
const coll2 = await manager.create(ProductCollection, {
id: "test-collection2",
handle: "test-collection2",
title: "Test collection 2",
@@ -41,28 +41,28 @@ module.exports = async (connection, data = {}) => {
await manager.save(coll2)
const tag = manager.create(ProductTag, {
const tag = await manager.create(ProductTag, {
id: "tag1",
value: "123",
})
await manager.save(tag)
const tag3 = manager.create(ProductTag, {
const tag3 = await manager.create(ProductTag, {
id: "tag3",
value: "123",
})
await manager.save(tag3)
const tag4 = manager.create(ProductTag, {
const tag4 = await manager.create(ProductTag, {
id: "tag4",
value: "123",
})
await manager.save(tag4)
const type = manager.create(ProductType, {
const type = await manager.create(ProductType, {
id: "test-type",
value: "test-type",
})
@@ -83,7 +83,7 @@ module.exports = async (connection, data = {}) => {
tax_rate: 0,
})
const p = manager.create(Product, {
const p = await manager.create(Product, {
id: "test-product",
handle: "test-product",
title: "Test product",
@@ -129,6 +129,34 @@ module.exports = async (connection, data = {}) => {
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, {
id: "test-variant_1",
inventory_quantity: 10,
@@ -172,7 +200,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(variant3)
const p1 = manager.create(Product, {
const p1 = await manager.create(Product, {
id: "test-product1",
handle: "test-product1",
title: "Test product1",
@@ -203,6 +231,7 @@ module.exports = async (connection, data = {}) => {
region_id: "test-region",
currency_code: "usd",
amount: 100,
type: "default",
},
],
options: [
@@ -225,7 +254,9 @@ module.exports = async (connection, data = {}) => {
ean: "test-ean4",
upc: "test-upc4",
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: [
{
id: "test-variant-option-4",
@@ -237,7 +268,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(variant5)
const product1 = manager.create(Product, {
const product1 = await manager.create(Product, {
id: "test-product_filtering_1",
handle: "test-product_filtering_1",
title: "Test product filtering 1",
@@ -251,7 +282,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(product1)
const product2 = manager.create(Product, {
const product2 = await manager.create(Product, {
id: "test-product_filtering_2",
handle: "test-product_filtering_2",
title: "Test product filtering 2",
@@ -265,7 +296,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(product2)
const product3 = manager.create(Product, {
const product3 = await manager.create(Product, {
id: "test-product_filtering_3",
handle: "test-product_filtering_3",
title: "Test product filtering 3",
@@ -279,7 +310,7 @@ module.exports = async (connection, data = {}) => {
await manager.save(product3)
const product4 = manager.create(Product, {
const product4 = await manager.create(Product, {
id: "test-product_filtering_4",
handle: "test-product_filtering_4",
title: "Test product filtering 4",
@@ -8,11 +8,45 @@ const {
ShippingProfile,
ProductVariant,
Image,
Cart,
PriceList,
} = require("@medusajs/medusa")
module.exports = async (connection, data = {}) => {
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, {
type: "default",
})
@@ -83,12 +117,20 @@ module.exports = async (connection, data = {}) => {
tax_rate: 0,
})
await manager.insert(Cart, {
id: "test-cart",
region_id: "test-region",
currency_code: "usd",
items: [],
})
const p = manager.create(Product, {
id: "test-product",
handle: "test-product",
title: "Test product",
profile_id: defaultProfile.id,
description: "test-product-description",
status: "published",
collection_id: "test-collection",
type: { id: "test-type", value: "test-type" },
tags: [
@@ -117,7 +159,21 @@ module.exports = async (connection, data = {}) => {
upc: "test-upc",
barcode: "test-barcode",
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: [
{
id: "test-variant-option",
@@ -139,7 +195,21 @@ module.exports = async (connection, data = {}) => {
upc: "test-upc1",
barcode: "test-barcode 1",
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: [
{
id: "test-variant-option-1",
@@ -160,7 +230,21 @@ module.exports = async (connection, data = {}) => {
ean: "test-ean2",
upc: "test-upc2",
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: [
{
id: "test-variant-option-2",
@@ -176,6 +260,7 @@ module.exports = async (connection, data = {}) => {
id: "test-product1",
handle: "test-product1",
title: "Test product1",
status: "published",
profile_id: defaultProfile.id,
description: "test-product-description1",
collection_id: "test-collection",
+10 -1
View File
@@ -349,7 +349,7 @@ const createSwap = async (options, manager) => {
is_disabled: false,
rule: dRule,
})
await manager.save(discount)
let discountDb = await manager.save(discount)
const cart = manager.create(Cart, {
id: `${swapId}-cart`,
@@ -396,6 +396,14 @@ const createSwap = async (options, manager) => {
thumbnail: "https://test.js/1234",
unit_price: 8000,
quantity: 1,
adjustments: [
{
amount: -800,
description: "discount",
discount_id: discountDb.id,
item_id: `${swapId}-return-item-1`,
},
],
variant_id: "test-variant",
order_id: "order-with-swap",
cart_id: cart.id,
@@ -419,6 +427,7 @@ const createSwap = async (options, manager) => {
const return_item1 = manager.create(LineItem, {
...li,
is_return: true,
unit_price: -1 * li.unit_price,
})
+1
View File
@@ -1,4 +1,5 @@
// API
process.chdir(__dirname)
module.exports = {
name: "api",
+2
View File
@@ -8,5 +8,7 @@ module.exports = {
// redis_url: REDIS_URL,
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`,
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\""
},
"dependencies": {
"@medusajs/medusa": "1.1.64-dev-1645628651544",
"@medusajs/medusa": "1.2.1-dev-1649181615374",
"faker": "^5.5.3",
"medusa-interfaces": "1.1.34-dev-1645628651544",
"medusa-interfaces": "1.2.1-dev-1649181615374",
"typeorm": "^0.2.31"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^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"
}
}
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 path = require("path")
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"
}
async createTemplateDb_() {
async createTemplateDb_({ cwd }) {
try {
const cwd = path.resolve(path.join(__dirname, ".."))
// const cwd = path.resolve(path.join(__dirname, ".."))
const connection = await this.getMasterConnection()
const migrationDir = path.resolve(
path.join(
cwd,
"api",
`node_modules`,
`@medusajs`,
`medusa`,
+1
View File
@@ -6,6 +6,7 @@ const pkgs = glob
module.exports = {
testEnvironment: `node`,
testTimeout: 10000,
globalSetup: "<rootDir>/integration-tests/globalSetup.js",
globalTeardown: "<rootDir>/integration-tests/globalTeardown.js",
rootDir: `../`,
@@ -427,6 +427,7 @@ Object {
"idempotency_key": null,
"items": Array [
Object {
"adjustments": Array [],
"allow_discounts": true,
"cart_id": null,
"claim_order_id": null,
@@ -761,6 +762,7 @@ Object {
"idempotency_key": null,
"items": Array [
Object {
"adjustments": Array [],
"allow_discounts": true,
"cart_id": null,
"claim_order_id": null,
@@ -982,6 +984,7 @@ Object {
"idempotency_key": null,
"items": Array [
Object {
"adjustments": Array [],
"allow_discounts": true,
"cart_id": null,
"claim_order_id": null,
@@ -1248,6 +1251,7 @@ Object {
"idempotency_key": null,
"items": Array [
Object {
"adjustments": Array [],
"allow_discounts": true,
"cart_id": null,
"claim_order_id": null,
@@ -1569,6 +1573,7 @@ Object {
"idempotency_key": null,
"items": Array [
Object {
"adjustments": Array [],
"allow_discounts": true,
"cart_id": null,
"claim_order_id": null,
@@ -107,6 +107,7 @@ describe("medusa-plugin-sendgrid", () => {
},
items: [
{
adjustments: [],
created_at: expect.any(Date),
updated_at: expect.any(Date),
order_id: expect.any(String),
@@ -227,6 +228,7 @@ describe("medusa-plugin-sendgrid", () => {
},
items: [
{
adjustments: [],
created_at: expect.any(Date),
updated_at: expect.any(Date),
order_id: expect.any(String),
@@ -285,6 +287,7 @@ describe("medusa-plugin-sendgrid", () => {
},
items: [
{
adjustments: [],
created_at: expect.any(Date),
updated_at: expect.any(Date),
order_id: expect.any(String),
@@ -24,5 +24,7 @@ module.exports = {
// redis_url: REDIS_URL,
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`,
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\""
},
"dependencies": {
"@medusajs/medusa": "1.1.60-dev-1642337635041",
"@medusajs/medusa": "1.2.1-dev-1649009241281",
"faker": "^5.5.3",
"medusa-fulfillment-webshipper": "1.1.35-dev-1642337635041",
"medusa-interfaces": "1.1.32-dev-1642337635041",
"medusa-plugin-sendgrid": "1.1.36-dev-1642337635041",
"medusa-fulfillment-webshipper": "1.2.1-dev-1649009241281",
"medusa-interfaces": "1.2.1-dev-1649009241281",
"medusa-plugin-sendgrid": "1.2.1-dev-1649009241281",
"typeorm": "^0.2.31"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^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"
}
}
+46 -46
View File
@@ -1268,10 +1268,10 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
"@medusajs/medusa-cli@1.1.25-dev-1642337635041":
version "1.1.25-dev-1642337635041"
resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.1.25-dev-1642337635041.tgz#e3ca33f453446dddc4922f42b4254995de9874b5"
integrity sha512-8pA5iBMglgrdPRSEZLvjovJczib5zk9qlQJsewlSrw/aolwnusukEE7vBfjAiUXAZKngMx6BnYoORlWSV88wdA==
"@medusajs/medusa-cli@1.2.1-dev-1649009241281":
version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.2.1-dev-1649009241281.tgz#e0cc327224af6067013e0eba1b882b3f9f0fd0e3"
integrity sha512-3949nJfrBIguXrKDwiKjQ10XxZ06ccTShbcOUsXkIuGIMOHBZ+AuhjErCAnXS3H/JSIO93lnQKVJ8DWSkf9BGQ==
dependencies:
"@babel/polyfill" "^7.8.7"
"@babel/runtime" "^7.9.6"
@@ -1289,8 +1289,8 @@
is-valid-path "^0.1.1"
joi-objectid "^3.0.1"
meant "^1.0.1"
medusa-core-utils "1.1.31-dev-1642337635041"
medusa-telemetry "0.0.11-dev-1642337635041"
medusa-core-utils "1.1.31-dev-1649009241281"
medusa-telemetry "0.0.11-dev-1649009241281"
netrc-parser "^3.1.6"
open "^8.0.6"
ora "^5.4.1"
@@ -1304,13 +1304,13 @@
winston "^3.3.3"
yargs "^15.3.1"
"@medusajs/medusa@1.1.60-dev-1642337635041":
version "1.1.60-dev-1642337635041"
resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.60-dev-1642337635041.tgz#011d64b9b85a3f66e01f3755788370aa444a2e27"
integrity sha512-MBef46mJZMSyE3+dcijMqjFZbNo7AL60mP/PwkOy4W1d/t2MZ+GOBIufLAVxVw7SaiSjAGpQDmCrUOqQrIhIdg==
"@medusajs/medusa@1.2.1-dev-1649009241281":
version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.2.1-dev-1649009241281.tgz#ed063a9abee468cd71b5d0201ea4f32dd2a59775"
integrity sha512-uNVPZq4rVOLRRP9yTeYWPn9xsLfhh7mgRexMjRL/7QmKTQmspBL/pbjdRk0WCBSSf3o5SFiOV0/n69Oxg6Oaig==
dependencies:
"@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"
awilix "^4.2.3"
body-parser "^1.19.0"
@@ -1334,8 +1334,8 @@
joi "^17.3.0"
joi-objectid "^3.0.1"
jsonwebtoken "^8.5.1"
medusa-core-utils "1.1.31-dev-1642337635041"
medusa-test-utils "1.1.35-dev-1642337635041"
medusa-core-utils "1.1.31-dev-1649009241281"
medusa-test-utils "1.1.37-dev-1649009241281"
morgan "^1.9.1"
multer "^1.4.2"
passport "^0.4.0"
@@ -1995,10 +1995,10 @@ babel-preset-jest@^26.6.2:
babel-plugin-jest-hoist "^26.6.2"
babel-preset-current-node-syntax "^1.0.0"
babel-preset-medusa-package@1.1.19-dev-1642337635041:
version "1.1.19-dev-1642337635041"
resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1642337635041.tgz#24c6f2db887f35c358270d85cc0f6b6c19f459fe"
integrity sha512-HtrHI6oWUEMyTK9MHKzCg7hzZHgi8+uCIQyTQIY5SYFRA6kZiNl54Wzai/BY26F1j5g0sVNfqmFXdBd53GEz7g==
babel-preset-medusa-package@1.1.19-dev-1649009241281:
version "1.1.19-dev-1649009241281"
resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1649009241281.tgz#253cb95840d3e122a45e2f72e15f623afa57ae61"
integrity sha512-UFH2A+rYUc33d2f+xEVCQOOmcm0RSSPlnTeSVV5bM8SoS4MXgekPdBO+sMjKi/bLbHJncKaapCm/HoYZ19ZJHg==
dependencies:
"@babel/plugin-proposal-class-properties" "^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"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
medusa-core-utils@1.1.31-dev-1642337635041:
version "1.1.31-dev-1642337635041"
resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1642337635041.tgz#d6928f377320ab851eff1750e13e44e2e366c9cb"
integrity sha512-m0VXTM3QwKi0oRk2Q4mqn6sK07xJgiwyz3rs/0gHTjl88B8k75XugbriFceLPEYqHs6AjP6nSNiRDqdRJqUsMg==
medusa-core-utils@1.1.31-dev-1649009241281:
version "1.1.31-dev-1649009241281"
resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1649009241281.tgz#1ff37cdd9057c5716124f250c0b4b261ef8c7cde"
integrity sha512-nYcgpTtUSGAFG00m1rKcWUgnlmL2zu5aOYl5Fas/re+hhc1fhi+jwHUMigK0c28t71RwgtaeJFNKgQNgxli+2A==
dependencies:
joi "^17.3.0"
joi-objectid "^3.0.1"
medusa-fulfillment-webshipper@1.1.35-dev-1642337635041:
version "1.1.35-dev-1642337635041"
resolved "http://localhost:4873/medusa-fulfillment-webshipper/-/medusa-fulfillment-webshipper-1.1.35-dev-1642337635041.tgz#ab4b0a23488094b39582d1c1d1c69e29713b464d"
integrity sha512-P1gRvnJcs5Svgmvb44Qp1Oph9TqtUQsEeawhX7u+3dSz9qDnJ1lo1XB1tBttpCP9qepPe3nc0Sk7mEwlq8eM6Q==
medusa-fulfillment-webshipper@1.2.1-dev-1649009241281:
version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/medusa-fulfillment-webshipper/-/medusa-fulfillment-webshipper-1.2.1-dev-1649009241281.tgz#d199209df0d3683a53829565713b857bac284282"
integrity sha512-dtMBJ3mObQS2uGKClga66qDOGTKjOenzLOdjScurCwheQqi5a+EOyUrZSssE4oQEMMRsuY+L/MTkcLyKr4f+HA==
dependencies:
axios "^0.20.0"
body-parser "^1.19.0"
cors "^2.8.5"
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:
version "1.1.32-dev-1642337635041"
resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.1.32-dev-1642337635041.tgz#8179a344a41e1163502b4ee8af5b294443067f99"
integrity sha512-I7Wp+BEgzJ2wLkGBoDe6VhAlB8lJRMAHJ7wGzWfCMiG3Dj0PX2KE2HZDT+PA5S7IIXHWh9c0n37/cHZDcIF5Hw==
medusa-interfaces@1.2.1-dev-1649009241281:
version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.2.1-dev-1649009241281.tgz#77b6c906b805e6b1fb092fb7632a7ad8ecbafec5"
integrity sha512-tgDptu8k7aWCKBo1m8RxcxcffsVjy2e2M80rfvTd81gjC3EtGBGxDmiwNGJoaWkFq+Matq2wve2l92xVA2t8uQ==
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:
version "1.1.36-dev-1642337635041"
resolved "http://localhost:4873/medusa-plugin-sendgrid/-/medusa-plugin-sendgrid-1.1.36-dev-1642337635041.tgz#ae4d1d23167daf3c45ced085f3079ed21d5e876d"
integrity sha512-nJBq0x9h9fMsPfW4BnGzvk8yWpuk1v90zeXP85cDhFeAAWJ50o2zF7HsNcJawPfhKooAF+3kRDApR8706gUyqQ==
medusa-plugin-sendgrid@1.2.1-dev-1649009241281:
version "1.2.1-dev-1649009241281"
resolved "http://localhost:4873/medusa-plugin-sendgrid/-/medusa-plugin-sendgrid-1.2.1-dev-1649009241281.tgz#362fafa54658c641fb482346c0ba6f28007bbbf3"
integrity sha512-2C7mfwR+HZSjm/Y3TGHHr7dJAoNhwUvEVZs2dpJCEfmpbsR2f6TG3J5e+kidubQ4NeCmHdrXGPmjS4lbH1tnhQ==
dependencies:
"@babel/plugin-transform-classes" "^7.9.5"
"@sendgrid/mail" "^7.1.1"
body-parser "^1.19.0"
express "^4.17.1"
medusa-core-utils "1.1.31-dev-1642337635041"
medusa-test-utils "1.1.35-dev-1642337635041"
medusa-core-utils "1.1.31-dev-1649009241281"
medusa-test-utils "1.1.37-dev-1649009241281"
medusa-telemetry@0.0.11-dev-1642337635041:
version "0.0.11-dev-1642337635041"
resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1642337635041.tgz#290bb20c1712adf99ec5d2f6871b200106203bc0"
integrity sha512-LlJVgdKAQFrp6ULW8D+Qvb/VSEgbQbKjEeoMnUwDaMMswGehsFUXsqBzs1LRo5nKmpLaPvSorw4OMvktR+RxTQ==
medusa-telemetry@0.0.11-dev-1649009241281:
version "0.0.11-dev-1649009241281"
resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1649009241281.tgz#76077de2a5b53f754f1a55701d892716be67876d"
integrity sha512-ZEWmHZI9RByg1Tm8sOOxVCs+dkr2LCvElXqFsJfgIoTdjvGS96tfG1pbod2LHD5rcBY2aXDSXBRf0pA1xRtvsQ==
dependencies:
axios "^0.21.1"
axios-retry "^3.1.9"
@@ -5191,13 +5191,13 @@ medusa-telemetry@0.0.11-dev-1642337635041:
remove-trailing-slash "^0.1.1"
uuid "^8.3.2"
medusa-test-utils@1.1.35-dev-1642337635041:
version "1.1.35-dev-1642337635041"
resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.35-dev-1642337635041.tgz#1daf7f6a671e2fffe43ab349d795581870104a4a"
integrity sha512-pCpMjmo0VyYbSRu/Ga/GdirBscJtS4HK4HtfXepMrut7Yg/kV+GpW/fFtBFkiqGDBpCWYLopzZAdRw+qpdpusA==
medusa-test-utils@1.1.37-dev-1649009241281:
version "1.1.37-dev-1649009241281"
resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.37-dev-1649009241281.tgz#59c9ac8fddf31a410347dac5ef147758a61f13da"
integrity sha512-+3c+2DB3UpJxyIt6LvWCR9aBtXwrABXV4GhUYW6toGl19JE2lZ9PBL3GSVmGi7etZ7Xv7tR9bwZXZURvKMr8Hg==
dependencies:
"@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"
merge-descriptors@1.0.1:
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
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)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"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",
"scripts": {
"test": "jest --watchAll",
+33 -45
View File
@@ -1,5 +1,4 @@
import axios, { AxiosPromise, AxiosRequestConfig } from "axios"
import { Reporter } from "gatsby-cli/lib/reporter/reporter"
function medusaRequest(
storeURL: string,
@@ -18,34 +17,28 @@ function medusaRequest(
return client(options)
}
export const createClient = (
options: MedusaPluginOptions,
reporter: Reporter
): any => {
export const createClient = (options: MedusaPluginOptions): any => {
const { storeUrl, authToken } = options as any
/**
*
* @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[]> {
let products: any[] = []
let offset = 0
let count = 1
do {
await medusaRequest(storeUrl, `/store/products?offset=${offset}`)
.then(({ data }) => {
products = [...products, ...data.products]
count = data.count
offset = data.products.length
})
.catch((error) => {
reporter.error(
`"The following error status was produced while attempting to fetch products: ${error}`
)
return []
})
let path = `/store/products?offset=${offset}`
if (_date) {
path += `&updated_at[gt]=${_date}`
}
await medusaRequest(storeUrl, path).then(({ data }) => {
products = [...products, ...data.products]
count = data.count
offset = data.products.length
})
} while (products.length < count)
return products
@@ -53,27 +46,25 @@ export const createClient = (
/**
*
* @param {string} date used fetch regions updated since the specified date
* @return {Promise<any[]>}
* @param {string} _date used fetch regions updated since the specified date
* @return {Promise<any[]>} regions to create nodes from
*/
async function regions(_date?: string): Promise<any[]> {
const regions = await medusaRequest(storeUrl, `/store/regions`)
.then(({ data }) => {
return data.regions
})
.catch((error) => {
console.warn(`
"The following error status was produced while attempting to fetch regions: ${error}
`)
return []
})
let path = `/store/regions`
if (_date) {
path += `?updated_at[gt]=${_date}`
}
const regions = await medusaRequest(storeUrl, path).then(({ data }) => {
return data.regions
})
return regions
}
/**
*
* @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[]> {
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
* @return {Promise<any[]>}
* @return {Promise<any[]>} collections to create nodes from
*/
async function collections(_date?: string): Promise<any[]> {
let collections: any[] = []
let offset = 0
let count = 1
do {
await medusaRequest(storeUrl, `/store/collections?offset=${offset}`)
.then(({ data }) => {
collections = [...collections, ...data.collections]
count = data.count
offset = data.collections.length
})
.catch((error) => {
reporter.error(
`"The following error status was produced while attempting to fetch products: ${error}`
)
return []
})
let path = `/store/collections?offset=${offset}`
if (_date) {
path += `&updated_at[gt]=${_date}`
}
await medusaRequest(storeUrl, path).then(({ data }) => {
collections = [...collections, ...data.collections]
count = data.count
offset = data.collections.length
})
} while (collections.length < count)
return collections
@@ -27,7 +27,7 @@ async function sourceAllNodes(
createRegionsOperation,
createOrdersOperation,
createCollectionsOperation,
} = createOperations(pluginOptions, gatsbyApi)
} = createOperations(pluginOptions)
const operations = [
createProductsOperation,
@@ -54,22 +54,17 @@ const medusaNodeTypes = [
"MedusaCollections",
]
export async function sourceUpdatedNodes(
async function sourceUpdatedNodes(
gatsbyApi: SourceNodesArgs,
pluginOptions: MedusaPluginOptions
pluginOptions: MedusaPluginOptions,
lastBuildTime: string
): Promise<void> {
const {
incrementalProductsOperation,
incrementalRegionsOperation,
incrementalOrdersOperation,
incrementalCollectionsOperation,
} = createOperations(pluginOptions, gatsbyApi)
const lastBuildTime = new Date(
gatsbyApi.store.getState().status.plugins?.[`gatsby-source-medusa`]?.[
`lastBuildTime`
]
)
} = createOperations(pluginOptions)
for (const nodeType of medusaNodeTypes) {
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(
gatsbyApi: SourceNodesArgs,
pluginOptions: MedusaPluginOptions
): Promise<void> {
const pluginStatus =
gatsbyApi.store.getState().status.plugins?.[`gatsby-source-medusa`]
const lastBuildTime = pluginStatus?.[`lastBuildTime`]
const { cache } = gatsbyApi
const lastBuildTime = await cache.get("timestamp")
if (lastBuildTime !== undefined) {
gatsbyApi.reporter.info(
`Cache is warm, but incremental builds are currently not supported. Running a clean build.`
)
await sourceAllNodes(gatsbyApi, pluginOptions)
await sourceUpdatedNodes(gatsbyApi, pluginOptions, lastBuildTime)
} else {
gatsbyApi.reporter.info(`Cache is cold, running a clean build.`)
await sourceAllNodes(gatsbyApi, pluginOptions)
}
gatsbyApi.reporter.info(`Finished sourcing nodes, caching last build time`)
gatsbyApi.actions.setPluginStatus(
pluginStatus !== undefined
? {
...pluginStatus,
[`lastBuildTime`]: Date.now(),
}
: {
[`lastBuildTime`]: Date.now(),
}
)
gatsbyApi.reporter.info(`Finished sourcing nodes`)
}
export function createResolvers({ createResolvers }: CreateResolversArgs): any {
+10 -14
View File
@@ -1,11 +1,7 @@
import { SourceNodesArgs } from "gatsby"
import { createClient } from "./client"
export function createOperations(
options: MedusaPluginOptions,
{ reporter }: SourceNodesArgs
): IOperations {
const client = createClient(options, reporter)
export function createOperations(options: MedusaPluginOptions): IOperations {
const client = createClient(options)
function createOperation(
name: "products" | "collections" | "regions" | "orders",
@@ -22,13 +18,13 @@ export function createOperations(
createCollectionsOperation: createOperation("collections"),
createRegionsOperation: createOperation("regions"),
createOrdersOperation: createOperation("orders"),
incrementalProductsOperation: (date: Date): any =>
createOperation("products", date.toISOString()),
incrementalCollectionsOperation: (date: Date): any =>
createOperation("collections", date.toISOString()),
incrementalRegionsOperation: (date: Date): any =>
createOperation("regions", date.toISOString()),
incrementalOrdersOperation: (date: Date): any =>
createOperation("orders", date.toISOString()),
incrementalProductsOperation: (date: string): any =>
createOperation("products", date),
incrementalCollectionsOperation: (date: string): any =>
createOperation("collections", date),
incrementalRegionsOperation: (date: string): any =>
createOperation("regions", date),
incrementalOrdersOperation: (date: string): any =>
createOperation("orders", date),
}
}
+4 -4
View File
@@ -22,8 +22,8 @@ interface IOperations {
createCollectionsOperation: IMedusaOperation
createRegionsOperation: IMedusaOperation
createOrdersOperation: IMedusaOperation
incrementalProductsOperation: (date: Date) => IMedusaOperation
incrementalCollectionsOperation: (date: Date) => IMedusaOperation
incrementalRegionsOperation: (date: Date) => IMedusaOperation
incrementalOrdersOperation: (date: Date) => IMedusaOperation
incrementalProductsOperation: (date: string) => IMedusaOperation
incrementalCollectionsOperation: (date: string) => IMedusaOperation
incrementalRegionsOperation: (date: string) => IMedusaOperation
incrementalOrdersOperation: (date: string) => IMedusaOperation
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@medusajs/medusa-cli",
"version": "1.2.1",
"version": "1.3.0",
"description": "Command Line interface for Medusa Commerce",
"main": "dist/index.js",
"bin": {
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
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)
**Note:** Version bump only for package medusa-file-s3
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-file-s3",
"version": "1.0.12",
"version": "1.1.0",
"description": "AWS s3 file connector for Medusa",
"main": "index.js",
"repository": {
@@ -24,7 +24,7 @@
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"medusa-test-utils": "^0.3.0"
"medusa-test-utils": "^1.1.37"
},
"scripts": {
"build": "babel src -d .",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
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)
**Note:** Version bump only for package medusa-file-spaces
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-file-spaces",
"version": "1.1.37",
"version": "1.2.0",
"description": "Digital Ocean Spaces file connector for Medusa",
"main": "index.js",
"repository": {
@@ -24,7 +24,7 @@
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"medusa-test-utils": "^0.3.0"
"medusa-test-utils": "^1.1.37"
},
"scripts": {
"build": "babel src -d .",
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
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,6 +1,6 @@
{
"name": "medusa-fulfillment-webshipper",
"version": "1.2.1",
"version": "1.3.0",
"description": "Webshipper Fulfillment provider for Medusa",
"main": "index.js",
"repository": {
@@ -18,7 +18,7 @@
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"@medusajs/medusa": "^1.2.1",
"@medusajs/medusa": "^1.3.0",
"client-sessions": "^0.8.0",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
@@ -1,4 +1,3 @@
import { TotalsService } from "@medusajs/medusa"
import { humanizeAmount } from "medusa-core-utils"
import { FulfillmentService } from "medusa-interfaces"
import Webshipper from "../utils/webshipper"
@@ -114,13 +113,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
const fromOrder = await this.orderService_.retrieve(orderId, {
select: ["total"],
relations: [
"discounts",
"discounts.rule",
"discounts.rule.valid_for",
"shipping_address",
"returns",
],
relations: ["discounts", "discounts.rule", "shipping_address", "returns"],
})
const methodData = returnOrder.shipping_method.data
@@ -145,7 +138,7 @@ class WebshipperFulfillmentService extends FulfillmentService {
}
}
let docs = []
const docs = []
if (this.invoiceGenerator_) {
const base64Invoice = await this.invoiceGenerator_.createReturnInvoice(
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 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.
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)
+8 -6
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-interfaces",
"version": "1.2.1",
"version": "1.3.0",
"description": "Core interfaces for Medusa",
"main": "dist/index.js",
"repository": {
@@ -9,7 +9,7 @@
"directory": "packages/medusa-interfaces"
},
"scripts": {
"build": "babel src --out-dir dist/ --ignore **/__tests__",
"build": "tsc --build",
"prepare": "cross-env NODE_ENV=production npm run build",
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__",
"test": "jest"
@@ -24,18 +24,20 @@
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/preset-typescript": "^7.13.0",
"@babel/runtime": "^7.9.6",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"medusa-core-utils": "^1.1.31",
"medusa-test-utils": "^1.1.37",
"prettier": "^1.19.1",
"typeorm": "^0.2.29"
"typeorm": "^0.2.29",
"typescript": "^4.4.4"
},
"peerDependencies": {
"medusa-core-utils": "^1.1.31",
"typeorm": "0.x"
},
"dependencies": {
"medusa-core-utils": "^1.1.31"
},
"gitHead": "cd1f5afa5aa8c0b15ea957008ee19f1d695cbd2e"
}
+14 -5
View File
@@ -158,12 +158,18 @@ class BaseService {
* @param {string} isolation - the isolation level to be used for the work.
* @return {any} the result of the transactional work
*/
async atomicPhase_(work, isolationOrErrorHandler, maybeErrorHandler) {
let errorHandler = maybeErrorHandler
async atomicPhase_(
work,
isolationOrErrorHandler,
maybeErrorHandlerOrDontFail
) {
let errorHandler = maybeErrorHandlerOrDontFail
let isolation = isolationOrErrorHandler
let dontFail = false
if (typeof isolationOrErrorHandler === "function") {
isolation = null
errorHandler = isolationOrErrorHandler
dontFail = !!maybeErrorHandlerOrDontFail
}
if (this.transactionManager_) {
@@ -222,12 +228,15 @@ class BaseService {
}
try {
const result = await this.manager_.transaction((m) => doWork(m))
return result
return await this.manager_.transaction((m) => doWork(m))
} catch (error) {
if (errorHandler) {
await errorHandler(error)
const result = await errorHandler(error)
if (dontFail) {
return result
}
}
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.
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)
+5 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@medusajs/medusa-js",
"version": "1.1.1",
"version": "1.2.0",
"description": "Client for Medusa Commerce Rest API",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,7 +15,7 @@
"author": "Oliver Juhl",
"license": "MIT",
"dependencies": {
"@medusajs/medusa": "^1.2.1",
"@medusajs/medusa": "^1.3.0",
"axios": "^0.24.0",
"form-data": "^4.0.0",
"qs": "^6.10.3",
@@ -23,10 +23,11 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/medusajs/medusa-js.git"
"url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-js"
},
"bugs": {
"url": "https://github.com/medusajs/medusa-js/issues"
"url": "https://github.com/medusajs/medusa/issues"
},
"devDependencies": {
"@types/jest": "^26.0.19",
+4
View File
@@ -187,6 +187,10 @@ class Client {
options: RequestOptions = {},
customHeaders: Record<string, any> = {}
): Promise<any> {
if (method === "POST" && !payload) {
payload = {}
}
const reqOpts = {
method,
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 customHeaders
*/
create(payload: AdminPostCustomersReq, customHeaders: Record<string, any> = {}): ResponsePromise<AdminCustomersRes> {
create(
payload: AdminPostCustomersReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminCustomersRes> {
const path = `/admin/customers`
return this.client.request("POST", path, payload, {}, customHeaders)
}
@@ -40,7 +43,10 @@ class AdminCustomersResource extends BaseResource {
* @param id customer id
* @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}`
return this.client.request("GET", path, {}, {}, customHeaders)
}
@@ -2,6 +2,7 @@ import {
AdminDiscountsDeleteRes,
AdminDiscountsListRes,
AdminDiscountsRes,
AdminGetDiscountParams,
AdminGetDiscountsParams,
AdminPostDiscountsDiscountDynamicCodesReq,
AdminPostDiscountsDiscountReq,
@@ -15,26 +16,22 @@ class AdminDiscountsResource extends BaseResource {
/**
* @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}`
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
*/
create(payload: AdminPostDiscountsReq, customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
create(
payload: AdminPostDiscountsReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts`
return this.client.request("POST", path, payload, {}, customHeaders)
}
@@ -45,7 +42,8 @@ class AdminDiscountsResource extends BaseResource {
update(
id: string,
payload: AdminPostDiscountsDiscountReq,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}`
return this.client.request("POST", path, payload, {}, customHeaders)
}
@@ -56,7 +54,8 @@ class AdminDiscountsResource extends BaseResource {
createDynamicCode(
id: string,
payload: AdminPostDiscountsDiscountDynamicCodesReq,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/dynamic-codes`
return this.client.request("POST", path, payload, {}, customHeaders)
}
@@ -64,7 +63,10 @@ class AdminDiscountsResource extends BaseResource {
/**
* @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}`
return this.client.request("DELETE", path, {}, {}, customHeaders)
}
@@ -75,7 +77,8 @@ class AdminDiscountsResource extends BaseResource {
deleteDynamicCode(
id: string,
code: string,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/dynamic-codes/${code}`
return this.client.request("DELETE", path, {}, {}, customHeaders)
}
@@ -83,7 +86,10 @@ class AdminDiscountsResource extends BaseResource {
/**
* @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}`
return this.client.request("GET", path, {}, {}, customHeaders)
}
@@ -91,7 +97,10 @@ class AdminDiscountsResource extends BaseResource {
/**
* @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}`
return this.client.request("GET", path, {}, {}, customHeaders)
}
@@ -101,7 +110,8 @@ class AdminDiscountsResource extends BaseResource {
*/
list(
query?: AdminGetDiscountsParams,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsListRes> {
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsListRes> {
let path = `/admin/discounts`
if (query) {
@@ -118,21 +128,11 @@ class AdminDiscountsResource extends BaseResource {
removeRegion(
id: string,
regionId: string,
customHeaders: Record<string, any> = {}): ResponsePromise<AdminDiscountsRes> {
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDiscountsRes> {
const path = `/admin/discounts/${id}/regions/${regionId}`
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
Loaded 100 of 415 files, more files were not shown because too many files have changed in this diff. Show more